import { useEffect, useState } from "react"; import { Controller, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { faCheck, faCopy, faKey, faXmark } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { yupResolver } from "@hookform/resolvers/yup"; import { format } from "date-fns"; import * as yup from "yup"; import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; import { Button, DeleteActionModal, EmptyState, FormControl, IconButton, Input, Modal, ModalContent, Table, TableContainer, TableSkeleton, TBody, Td, Th, THead, Tr } from "@app/components/v2"; import { useOrganization } from "@app/context"; import { useToggle } from "@app/hooks"; import { useCreateScimToken, useDeleteScimToken, useGetScimTokens } from "@app/hooks/api"; import { UsePopUpState } from "@app/hooks/usePopUp"; const schema = yup.object({ description: yup.string(), ttlDays: yup.string().required("TTL is required") }); export type FormData = yup.InferType; type Props = { popUp: UsePopUpState<["scimToken", "deleteScimToken"]>; handlePopUpOpen: ( popUpName: keyof UsePopUpState<["deleteScimToken"]>, data?: { scimTokenId: string; } ) => void; handlePopUpToggle: ( popUpName: keyof UsePopUpState<["scimToken", "deleteScimToken"]>, state?: boolean ) => void; }; export const ScimTokenModal = ({ popUp, handlePopUpOpen, handlePopUpToggle }: Props) => { const { currentOrg } = useOrganization(); const { t } = useTranslation(); const { createNotification } = useNotificationContext(); const [token, setToken] = useState(""); const [isScimUrlCopied, setIsScimUrlCopied] = useToggle(false); const [isScimTokenCopied, setIsScimTokenCopied] = useToggle(false); const { data, isLoading } = useGetScimTokens(currentOrg?.id ?? ""); const { mutateAsync: createScimTokenMutateAsync } = useCreateScimToken(); const { mutateAsync: deleteScimTokenMutateAsync } = useDeleteScimToken(); const { control, handleSubmit, reset, formState: { isSubmitting } } = useForm({ resolver: yupResolver(schema), defaultValues: { description: "", ttlDays: "365" } }); useEffect(() => { let timer: NodeJS.Timeout; if (isScimUrlCopied) { timer = setTimeout(() => setIsScimUrlCopied.off(), 2000); } if (isScimTokenCopied) { timer = setTimeout(() => setIsScimTokenCopied.off(), 2000); } return () => clearTimeout(timer); }, [isScimTokenCopied, isScimUrlCopied]); const onFormSubmit = async ({ description, ttlDays }: FormData) => { try { if (!currentOrg?.id) return; const { scimToken } = await createScimTokenMutateAsync({ organizationId: currentOrg.id, description, ttlDays: Number(ttlDays) }); setToken(scimToken); createNotification({ text: "Successfully created SCIM token", type: "success" }); } catch (err) { console.error(err); createNotification({ text: "Failed to create SCIM token", type: "error" }); } }; const onDeleteScimTokenSubmit = async (scimTokenId: string) => { try { if (!currentOrg?.id) return; await deleteScimTokenMutateAsync({ organizationId: currentOrg.id, scimTokenId }); handlePopUpToggle("deleteScimToken", false); createNotification({ text: "Successfully deleted SCIM token", type: "success" }); } catch (err) { console.error(err); createNotification({ text: "Failed to delete SCIM token", type: "error" }); } }; const hasToken = Boolean(token); const scimUrl = `${window.origin}/api/v1/scim`; return ( { handlePopUpToggle("scimToken", isOpen); reset(); setToken(""); }} >

SCIM URL

{scimUrl}

{ navigator.clipboard.writeText(scimUrl); setIsScimUrlCopied.on(); }} > {t("common.click-to-copy")}

New SCIM Token

{hasToken ? (

We will only show this token once

{token}

{ navigator.clipboard.writeText(token); setIsScimTokenCopied.on(); }} > {t("common.click-to-copy")}
) : (
( )} /> (
)} /> )}

SCIM Tokens

{isLoading && } {!isLoading && data && data.length > 0 && data.map(({ id, description, ttlDays, createdAt }) => { let expiresAt; if (ttlDays > 0) { expiresAt = new Date(new Date(createdAt).getTime() + ttlDays * 86400 * 1000); } return ( ); })} {!isLoading && data && data?.length === 0 && ( )}
Description Expires At Created At
{description === "" ? "-" : description} {expiresAt ? format(expiresAt, "yyyy-MM-dd HH:mm:ss") : "-"} {format(new Date(createdAt), "yyyy-MM-dd HH:mm:ss")} { handlePopUpOpen("deleteScimToken", { scimTokenId: id }); }} size="lg" colorSchema="primary" variant="plain" ariaLabel="update" >
handlePopUpToggle("scimToken", isOpen)} deleteKey="confirm" onDeleteApproved={() => { const deleteScimTokenData = popUp?.deleteScimToken?.data as { scimTokenId: string; }; return onDeleteScimTokenSubmit(deleteScimTokenData.scimTokenId); }} />
); };