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")}
) : (
setConfirmed(event.currentTarget.checked)}
color="dark"
/>
)}
>
)}
{info.permissions?.canManage && (
)}
{status === "draft" && info.permissions?.canSubmitForApproval && (
)}
{status === "in_approval" &&
info.permissions?.canVerify &&
!showRejectForm && (
<>
>
)}
{status === "approved" && (
<>
{info.permissions?.canSubmitForApproval && (
)}
{info.permissions?.canMarkObsolete && (
)}
>
)}
);
}