feat: integrate electronic signature verification for document effective action using password and optional PIN

This commit is contained in:
Yashwin 2026-06-24 14:44:16 +05:30
parent f76bbc6f65
commit 67baf47dbd

View File

@ -4,6 +4,7 @@ import { Layout } from "@/components/layout/Layout";
import { import {
DataTable, DataTable,
FormSelect, FormSelect,
FormField,
Modal, Modal,
PrimaryButton, PrimaryButton,
SecondaryButton, SecondaryButton,
@ -76,7 +77,8 @@ const ViewDocument = (): ReactElement => {
>([]); >([]);
const [actionComment, setActionComment] = useState(""); const [actionComment, setActionComment] = useState("");
const [effectiveDate, setEffectiveDate] = useState(""); const [effectiveDate, setEffectiveDate] = useState("");
const [signatureId, setSignatureId] = useState(""); const [actionPassword, setActionPassword] = useState("");
const [actionPin, setActionPin] = useState("");
const [showNewVersionForm, setShowNewVersionForm] = useState(false); const [showNewVersionForm, setShowNewVersionForm] = useState(false);
const [newVersionContent, setNewVersionContent] = useState(""); const [newVersionContent, setNewVersionContent] = useState("");
const [newVersionContentHtml, setNewVersionContentHtml] = useState(""); const [newVersionContentHtml, setNewVersionContentHtml] = useState("");
@ -310,7 +312,8 @@ const ViewDocument = (): ReactElement => {
setWorkflowDefinitionId(""); setWorkflowDefinitionId("");
setActionComment(""); setActionComment("");
setEffectiveDate(""); setEffectiveDate("");
setSignatureId(""); setActionPassword("");
setActionPin("");
setWorkflowOptions([]); setWorkflowOptions([]);
setActionErrors({}); setActionErrors({});
}; };
@ -356,9 +359,14 @@ const ViewDocument = (): ReactElement => {
if (action === "reject" && !actionComment.trim()) { if (action === "reject" && !actionComment.trim()) {
localErrors.comment = "Reason is required for reject"; localErrors.comment = "Reason is required for reject";
} }
if (action === "effective" && !effectiveDate) { if (action === "effective") {
if (!effectiveDate) {
localErrors.effective_date = "Effective date is required"; localErrors.effective_date = "Effective date is required";
} }
if (!actionPassword.trim()) {
localErrors.password = "Password is required for electronic signature";
}
}
if (action === "obsolete" && !actionComment.trim()) { if (action === "obsolete" && !actionComment.trim()) {
localErrors.comment = "Reason is required to obsolete"; localErrors.comment = "Reason is required to obsolete";
} }
@ -381,10 +389,39 @@ const ViewDocument = (): ReactElement => {
await documentService.reject(id, actionComment.trim()); await documentService.reject(id, actionComment.trim());
} }
if (action === "effective") { if (action === "effective") {
const currentVersionObj = versions.find(
(v) => v.version_number === document?.current_version
) || versions[0];
const currentVersionId = currentVersionObj?.id;
if (!currentVersionId) {
showToast.error("Current document version could not be resolved.");
return;
}
// Generate electronic signature
const signRes = await electronicSignatureService.sign({
resource_type: "document_version",
resource_id: currentVersionId,
resource_name: `${document?.document_number || "Document"} - Version ${document?.current_version || "1.0"} - Make Effective`,
action: "RELEASE",
meaning: "I authorize the release of this record",
password: actionPassword.trim(),
pin: actionPin.trim() || null,
auth_method: actionPin.trim() ? "password_pin" : "password",
});
const finalSignatureId = signRes.data?.id || signRes.data?.signature?.id;
if (!finalSignatureId) {
showToast.error("Failed to generate electronic signature.");
return;
}
await documentService.makeEffective( await documentService.makeEffective(
id, id,
effectiveDate, effectiveDate,
signatureId.trim(), finalSignatureId,
); );
} }
if (action === "obsolete") { if (action === "obsolete") {
@ -1782,12 +1819,10 @@ const ViewDocument = (): ReactElement => {
</div> </div>
)} )}
{activeAction === "effective" && ( {activeAction === "effective" && (
<div className="grid grid-cols-1 md:grid-cols-2 gap-3"> <div className="space-y-4">
<div> <FormField
<label className="text-[13px] font-medium text-[#0e1b2a] mb-2 block"> label="Effective Date"
Effective Date <span className="text-red-500">*</span> required
</label>
<input
type="date" type="date"
value={effectiveDate} value={effectiveDate}
onChange={(event) => { onChange={(event) => {
@ -1798,30 +1833,43 @@ const ViewDocument = (): ReactElement => {
effective_date: "", effective_date: "",
})); }));
}} }}
className={`h-10 px-3 w-full border rounded-md text-sm transition-colors ${ error={actionErrors.effective_date}
actionErrors.effective_date
? "border-red-500 bg-red-50/30"
: "border-[rgba(0,0,0,0.08)]"
}`}
/> />
{actionErrors.effective_date && (
<p className="text-[11px] text-red-500 mt-1 font-medium italic"> <div className="border-t border-[rgba(0,0,0,0.08)] pt-3">
{actionErrors.effective_date} <p className="text-xs font-semibold text-gray-500 mb-2 uppercase tracking-wider">
Electronic Signature Verification
</p> </p>
)} <p className="text-[11px] text-gray-400 mb-4">
</div> Enter your credentials below to electronically sign this document and authorize its release.
<div> </p>
<label className="text-[13px] font-medium text-[#0e1b2a] mb-2 block">
Signature ID{" "} <div className="grid grid-cols-1 md:grid-cols-2 gap-3">
<span className="text-gray-400 font-normal">(Optional)</span> <FormField
</label> label="Account Password"
<input required
type="text" type="password"
value={signatureId} value={actionPassword}
onChange={(event) => setSignatureId(event.target.value)} onChange={(event) => {
placeholder="Enter signature ID" setActionPassword(event.target.value);
className="h-10 px-3 w-full border border-[rgba(0,0,0,0.08)] rounded-md text-sm" if (event.target.value) {
setActionErrors((prev) => ({
...prev,
password: "",
}));
}
}}
placeholder="Enter password"
error={actionErrors.password}
/> />
<FormField
label="Signature PIN"
type="password"
value={actionPin}
onChange={(event) => setActionPin(event.target.value)}
placeholder="Enter PIN (Optional)"
/>
</div>
</div> </div>
</div> </div>
)} )}