mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-04 11:22:05 +00:00
148 lines
4.5 KiB
TypeScript
148 lines
4.5 KiB
TypeScript
import { useState } from "react";
|
|
import { Text } from "@/components/ui/text";
|
|
import { validateVendorDocument } from "@/domain/vendor-portal/lib/document-validation";
|
|
import {
|
|
isApprovalRequired,
|
|
validateUpliftAmount,
|
|
type UpliftFormValues,
|
|
} from "@/domain/vendor-portal/lib/uplift-validation";
|
|
|
|
type UpliftRequestFormProps = {
|
|
mode: "create" | "revise";
|
|
currentNte?: number;
|
|
initialRequested?: string;
|
|
initialReason?: string;
|
|
hasExistingEvidence?: boolean;
|
|
submitting: boolean;
|
|
uploadingPercent: number | undefined;
|
|
errorMessage: string;
|
|
onSubmit: (values: UpliftFormValues) => void;
|
|
onCancel?: () => void;
|
|
};
|
|
|
|
export function UpliftRequestForm({
|
|
mode,
|
|
currentNte,
|
|
initialRequested = "",
|
|
initialReason = "",
|
|
hasExistingEvidence = false,
|
|
submitting,
|
|
uploadingPercent,
|
|
errorMessage,
|
|
onSubmit,
|
|
onCancel,
|
|
}: UpliftRequestFormProps) {
|
|
const [requested, setRequested] = useState(initialRequested);
|
|
const [reason, setReason] = useState(initialReason);
|
|
const [evidenceFile, setEvidenceFile] = useState<File | undefined>();
|
|
const [formError, setFormError] = useState("");
|
|
|
|
const showApprovalFields = mode === "revise" || isApprovalRequired(requested, currentNte);
|
|
|
|
const submit = (event: React.FormEvent) => {
|
|
event.preventDefault();
|
|
const amount = Number.parseFloat(requested);
|
|
const validationError = validateUpliftAmount(requested, currentNte, reason, {
|
|
file: evidenceFile,
|
|
hasExisting: hasExistingEvidence,
|
|
mode,
|
|
});
|
|
setFormError(validationError);
|
|
if (validationError) {
|
|
return;
|
|
}
|
|
onSubmit({ amount, reason: reason.trim(), evidenceFile });
|
|
};
|
|
|
|
const chooseFile = (file?: File) => {
|
|
const validationMessage = file ? validateVendorDocument(file) : undefined;
|
|
setFormError(validationMessage ?? "");
|
|
setEvidenceFile(file);
|
|
};
|
|
|
|
const shownError = errorMessage || formError;
|
|
const busy = submitting || uploadingPercent !== undefined;
|
|
|
|
return (
|
|
<form
|
|
onSubmit={(event) => void submit(event)}
|
|
className="vp-uplift-form"
|
|
aria-label={mode === "revise" ? "Revise uplift request" : "Request NTE uplift"}
|
|
>
|
|
{mode === "create" && (
|
|
<div className="vp-row">
|
|
<strong>Current NTE</strong>
|
|
<span>${Number(currentNte ?? 0).toFixed(2)}</span>
|
|
</div>
|
|
)}
|
|
<div className="vp-row">
|
|
<strong>
|
|
<label htmlFor={`uplift-amount-${mode}`}>New total NTE</label>
|
|
</strong>
|
|
<input
|
|
id={`uplift-amount-${mode}`}
|
|
type="number"
|
|
step="0.01"
|
|
min="0"
|
|
className="vp-input"
|
|
value={requested}
|
|
onChange={(event) => {
|
|
setRequested(event.target.value);
|
|
setFormError("");
|
|
}}
|
|
placeholder="0.00"
|
|
/>
|
|
</div>
|
|
{showApprovalFields && (
|
|
<>
|
|
<div className="vp-row">
|
|
<strong>
|
|
<label htmlFor={`uplift-reason-${mode}`}>Reason</label>
|
|
</strong>
|
|
<textarea
|
|
id={`uplift-reason-${mode}`}
|
|
className="vp-textarea"
|
|
value={reason}
|
|
onChange={(event) => {
|
|
setReason(event.target.value);
|
|
setFormError("");
|
|
}}
|
|
placeholder="Why is the uplift needed?"
|
|
/>
|
|
</div>
|
|
<div className="vp-row">
|
|
<strong>
|
|
<label htmlFor={`uplift-evidence-${mode}`}>Evidence (PDF/JPG/PNG)</label>
|
|
</strong>
|
|
<input
|
|
id={`uplift-evidence-${mode}`}
|
|
type="file"
|
|
className="vp-input"
|
|
accept=".pdf,.jpg,.jpeg,.png,application/pdf,image/jpeg,image/png"
|
|
onChange={(event) => chooseFile(event.target.files?.[0])}
|
|
/>
|
|
</div>
|
|
</>
|
|
)}
|
|
{uploadingPercent !== undefined && (
|
|
<div className="vp-row" role="status" aria-live="polite">
|
|
<strong>Uploading</strong>
|
|
<span>{uploadingPercent}%</span>
|
|
</div>
|
|
)}
|
|
<Text variant="error" when={Boolean(shownError)}>
|
|
{shownError}
|
|
</Text>
|
|
<div className="vp-actions">
|
|
<button type="submit" className="vp-btn vp-btn-primary" disabled={busy}>
|
|
{busy ? "Submitting…" : mode === "revise" ? "Submit revision" : "Request uplift"}
|
|
</button>
|
|
{mode === "revise" && (
|
|
<button type="button" className="vp-btn vp-btn-ghost" onClick={onCancel} disabled={busy}>
|
|
Cancel
|
|
</button>
|
|
)}
|
|
</div>
|
|
</form>
|
|
);
|
|
}
|