mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-03 20:13:15 +00:00
* feat(vendors): complete operations roadmap frontend * test(vendors): cover work order assignment flow * fix(vendors): address review feedback * fix(vendors): prevent stale preference saves * fix(vendors): require truthful appointment bounds
135 lines
3.9 KiB
TypeScript
135 lines
3.9 KiB
TypeScript
import { useState } from "react";
|
|
import { Text } from "@/components/ui/text";
|
|
import { SignaturePad } from "@/app/v/_components/signature-pad";
|
|
import { vendorPortalApi } from "@/domain/vendor-portal/api/vendor-portal-api";
|
|
import { formatVendorPortalDateTime } from "@/domain/vendor-portal/lib/status-helpers";
|
|
import type { VendorPortalSignoff } from "@/domain/vendor-portal/types/vendor-portal";
|
|
|
|
type SignoffSectionProps = {
|
|
token: string;
|
|
dispatchId: number;
|
|
existing: VendorPortalSignoff[];
|
|
signoffType: string;
|
|
nameLabel: string;
|
|
onAdded: () => void;
|
|
};
|
|
|
|
export function SignoffSection({
|
|
token,
|
|
dispatchId,
|
|
existing,
|
|
signoffType,
|
|
nameLabel,
|
|
onAdded,
|
|
}: SignoffSectionProps) {
|
|
const [name, setName] = useState("");
|
|
const [method, setMethod] = useState<"drawn" | "typed">("drawn");
|
|
const [typedSig, setTypedSig] = useState("");
|
|
const [drawnSig, setDrawnSig] = useState("");
|
|
const [submitting, setSubmitting] = useState(false);
|
|
const [errorMessage, setErrorMessage] = useState("");
|
|
|
|
const existingSignoff = existing.find((signoff) => signoff.signoffType === signoffType);
|
|
|
|
if (existingSignoff) {
|
|
return (
|
|
<div>
|
|
<div className="vp-row">
|
|
<strong>Signed by</strong>
|
|
<span>{existingSignoff.name}</span>
|
|
</div>
|
|
<div className="vp-row">
|
|
<strong>Method</strong>
|
|
<span>{existingSignoff.signatureMethod}</span>
|
|
</div>
|
|
<div className="vp-row">
|
|
<strong>Signed at</strong>
|
|
<span>{formatVendorPortalDateTime(existingSignoff.signedAt)}</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const submit = async (event: React.FormEvent) => {
|
|
event.preventDefault();
|
|
const signature = method === "drawn" ? drawnSig : typedSig;
|
|
if (!name.trim() || !signature) {
|
|
setErrorMessage("Name and signature are required.");
|
|
return;
|
|
}
|
|
setErrorMessage("");
|
|
setSubmitting(true);
|
|
try {
|
|
await vendorPortalApi.addSignoff(token, dispatchId, {
|
|
name: name.trim(),
|
|
signature,
|
|
signatureMethod: method,
|
|
signoffType,
|
|
});
|
|
onAdded();
|
|
} catch {
|
|
setErrorMessage("Unable to submit the signoff. Please try again.");
|
|
} finally {
|
|
setSubmitting(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<form onSubmit={(event) => void submit(event)}>
|
|
<div className="vp-row">
|
|
<strong>{nameLabel}</strong>
|
|
<input
|
|
className="vp-input"
|
|
value={name}
|
|
onChange={(event) => {
|
|
setName(event.target.value);
|
|
setErrorMessage("");
|
|
}}
|
|
/>
|
|
</div>
|
|
<div className="vp-row">
|
|
<strong>Method</strong>
|
|
<div>
|
|
<label style={{ marginRight: 12 }}>
|
|
<input type="radio" checked={method === "drawn"} onChange={() => setMethod("drawn")} />{" "}
|
|
Drawn
|
|
</label>
|
|
<label>
|
|
<input type="radio" checked={method === "typed"} onChange={() => setMethod("typed")} />{" "}
|
|
Typed
|
|
</label>
|
|
</div>
|
|
</div>
|
|
{method === "drawn" ? (
|
|
<div style={{ marginTop: 8 }}>
|
|
<SignaturePad
|
|
onChange={(signature) => {
|
|
setDrawnSig(signature);
|
|
setErrorMessage("");
|
|
}}
|
|
/>
|
|
</div>
|
|
) : (
|
|
<div className="vp-row">
|
|
<strong>Signature</strong>
|
|
<input
|
|
className="vp-input"
|
|
value={typedSig}
|
|
onChange={(event) => {
|
|
setTypedSig(event.target.value);
|
|
setErrorMessage("");
|
|
}}
|
|
/>
|
|
</div>
|
|
)}
|
|
<Text variant="error" when={Boolean(errorMessage)}>
|
|
{errorMessage}
|
|
</Text>
|
|
<div className="vp-actions">
|
|
<button type="submit" className="vp-btn vp-btn-success" disabled={submitting}>
|
|
{submitting ? "Signing…" : "Submit Signoff"}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
);
|
|
}
|