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,8 +359,13 @@ 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") {
localErrors.effective_date = "Effective date is required"; 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()) { 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,46 +1819,57 @@ 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> type="date"
<input value={effectiveDate}
type="date" onChange={(event) => {
value={effectiveDate} setEffectiveDate(event.target.value);
onChange={(event) => { if (event.target.value)
setEffectiveDate(event.target.value); setActionErrors((prev) => ({
if (event.target.value) ...prev,
setActionErrors((prev) => ({ effective_date: "",
...prev, }));
effective_date: "", }}
})); error={actionErrors.effective_date}
}} />
className={`h-10 px-3 w-full border rounded-md text-sm transition-colors ${
actionErrors.effective_date <div className="border-t border-[rgba(0,0,0,0.08)] pt-3">
? "border-red-500 bg-red-50/30" <p className="text-xs font-semibold text-gray-500 mb-2 uppercase tracking-wider">
: "border-[rgba(0,0,0,0.08)]" Electronic Signature Verification
}`} </p>
/> <p className="text-[11px] text-gray-400 mb-4">
{actionErrors.effective_date && ( Enter your credentials below to electronically sign this document and authorize its release.
<p className="text-[11px] text-red-500 mt-1 font-medium italic"> </p>
{actionErrors.effective_date}
</p> <div className="grid grid-cols-1 md:grid-cols-2 gap-3">
)} <FormField
</div> label="Account Password"
<div> required
<label className="text-[13px] font-medium text-[#0e1b2a] mb-2 block"> type="password"
Signature ID{" "} value={actionPassword}
<span className="text-gray-400 font-normal">(Optional)</span> onChange={(event) => {
</label> setActionPassword(event.target.value);
<input if (event.target.value) {
type="text" setActionErrors((prev) => ({
value={signatureId} ...prev,
onChange={(event) => setSignatureId(event.target.value)} password: "",
placeholder="Enter signature ID" }));
className="h-10 px-3 w-full border border-[rgba(0,0,0,0.08)] rounded-md text-sm" }
/> }}
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>
)} )}