import { useState } from "react"; import { Button, Center, Checkbox, Divider, Group, Loader, Stack, Text, Textarea, } from "@mantine/core"; import { modals } from "@mantine/modals"; import { useTranslation } from "react-i18next"; import { useMarkObsoleteMutation, usePageVerificationInfoQuery, useRejectApprovalMutation, useRemoveVerificationMutation, useSubmitForApprovalMutation, useUpdateVerificationMutation, useVerifyPageMutation, } from "@/ee/page-verification/queries/page-verification-query"; import { ExpirationMode, IPageVerificationInfo, PeriodUnit, } from "@/ee/page-verification/types/page-verification.types"; import { useTimeAgo } from "@/hooks/use-time-ago"; import { VerifierList } from "./verifier-list"; import { ExpirationFields, PERIOD_AMOUNT_MIN, PERIOD_UNIT_MAX_AMOUNT, toLocalDateString, } from "./expiration-fields"; import { VerifierPicker } from "./verifier-picker"; import { MAX_VERIFIERS } from "./user-option"; type ManageVerificationFormProps = { pageId: string; onClose: () => void; }; export function ManageVerificationForm({ pageId, onClose, }: ManageVerificationFormProps) { const { data: info, isLoading } = usePageVerificationInfoQuery(pageId); if (isLoading || !info) { return (
); } if (info.type === "qms") { return ; } return ( ); } type ManageContentProps = { pageId: string; info: IPageVerificationInfo; onClose: () => void; }; function ExpiringManageContent({ pageId, info, onClose }: ManageContentProps) { const { t } = useTranslation(); const verifyMutation = useVerifyPageMutation(); const removeMutation = useRemoveVerificationMutation(); const updateMutation = useUpdateVerificationMutation(); const [confirmed, setConfirmed] = useState(false); const initialMode: ExpirationMode = (info.mode as ExpirationMode) ?? "period"; const initialPeriodAmount = info.periodAmount ?? 1; const initialPeriodUnit: PeriodUnit = (info.periodUnit as PeriodUnit) ?? "month"; const initialFixedDate = initialMode === "fixed" && info.expiresAt ? toLocalDateString(info.expiresAt) : ""; const [mode, setMode] = useState(initialMode); const [periodAmount, setPeriodAmount] = useState(initialPeriodAmount); const [periodUnit, setPeriodUnit] = useState(initialPeriodUnit); const [fixedDate, setFixedDate] = useState(initialFixedDate); const verifiedAtAgo = useTimeAgo(info.verifiedAt ?? new Date().toISOString()); const hasExpirationChange = mode !== initialMode || (mode === "period" && (periodAmount !== initialPeriodAmount || periodUnit !== initialPeriodUnit)) || (mode === "fixed" && fixedDate !== initialFixedDate); const periodValid = mode !== "period" || (Number.isInteger(periodAmount) && periodAmount >= PERIOD_AMOUNT_MIN && periodAmount <= PERIOD_UNIT_MAX_AMOUNT[periodUnit]); const fixedDateValid = mode !== "fixed" || (!!fixedDate && new Date(fixedDate).getTime() > Date.now()); const canSaveExpiration = hasExpirationChange && periodValid && fixedDateValid; const storedFixedExpired = info.mode === "fixed" && !!info.expiresAt && new Date(info.expiresAt).getTime() <= Date.now(); const existingVerifierIds = info.verifiers?.map((v) => v.id) ?? []; const handleVerify = () => { verifyMutation.mutate(pageId, { onSuccess: () => { setConfirmed(false); onClose(); }, }); }; const handleRemove = () => { modals.openConfirmModal({ title: t("Remove verification"), children: ( {t("Are you sure you want to remove verification from this page?")} ), labels: { confirm: t("Remove"), cancel: t("Cancel") }, confirmProps: { color: "red" }, onConfirm: () => removeMutation.mutate(pageId, { onSuccess: onClose }), }); }; const handleSaveExpiration = () => { if (!canSaveExpiration) return; updateMutation.mutate({ pageId, mode, ...(mode === "period" && { periodAmount, periodUnit, }), ...(mode === "fixed" && fixedDate && { fixedExpiresAt: new Date(fixedDate).toISOString(), }), }); }; const handleRemoveVerifier = (userId: string) => { if (!info.verifiers) return; const remaining = info.verifiers .filter((v) => v.id !== userId) .map((v) => v.id); updateMutation.mutate({ pageId, verifierIds: remaining }); }; const handleAddVerifier = (userId: string) => { if (!info.verifiers) return; if (info.verifiers.some((v) => v.id === userId)) return; const verifierIds = [...info.verifiers.map((v) => v.id), userId]; updateMutation.mutate({ pageId, verifierIds }); }; const status = info.status; return ( {t("Assigned verifiers must periodically re-verify this page.")} {info.verifiedBy && (
{status === "expired" ? t("Last verified by {{name}} {{time}} (expired)", { name: info.verifiedBy.name, time: verifiedAtAgo, }) : t("Verified by {{name}} {{time}}", { name: info.verifiedBy.name, time: verifiedAtAgo, })} {info.expiresAt && ( {t(status === "expired" ? "Expired {{date}}" : "Expires {{date}}", { date: new Date(info.expiresAt).toLocaleDateString(undefined, { month: "long", day: "numeric", year: "numeric", }), })} )}
)} {info.verifiers && info.verifiers.length > 0 && ( <>
{t("Verifiers")} {info.permissions?.canManage && info.verifiers.length < MAX_VERIFIERS && (
handleAddVerifier(user.value)} />
)}
)} {info.permissions?.canManage && ( <>
{t("Expiration")} {hasExpirationChange && ( )}
)} {info.permissions?.canVerify && (
{t("Confirm")} setConfirmed(event.currentTarget.checked)} color="dark" /> {storedFixedExpired && ( {t("The fixed expiration date has passed.")} )}
)} {info.permissions?.canManage && ( )} {info.permissions?.canVerify && ( )}
); } function QmsManageContent({ pageId, info, onClose }: ManageContentProps) { const { t } = useTranslation(); const verifyMutation = useVerifyPageMutation(); const submitMutation = useSubmitForApprovalMutation(); const rejectMutation = useRejectApprovalMutation(); const obsoleteMutation = useMarkObsoleteMutation(); const removeMutation = useRemoveVerificationMutation(); const updateMutation = useUpdateVerificationMutation(); const [confirmed, setConfirmed] = useState(false); const [showRejectForm, setShowRejectForm] = useState(false); const [rejectComment, setRejectComment] = useState(""); const verifiedAtAgo = useTimeAgo(info.verifiedAt ?? new Date().toISOString()); const requestedAtAgo = useTimeAgo( info.requestedAt ?? new Date().toISOString(), ); const rejectedAtAgo = useTimeAgo(info.rejectedAt ?? new Date().toISOString()); const status = info.status; const existingVerifierIds = info.verifiers?.map((v) => v.id) ?? []; const handleSubmitForApproval = () => { submitMutation.mutate(pageId, { onSuccess: onClose }); }; const handleVerify = () => { verifyMutation.mutate(pageId, { onSuccess: () => { setConfirmed(false); onClose(); }, }); }; const handleReject = () => { rejectMutation.mutate( { pageId, comment: rejectComment || undefined }, { onSuccess: () => { setShowRejectForm(false); setRejectComment(""); onClose(); }, }, ); }; const handleMarkObsolete = () => { modals.openConfirmModal({ title: t("Mark as obsolete"), children: ( {t( "Are you sure you want to mark this page as obsolete? This action cannot be undone.", )} {t( "To restore this page, you will need to remove verification and set it up again.", )} ), labels: { confirm: t("Mark obsolete"), cancel: t("Cancel") }, confirmProps: { color: "red" }, onConfirm: () => obsoleteMutation.mutate(pageId, { onSuccess: onClose }), }); }; const handleRemove = () => { modals.openConfirmModal({ title: t("Remove verification"), children: ( {t("Are you sure you want to remove verification from this page?")} ), labels: { confirm: t("Remove"), cancel: t("Cancel") }, confirmProps: { color: "red" }, onConfirm: () => removeMutation.mutate(pageId, { onSuccess: onClose }), }); }; const handleRemoveVerifier = (userId: string) => { if (!info.verifiers) return; const remaining = info.verifiers .filter((v) => v.id !== userId) .map((v) => v.id); updateMutation.mutate({ pageId, verifierIds: remaining }); }; const handleAddVerifier = (userId: string) => { if (!info.verifiers) return; if (info.verifiers.some((v) => v.id === userId)) return; const verifierIds = [...info.verifiers.map((v) => v.id), userId]; updateMutation.mutate({ pageId, verifierIds }); }; const canManageVerifiers = info.permissions?.canManage && status !== "obsolete"; return ( {t("Pages move through draft, approval, and approved stages.")} {status === "draft" && ( <> {info.rejectedBy && info.rejectedAt && (
{t("Returned by {{name}} {{time}}", { name: info.rejectedBy.name, time: rejectedAtAgo, })} {info.rejectionComment && ( “{info.rejectionComment}” )}
)} {!info.rejectedBy && ( {t("No approval has been requested yet.")} )} )} {status === "in_approval" && (
{t("Submitted by {{name}} {{time}}", { name: info.requestedBy?.name ?? t("Someone"), time: requestedAtAgo, })}
)} {status === "approved" && info.verifiedBy && (
{t("Approved by {{name}} {{time}}", { name: info.verifiedBy.name, time: verifiedAtAgo, })}
)} {status === "obsolete" && ( {t("This document has been marked as obsolete.")} )} {info.verifiers && info.verifiers.length > 0 && ( <>
{t("Verifiers")} {canManageVerifiers && info.verifiers.length < MAX_VERIFIERS && (
handleAddVerifier(user.value)} />
)}
)} {status === "in_approval" && info.permissions?.canVerify && ( <> {showRejectForm ? (
{t("Rejection comment")}