mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-04 19:32:06 +00:00
The vendor completion upload and uplift evidence checked type, size and duration but not the 10-photo / 3-video work-order limit. Use the counts the dispatch detail now reports (shoc-backend#173), on the same basis as the server: a new completion version does not count the document it replaces. When the backend does not report counts yet, the server check still applies.
140 lines
4.4 KiB
TypeScript
140 lines
4.4 KiB
TypeScript
import { useState } from "react";
|
|
import { Text } from "@/components/ui/text";
|
|
import { UpliftRequestForm } from "@/app/v/[token]/dispatch/_components/uplift-request-form";
|
|
import type { VendorMediaUsage } from "@/domain/vendor-portal/lib/document-validation";
|
|
import type { UpliftFormValues } from "@/domain/vendor-portal/lib/uplift-validation";
|
|
import type { VendorPortalUpliftRequest } from "@/domain/vendor-portal/types/vendor-portal";
|
|
import { formatDateTime } from "@/lib/time-utils";
|
|
|
|
type UpliftRequestCardProps = {
|
|
request: VendorPortalUpliftRequest;
|
|
locked: boolean;
|
|
canAct: boolean;
|
|
uploadingPercent: number | undefined;
|
|
submitting: boolean;
|
|
errorMessage: string;
|
|
onWithdraw: (request: VendorPortalUpliftRequest) => void;
|
|
onRevise: (request: VendorPortalUpliftRequest, values: UpliftFormValues) => void;
|
|
onCancelRevise: () => void;
|
|
mediaUsage?: VendorMediaUsage;
|
|
};
|
|
|
|
const WITHDRAWABLE_STATUSES = new Set(["Pending", "ChangesRequested"]);
|
|
|
|
function statusClassName(status: string): string {
|
|
return `vp-uplift-status-${String(status).toLowerCase()}`;
|
|
}
|
|
|
|
export function UpliftRequestCard({
|
|
request,
|
|
locked,
|
|
canAct,
|
|
uploadingPercent,
|
|
submitting,
|
|
errorMessage,
|
|
onWithdraw,
|
|
onRevise,
|
|
onCancelRevise,
|
|
mediaUsage,
|
|
}: UpliftRequestCardProps) {
|
|
const [revising, setRevising] = useState(false);
|
|
const canWithdraw = !locked && canAct && WITHDRAWABLE_STATUSES.has(request.status);
|
|
const canRevise = !locked && canAct && request.status === "ChangesRequested";
|
|
|
|
return (
|
|
<div className="vp-uplift-row">
|
|
<div className="vp-row">
|
|
<strong>Requested</strong>
|
|
<span>
|
|
${Number(request.requestedNTE).toFixed(2)} (was $
|
|
{Number(request.currentNTE ?? 0).toFixed(2)})
|
|
</span>
|
|
</div>
|
|
<div className="vp-row">
|
|
<strong>Status</strong>
|
|
<span>
|
|
<span className={statusClassName(request.status)}>{request.status}</span>
|
|
</span>
|
|
</div>
|
|
{Boolean(request.expiresAt) && (
|
|
<div className="vp-row">
|
|
<strong>Expires</strong>
|
|
<span>{formatDateTime(request.expiresAt as string)}</span>
|
|
</div>
|
|
)}
|
|
{Boolean(request.vendorReason) && (
|
|
<div className="vp-row">
|
|
<strong>Reason</strong>
|
|
<span>{request.vendorReason}</span>
|
|
</div>
|
|
)}
|
|
{Boolean(request.evidenceFileName) && (
|
|
<div className="vp-row">
|
|
<strong>Evidence</strong>
|
|
<span>
|
|
{request.evidenceFileName}
|
|
{request.evidenceContentType ? ` · ${request.evidenceContentType}` : ""}
|
|
</span>
|
|
</div>
|
|
)}
|
|
{Boolean(request.decisionNote) && (
|
|
<div className="vp-row">
|
|
<strong>Decision</strong>
|
|
<span>
|
|
{request.decisionNote}
|
|
{request.decidedByName ? ` — ${request.decidedByName}` : ""}
|
|
</span>
|
|
</div>
|
|
)}
|
|
{request.notificationStatus === "Error" && Boolean(request.notificationError) && (
|
|
<div className="vp-row">
|
|
<strong>Notification</strong>
|
|
<span className="vp-error-inline">{request.notificationError}</span>
|
|
</div>
|
|
)}
|
|
<Text variant="error" when={Boolean(errorMessage)}>
|
|
{errorMessage}
|
|
</Text>
|
|
|
|
{revising && canRevise ? (
|
|
<UpliftRequestForm
|
|
mode="revise"
|
|
currentNte={request.currentNTE}
|
|
initialRequested={String(request.requestedNTE)}
|
|
initialReason={request.vendorReason ?? ""}
|
|
hasExistingEvidence={request.evidenceDocumentId != null}
|
|
mediaUsage={mediaUsage}
|
|
submitting={submitting}
|
|
uploadingPercent={uploadingPercent}
|
|
errorMessage=""
|
|
onSubmit={(values) => onRevise(request, values)}
|
|
onCancel={() => {
|
|
setRevising(false);
|
|
onCancelRevise();
|
|
}}
|
|
/>
|
|
) : (
|
|
<div className="vp-actions">
|
|
{canWithdraw && (
|
|
<button
|
|
type="button"
|
|
className="vp-btn vp-btn-ghost"
|
|
onClick={() => onWithdraw(request)}
|
|
>
|
|
Withdraw request
|
|
</button>
|
|
)}
|
|
{canRevise && (
|
|
<button
|
|
type="button"
|
|
className="vp-btn vp-btn-primary"
|
|
onClick={() => setRevising(true)}
|
|
>
|
|
Revise
|
|
</button>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|