fix: minor ui changes + delete expired secrets + address other feedback

This commit is contained in:
ShubhamPalriwala
2024-05-29 14:10:47 +05:30
parent f85a35fde8
commit b5b778e241
14 changed files with 174 additions and 203 deletions
@@ -19,11 +19,12 @@ import {
Modal,
ModalClose,
ModalContent,
SecretInput,
Select,
SelectItem
} from "@app/components/v2";
import { useOrganization } from "@app/context";
import { useToggle } from "@app/hooks";
import { useTimedReset } from "@app/hooks";
import { useCreateSharedSecret } from "@app/hooks/api/secretSharing";
import { UsePopUpState } from "@app/hooks/usePopUp";
@@ -89,22 +90,20 @@ export const AddShareSecretModal = ({ popUp, handlePopUpToggle }: Props) => {
const createSharedSecret = useCreateSharedSecret();
const { currentOrg } = useOrganization();
const [newSharedSecret, setnewSharedSecret] = useState("");
const [isUrlCopied, setIsUrlCopied] = useToggle(false);
const hasSharedSecret = Boolean(newSharedSecret);
useEffect(() => {
let timer: NodeJS.Timeout;
if (isUrlCopied) {
timer = setTimeout(() => setIsUrlCopied.off(), 2000);
}
return () => clearTimeout(timer);
}, [isUrlCopied]);
const [isUrlCopied,, setIsUrlCopied] = useTimedReset<boolean>({
initialState: false,
});
const copyUrlToClipboard = () => {
navigator.clipboard.writeText(newSharedSecret);
setIsUrlCopied.on();
setIsUrlCopied(true);
};
useEffect(() => {
if (isUrlCopied) {
setTimeout(() => setIsUrlCopied(false), 2000);
}
}, [isUrlCopied]);
const onFormSubmit = async ({ name, value, expiresInValue, expiresInUnit }: FormData) => {
try {
@@ -195,7 +194,11 @@ export const AddShareSecretModal = ({ popUp, handlePopUpToggle }: Props) => {
isError={Boolean(error)}
errorText={error?.message}
>
<Input {...field} placeholder="Type your secret value" />
<SecretInput
isVisible
{...field}
containerClassName="py-1.5 rounded-md transition-all group-hover:mr-2 text-bunker-300 hover:border-primary-400/50 border border-mineshaft-600 bg-mineshaft-900 px-2"
/>
</FormControl>
)}
/>
@@ -1,12 +1,8 @@
import { useState } from "react";
import { faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { createNotification } from "@app/components/notifications";
import { OrgPermissionCan } from "@app/components/permissions";
import { Button, Checkbox, DeleteActionModal } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
import { withPermission } from "@app/hoc";
import { Button, DeleteActionModal } from "@app/components/v2";
import { usePopUp } from "@app/hooks";
import { useDeleteSharedSecret } from "@app/hooks/api/secretSharing";
@@ -15,90 +11,67 @@ import { ShareSecretsTable } from "./ShareSecretsTable";
type DeleteModalData = { name: string; id: string };
export const ShareSecretSection = withPermission(
() => {
const deleteSharedSecret = useDeleteSharedSecret();
const [showExpiredSharedSecrets, setShowExpiredSharedSecrets] = useState(false);
export const ShareSecretSection = () => {
const deleteSharedSecret = useDeleteSharedSecret();
const { popUp, handlePopUpToggle, handlePopUpClose, handlePopUpOpen } = usePopUp([
"createSharedSecret",
"deleteSharedSecretConfirmation"
] as const);
const { popUp, handlePopUpToggle, handlePopUpClose, handlePopUpOpen } = usePopUp([
"createSharedSecret",
"deleteSharedSecretConfirmation"
] as const);
const onDeleteApproved = async () => {
try {
deleteSharedSecret.mutateAsync({
sharedSecretId: (popUp?.deleteSharedSecretConfirmation?.data as DeleteModalData)?.id,
});
createNotification({
text: "Successfully deleted shared secret",
type: "success"
});
const onDeleteApproved = async () => {
try {
deleteSharedSecret.mutateAsync({
sharedSecretId: (popUp?.deleteSharedSecretConfirmation?.data as DeleteModalData)?.id,
});
createNotification({
text: "Successfully deleted shared secret",
type: "success"
});
handlePopUpClose("deleteSharedSecretConfirmation");
} catch (err) {
console.error(err);
createNotification({
text: "Failed to delete shared secret",
type: "error"
});
}
};
handlePopUpClose("deleteSharedSecretConfirmation");
} catch (err) {
console.error(err);
createNotification({
text: "Failed to delete shared secret",
type: "error"
});
}
};
return (
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="mb-2 flex justify-between">
<p className="text-xl font-semibold text-mineshaft-100">Shared Secrets</p>
return (
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="mb-2 flex justify-between">
<p className="text-xl font-semibold text-mineshaft-100">Shared Secrets</p>
<OrgPermissionCan
I={OrgPermissionActions.Create}
a={OrgPermissionSubjects.SecretSharing}
>
{(isAllowed) => (
<Button
colorSchema="primary"
leftIcon={<FontAwesomeIcon icon={faPlus} />}
onClick={() => {
handlePopUpOpen("createSharedSecret");
}}
isDisabled={!isAllowed}
>
Share Secret
</Button>
)}
</OrgPermissionCan>
</div>
<div className="mb-8 flex items-center justify-between">
<p className="flex-grow text-gray-400">
Every secret shared can be accessed with the URL (shown during creation) before its
expiry.
</p>
<Checkbox
className="shrink-0 data-[state=checked]:bg-primary"
id="showInactive"
isChecked={showExpiredSharedSecrets}
onCheckedChange={(state) => {
setShowExpiredSharedSecrets(state as boolean);
}}
>
Show expired shared secrets too
</Checkbox>
</div>
<ShareSecretsTable
handlePopUpOpen={handlePopUpOpen}
showExpiredSharedSecrets={showExpiredSharedSecrets}
/>
<AddShareSecretModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
<DeleteActionModal
isOpen={popUp.deleteSharedSecretConfirmation.isOpen}
title={`Delete ${(popUp?.deleteSharedSecretConfirmation?.data as DeleteModalData)?.name || " "
} shared secret?`}
onChange={(isOpen) => handlePopUpToggle("deleteSharedSecretConfirmation", isOpen)}
deleteKey={(popUp?.deleteSharedSecretConfirmation?.data as DeleteModalData)?.name}
onClose={() => handlePopUpClose("deleteSharedSecretConfirmation")}
onDeleteApproved={onDeleteApproved}
/>
<Button
colorSchema="primary"
leftIcon={<FontAwesomeIcon icon={faPlus} />}
onClick={() => {
handlePopUpOpen("createSharedSecret");
}}
>
Share Secret
</Button>
</div>
);
},
{ action: OrgPermissionActions.Read, subject: OrgPermissionSubjects.SecretSharing }
);
<div className="mb-8 flex items-center justify-between">
<p className="flex-grow text-gray-400">
Every secret shared can be accessed with the URL (shown during creation) before its
expiry.
</p>
</div>
<ShareSecretsTable
handlePopUpOpen={handlePopUpOpen}
/>
<AddShareSecretModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
<DeleteActionModal
isOpen={popUp.deleteSharedSecretConfirmation.isOpen}
title={`Delete ${(popUp?.deleteSharedSecretConfirmation?.data as DeleteModalData)?.name || " "
} shared secret?`}
onChange={(isOpen) => handlePopUpToggle("deleteSharedSecretConfirmation", isOpen)}
deleteKey={(popUp?.deleteSharedSecretConfirmation?.data as DeleteModalData)?.name}
onClose={() => handlePopUpClose("deleteSharedSecretConfirmation")}
onDeleteApproved={onDeleteApproved}
/>
</div>
);
};
@@ -2,9 +2,7 @@ import { useEffect, useState } from "react";
import { faTrashCan } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { OrgPermissionCan } from "@app/components/permissions";
import { IconButton, Td, Tr } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context";
import { TSharedSecret } from "@app/hooks/api/secretSharing";
import { UsePopUpState } from "@app/hooks/usePopUp";
@@ -28,38 +26,31 @@ const timeAgo = (inputDate: Date, currentDate: Date): string => {
const elapsedYears = Math.abs(Math.floor(elapsedDays / 365));
if (elapsedYears > 0) {
return `${elapsedYears} year${elapsedYears === 1 ? "" : "s"} ${
elapsedMilliseconds >= 0 ? "ago" : "from now"
}`;
return `${elapsedYears} year${elapsedYears === 1 ? "" : "s"} ${elapsedMilliseconds >= 0 ? "ago" : "from now"
}`;
}
if (elapsedMonths > 0) {
return `${elapsedMonths} month${elapsedMonths === 1 ? "" : "s"} ${
elapsedMilliseconds >= 0 ? "ago" : "from now"
}`;
return `${elapsedMonths} month${elapsedMonths === 1 ? "" : "s"} ${elapsedMilliseconds >= 0 ? "ago" : "from now"
}`;
}
if (elapsedWeeks > 0) {
return `${elapsedWeeks} week${elapsedWeeks === 1 ? "" : "s"} ${
elapsedMilliseconds >= 0 ? "ago" : "from now"
}`;
return `${elapsedWeeks} week${elapsedWeeks === 1 ? "" : "s"} ${elapsedMilliseconds >= 0 ? "ago" : "from now"
}`;
}
if (elapsedDays > 0) {
return `${elapsedDays} day${elapsedDays === 1 ? "" : "s"} ${
elapsedMilliseconds >= 0 ? "ago" : "from now"
}`;
return `${elapsedDays} day${elapsedDays === 1 ? "" : "s"} ${elapsedMilliseconds >= 0 ? "ago" : "from now"
}`;
}
if (elapsedHours > 0) {
return `${elapsedHours} hour${elapsedHours === 1 ? "" : "s"} ${
elapsedMilliseconds >= 0 ? "ago" : "from now"
}`;
return `${elapsedHours} hour${elapsedHours === 1 ? "" : "s"} ${elapsedMilliseconds >= 0 ? "ago" : "from now"
}`;
}
if (elapsedMinutes > 0) {
return `${elapsedMinutes} minute${elapsedMinutes === 1 ? "" : "s"} ${
elapsedMilliseconds >= 0 ? "ago" : "from now"
}`;
return `${elapsedMinutes} minute${elapsedMinutes === 1 ? "" : "s"} ${elapsedMilliseconds >= 0 ? "ago" : "from now"
}`;
}
return `${elapsedSeconds} second${elapsedSeconds === 1 ? "" : "s"} ${
elapsedMilliseconds >= 0 ? "ago" : "from now"
}`;
return `${elapsedSeconds} second${elapsedSeconds === 1 ? "" : "s"} ${elapsedMilliseconds >= 0 ? "ago" : "from now"
}`;
};
export const ShareSecretsRow = ({
@@ -110,26 +101,20 @@ export const ShareSecretsRow = ({
<p className="text-xs text-gray-500">{formatDate(row.expiresAt)}</p>
</Td>
<Td>
<OrgPermissionCan
I={OrgPermissionActions.Delete}
a={OrgPermissionSubjects.SecretSharing}
<IconButton
onClick={() =>
handlePopUpOpen("deleteSharedSecretConfirmation", {
name: row.name,
id: row.id
})
}
colorSchema="danger"
ariaLabel="delete"
>
{(isAllowed) => (
<IconButton
onClick={() =>
handlePopUpOpen("deleteSharedSecretConfirmation", {
name: row.name,
id: row.id
})
}
colorSchema="danger"
ariaLabel="delete"
isDisabled={!isAllowed}
>
<FontAwesomeIcon icon={faTrashCan} />
</IconButton>
)}
</OrgPermissionCan>
<FontAwesomeIcon icon={faTrashCan} />
</IconButton>
</Td>
</Tr>
);
@@ -28,29 +28,22 @@ type Props = {
id: string;
}
) => void;
showExpiredSharedSecrets: boolean;
};
export const ShareSecretsTable = ({ handlePopUpOpen, showExpiredSharedSecrets }: Props) => {
export const ShareSecretsTable = ({ handlePopUpOpen }: Props) => {
const [tableData, setTableData] = useState<TSharedSecret[]>([]);
const { isLoading, data = [] } = useGetSharedSecrets();
const { isLoading, data = [] } = useGetSharedSecrets();
useEffect(() => {
if (!isLoading) {
if (!showExpiredSharedSecrets) {
setTableData(data.filter((secret) => new Date(secret.expiresAt) > new Date()));
} else {
setTableData(data);
}
setTableData(data);
}
}, [isLoading, data, showExpiredSharedSecrets]);
}, [isLoading, data]);
const handleSecretExpiration = () => {
if (!showExpiredSharedSecrets) {
setTableData(
data.filter((secret) => !secret.expiresAt || new Date(secret.expiresAt) > new Date())
);
}
setTableData(
data.filter((secret) => !secret.expiresAt || new Date(secret.expiresAt) > new Date())
);
};
return (
@@ -77,7 +70,7 @@ export const ShareSecretsTable = ({ handlePopUpOpen, showExpiredSharedSecrets }:
{!isLoading && tableData && tableData?.length === 0 && (
<Tr>
<Td colSpan={4} className="bg-mineshaft-800 text-center text-bunker-400">
<EmptyState title="No secrets shared yet!" icon={faKey} />
<EmptyState title="No secrets shared currently" icon={faKey} />
</Td>
</Tr>
)}