Finish preliminary making user settings, org settings styling similar to usage and billing page

This commit is contained in:
Tuan Dang
2023-06-29 17:47:23 +07:00
parent 81c69d92b3
commit 056f5a4555
73 changed files with 2098 additions and 1698 deletions
@@ -1,208 +0,0 @@
import { Fragment, useState } from "react";
import { useTranslation } from "react-i18next";
import { faCheck, faCopy } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Dialog, Transition } from "@headlessui/react";
import addAPIKey from "@app/pages/api/apiKey/addAPIKey";
import Button from "../buttons/Button";
import InputField from "../InputField";
import ListBox from "../Listbox";
const expiryMapping = {
"1 day": 86400,
"7 days": 604800,
"1 month": 2592000,
"6 months": 15552000,
"12 months": 31104000
};
type Props = {
isOpen: boolean;
closeModal: () => void;
// TODO: These never and any will be filled by single folder that contains types and hooks about the API
apiKeys: any[];
setApiKeys: (arg: any[]) => void;
};
// TODO: convert to TS
const AddApiKeyDialog = ({ isOpen, closeModal, apiKeys, setApiKeys }: Props) => {
const [apiKey, setApiKey] = useState("");
const [apiKeyName, setApiKeyName] = useState("");
const [apiKeyExpiresIn, setApiKeyExpiresIn] = useState("1 day");
const [apiKeyCopied, setApiKeyCopied] = useState(false);
const { t } = useTranslation();
const generateAPIKey = async () => {
const newApiKey = await addAPIKey({
name: apiKeyName,
expiresIn: expiryMapping[apiKeyExpiresIn as keyof typeof expiryMapping]
});
setApiKeys([...apiKeys, newApiKey.apiKeyData]);
setApiKey(newApiKey.apiKey);
};
function copyToClipboard() {
// Get the text field
const copyText = document.getElementById("apiKey") as HTMLInputElement;
// Select the text field
copyText.select();
copyText.setSelectionRange(0, 99999); // For mobile devices
// Copy the text inside the text field
navigator.clipboard.writeText(copyText.value);
setApiKeyCopied(true);
setTimeout(() => setApiKeyCopied(false), 2000);
// Alert the copied text
// alert("Copied the text: " + copyText.value);
}
const closeAddApiKeyModal = () => {
closeModal();
setApiKeyName("");
setApiKey("");
};
return (
<div className="z-50">
<Transition appear show={isOpen} as={Fragment}>
<Dialog as="div" className="relative" onClose={closeModal}>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-bunker-700 bg-opacity-80" />
</Transition.Child>
<div className="fixed inset-0 overflow-y-auto">
<div className="flex min-h-full items-center justify-center p-4 text-center">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"
>
{apiKey === "" ? (
<Dialog.Panel className="w-full max-w-md transform rounded-md border border-gray-700 bg-bunker-800 p-6 text-left align-middle shadow-xl transition-all">
<Dialog.Title
as="h3"
className="z-50 text-lg font-medium leading-6 text-gray-400"
>
{t("section.api-key.add-dialog.title")}
</Dialog.Title>
<div className="mt-2 mb-4">
<div className="flex flex-col">
<p className="text-sm text-gray-500">
{t("section.api-key.add-dialog.description")}
</p>
</div>
</div>
<div className="mb-2 max-h-28">
<InputField
label={t("section.api-key.add-dialog.name")}
onChangeHandler={setApiKeyName}
type="varName"
value={apiKeyName}
placeholder=""
isRequired
/>
</div>
<div className="max-h-28">
<ListBox
isSelected={apiKeyExpiresIn}
onChange={setApiKeyExpiresIn}
data={["1 day", "7 days", "1 month", "6 months", "12 months"]}
isFull
text={`${t("common.expired-in")}: `}
/>
</div>
<div className="max-w-max">
<div className="mt-6 flex w-max flex-col justify-start">
<Button
onButtonPressed={() => generateAPIKey()}
color="mineshaft"
text={t("section.api-key.add-dialog.add") as string}
textDisabled={t("section.api-key.add-dialog.add") as string}
size="md"
active={apiKeyName !== ""}
/>
</div>
</div>
</Dialog.Panel>
) : (
<Dialog.Panel className="w-full max-w-md transform rounded-md border border-gray-700 bg-bunker-800 p-6 text-left align-middle shadow-xl transition-all">
<Dialog.Title
as="h3"
className="z-50 text-lg font-medium leading-6 text-gray-400"
>
{t("section.api-key.add-dialog.copy-service-token")}
</Dialog.Title>
<div className="mt-2 mb-4">
<div className="flex flex-col">
<p className="text-sm text-gray-500">
{t("section.api-key.add-dialog.copy-service-token-description")}
</p>
</div>
</div>
<div className="w-full">
<div className="mt-2 mr-2 flex h-20 w-full items-center justify-end rounded-md bg-white/[0.07] text-base text-gray-400">
<input
type="text"
value={apiKey}
disabled
id="apiKey"
className="invisible w-full min-w-full bg-white/0 py-2 px-2 text-gray-400 outline-none"
/>
<div className="w-full max-w-md break-words bg-white/0 py-2 pl-14 pr-2 text-sm text-gray-400 outline-none">
{apiKey}
</div>
<div className="group relative inline-block h-full font-normal text-gray-400 underline duration-200 hover:text-primary">
<button
type="button"
onClick={copyToClipboard}
className="h-full border-l border-white/20 py-2 pl-3.5 pr-4 duration-200 hover:bg-white/[0.12]"
>
{apiKeyCopied ? (
<FontAwesomeIcon icon={faCheck} className="pr-0.5" />
) : (
<FontAwesomeIcon icon={faCopy} />
)}
</button>
<span className="absolute -left-8 -top-20 hidden w-28 translate-y-full rounded-md bg-chicago-900 px-3 py-2 text-center text-sm text-gray-400 duration-300 group-hover:flex group-hover:animate-popup">
{t("common.click-to-copy")}
</span>
</div>
</div>
</div>
<div className="mt-6 flex w-max flex-col justify-start">
<Button
onButtonPressed={() => closeAddApiKeyModal()}
color="mineshaft"
text="Close"
size="md"
/>
</div>
</Dialog.Panel>
)}
</Transition.Child>
</div>
</div>
</Dialog>
</Transition>
</div>
);
};
export default AddApiKeyDialog;
@@ -1,85 +0,0 @@
import { faX } from "@fortawesome/free-solid-svg-icons";
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
import deleteAPIKey from "../../../pages/api/apiKey/deleteAPIKey";
import guidGenerator from "../../utilities/randomId";
import Button from "../buttons/Button";
interface TokenProps {
_id: string;
name: string;
expiresAt: string;
}
interface ServiceTokensProps {
data: TokenProps[];
setApiKeys: (value: TokenProps[]) => void;
}
/**
* This is the component that we utilize for the api key table
* @param {object} obj
* @param {any[]} obj.data - current state of the api key table
* @param {function} obj.setApiKeys - updating the state of the api key table
* @returns
*/
const ApiKeyTable = ({ data, setApiKeys }: ServiceTokensProps) => {
const { createNotification } = useNotificationContext();
return (
<div className="table-container w-full bg-bunker rounded-md mb-6 border border-mineshaft-700 relative mt-1">
<div className="absolute rounded-t-md w-full h-12 bg-white/5" />
<table className="w-full my-1">
<thead className="text-bunker-300 text-sm font-light">
<tr>
<th className="text-left pl-6 pt-2.5 pb-2">API KEY NAME</th>
<th className="text-left pl-6 pt-2.5 pb-2">VALID UNTIL</th>
<th aria-label="button" />
</tr>
</thead>
<tbody>
{data?.length > 0 ? (
data?.map((row) => (
<tr
key={guidGenerator()}
className="bg-bunker-800 hover:bg-bunker-800/5 duration-100"
>
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">
{row.name}
</td>
<td className="pl-6 py-2 border-mineshaft-700 border-t text-gray-300">
{new Date(row.expiresAt).toUTCString()}
</td>
<td className="py-2 border-mineshaft-700 border-t">
<div className="opacity-50 hover:opacity-100 duration-200 flex items-center">
<Button
onButtonPressed={() => {
deleteAPIKey({ apiKeyId: row._id });
setApiKeys(data.filter((token) => token._id !== row._id));
createNotification({
text: `'${row.name}' API key has been revoked.`,
type: "error"
});
}}
color="red"
size="icon-sm"
icon={faX}
/>
</div>
</td>
</tr>
))
) : (
<tr>
<td colSpan={4} className="text-center pt-7 pb-5 text-bunker-300 text-sm">
No API keys yet
</td>
</tr>
)}
</tbody>
</table>
</div>
);
};
export default ApiKeyTable;