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 {
DataTable,
FormSelect,
FormField,
Modal,
PrimaryButton,
SecondaryButton,
@ -76,7 +77,8 @@ const ViewDocument = (): ReactElement => {
>([]);
const [actionComment, setActionComment] = useState("");
const [effectiveDate, setEffectiveDate] = useState("");
const [signatureId, setSignatureId] = useState("");
const [actionPassword, setActionPassword] = useState("");
const [actionPin, setActionPin] = useState("");
const [showNewVersionForm, setShowNewVersionForm] = useState(false);
const [newVersionContent, setNewVersionContent] = useState("");
const [newVersionContentHtml, setNewVersionContentHtml] = useState("");
@ -310,7 +312,8 @@ const ViewDocument = (): ReactElement => {
setWorkflowDefinitionId("");
setActionComment("");
setEffectiveDate("");
setSignatureId("");
setActionPassword("");
setActionPin("");
setWorkflowOptions([]);
setActionErrors({});
};
@ -356,9 +359,14 @@ const ViewDocument = (): ReactElement => {
if (action === "reject" && !actionComment.trim()) {
localErrors.comment = "Reason is required for reject";
}
if (action === "effective" && !effectiveDate) {
if (action === "effective") {
if (!effectiveDate) {
localErrors.effective_date = "Effective date is required";
}
if (!actionPassword.trim()) {
localErrors.password = "Password is required for electronic signature";
}
}
if (action === "obsolete" && !actionComment.trim()) {
localErrors.comment = "Reason is required to obsolete";
}
@ -381,10 +389,39 @@ const ViewDocument = (): ReactElement => {
await documentService.reject(id, actionComment.trim());
}
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(
id,
effectiveDate,
signatureId.trim(),
finalSignatureId,
);
}
if (action === "obsolete") {
@ -1782,12 +1819,10 @@ const ViewDocument = (): ReactElement => {
</div>
)}
{activeAction === "effective" && (
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
<div>
<label className="text-[13px] font-medium text-[#0e1b2a] mb-2 block">
Effective Date <span className="text-red-500">*</span>
</label>
<input
<div className="space-y-4">
<FormField
label="Effective Date"
required
type="date"
value={effectiveDate}
onChange={(event) => {
@ -1798,30 +1833,43 @@ const ViewDocument = (): ReactElement => {
effective_date: "",
}));
}}
className={`h-10 px-3 w-full border rounded-md text-sm transition-colors ${
actionErrors.effective_date
? "border-red-500 bg-red-50/30"
: "border-[rgba(0,0,0,0.08)]"
}`}
error={actionErrors.effective_date}
/>
{actionErrors.effective_date && (
<p className="text-[11px] text-red-500 mt-1 font-medium italic">
{actionErrors.effective_date}
<div className="border-t border-[rgba(0,0,0,0.08)] pt-3">
<p className="text-xs font-semibold text-gray-500 mb-2 uppercase tracking-wider">
Electronic Signature Verification
</p>
)}
</div>
<div>
<label className="text-[13px] font-medium text-[#0e1b2a] mb-2 block">
Signature ID{" "}
<span className="text-gray-400 font-normal">(Optional)</span>
</label>
<input
type="text"
value={signatureId}
onChange={(event) => setSignatureId(event.target.value)}
placeholder="Enter signature ID"
className="h-10 px-3 w-full border border-[rgba(0,0,0,0.08)] rounded-md text-sm"
<p className="text-[11px] text-gray-400 mb-4">
Enter your credentials below to electronically sign this document and authorize its release.
</p>
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
<FormField
label="Account Password"
required
type="password"
value={actionPassword}
onChange={(event) => {
setActionPassword(event.target.value);
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>
)}