mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-06 13:32:06 +00:00
Merge pull request #266 from Sea-Haven-Industries/fix/ab/sh-400-vendor-readonly-uplifts
Some checks are pending
Frontend checks / static (push) Waiting to run
Frontend checks / build (push) Waiting to run
Frontend checks / unit (push) Waiting to run
Frontend checks / Visual regression (push) Waiting to run
Frontend checks / browser-smoke (push) Waiting to run
Frontend checks / governance (push) Waiting to run
Frontend checks / ci-complete (push) Blocked by required conditions
Deploy Web / Resolve target (push) Waiting to run
Deploy Web / Deploy SPA to (push) Blocked by required conditions
Some checks are pending
Frontend checks / static (push) Waiting to run
Frontend checks / build (push) Waiting to run
Frontend checks / unit (push) Waiting to run
Frontend checks / Visual regression (push) Waiting to run
Frontend checks / browser-smoke (push) Waiting to run
Frontend checks / governance (push) Waiting to run
Frontend checks / ci-complete (push) Blocked by required conditions
Deploy Web / Resolve target (push) Waiting to run
Deploy Web / Deploy SPA to (push) Blocked by required conditions
Show work-order uplift requests read-only in the Vendor Portal
This commit is contained in:
commit
2084cfa453
3 changed files with 103 additions and 4 deletions
|
|
@ -25,6 +25,21 @@ function statusClassName(status: string): string {
|
||||||
return `vp-uplift-status-${String(status).toLowerCase()}`;
|
return `vp-uplift-status-${String(status).toLowerCase()}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function availableActions(request: VendorPortalUpliftRequest, locked: boolean, canAct: boolean) {
|
||||||
|
// Requests raised from the work order are visible but read-only to the vendor.
|
||||||
|
const actionable = !locked && canAct && request.raisedByVendor !== false;
|
||||||
|
return {
|
||||||
|
canWithdraw: actionable && WITHDRAWABLE_STATUSES.has(request.status),
|
||||||
|
canRevise: actionable && request.status === "ChangesRequested",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// A vendor request stores the requested new NTE total; a work-order request stores the increase.
|
||||||
|
function requestedTotal(request: VendorPortalUpliftRequest): number {
|
||||||
|
const requested = Number(request.requestedNTE);
|
||||||
|
return request.raisedByVendor === false ? Number(request.currentNTE ?? 0) + requested : requested;
|
||||||
|
}
|
||||||
|
|
||||||
export function UpliftRequestCard({
|
export function UpliftRequestCard({
|
||||||
request,
|
request,
|
||||||
locked,
|
locked,
|
||||||
|
|
@ -38,16 +53,14 @@ export function UpliftRequestCard({
|
||||||
mediaUsage,
|
mediaUsage,
|
||||||
}: UpliftRequestCardProps) {
|
}: UpliftRequestCardProps) {
|
||||||
const [revising, setRevising] = useState(false);
|
const [revising, setRevising] = useState(false);
|
||||||
const canWithdraw = !locked && canAct && WITHDRAWABLE_STATUSES.has(request.status);
|
const { canWithdraw, canRevise } = availableActions(request, locked, canAct);
|
||||||
const canRevise = !locked && canAct && request.status === "ChangesRequested";
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="vp-uplift-row">
|
<div className="vp-uplift-row">
|
||||||
<div className="vp-row">
|
<div className="vp-row">
|
||||||
<strong>Requested</strong>
|
<strong>Requested</strong>
|
||||||
<span>
|
<span>
|
||||||
${Number(request.requestedNTE).toFixed(2)} (was $
|
${requestedTotal(request).toFixed(2)} (was ${Number(request.currentNTE ?? 0).toFixed(2)})
|
||||||
{Number(request.currentNTE ?? 0).toFixed(2)})
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="vp-row">
|
<div className="vp-row">
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,8 @@ export interface VendorPortalUpliftRequest {
|
||||||
requestedNTE: number;
|
requestedNTE: number;
|
||||||
currentNTE?: number;
|
currentNTE?: number;
|
||||||
vendorReason?: string;
|
vendorReason?: string;
|
||||||
|
/** False when the request was raised from the work order; the vendor cannot revise or withdraw it. */
|
||||||
|
raisedByVendor?: boolean;
|
||||||
requestedAt?: string;
|
requestedAt?: string;
|
||||||
decidedAt?: string;
|
decidedAt?: string;
|
||||||
decidedByName?: string;
|
decidedByName?: string;
|
||||||
|
|
|
||||||
84
src/test/app/v/uplift-request-card.test.tsx
Normal file
84
src/test/app/v/uplift-request-card.test.tsx
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
import { render, screen } from "@testing-library/react";
|
||||||
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
import { UpliftRequestCard } from "@/app/v/[token]/dispatch/_components/uplift-request-card";
|
||||||
|
import type { VendorPortalUpliftRequest } from "@/domain/vendor-portal/types/vendor-portal";
|
||||||
|
|
||||||
|
function request(overrides: Partial<VendorPortalUpliftRequest> = {}): VendorPortalUpliftRequest {
|
||||||
|
return {
|
||||||
|
id: 7,
|
||||||
|
status: "Pending",
|
||||||
|
requestedNTE: 1500,
|
||||||
|
currentNTE: 1000,
|
||||||
|
vendorReason: "Extra parts",
|
||||||
|
raisedByVendor: true,
|
||||||
|
...overrides,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderCard(req: VendorPortalUpliftRequest) {
|
||||||
|
return render(
|
||||||
|
<UpliftRequestCard
|
||||||
|
request={req}
|
||||||
|
locked={false}
|
||||||
|
canAct
|
||||||
|
uploadingPercent={undefined}
|
||||||
|
submitting={false}
|
||||||
|
errorMessage=""
|
||||||
|
onWithdraw={vi.fn()}
|
||||||
|
onRevise={vi.fn()}
|
||||||
|
onCancelRevise={vi.fn()}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("UpliftRequestCard actions", () => {
|
||||||
|
it("offers Withdraw on a pending request the vendor raised", () => {
|
||||||
|
renderCard(request({ status: "Pending" }));
|
||||||
|
|
||||||
|
expect(screen.getByRole("button", { name: /withdraw request/i })).toBeInTheDocument();
|
||||||
|
expect(screen.queryByRole("button", { name: /revise/i })).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("offers Withdraw and Revise when changes are requested on a request the vendor raised", () => {
|
||||||
|
renderCard(request({ status: "ChangesRequested" }));
|
||||||
|
|
||||||
|
expect(screen.getByRole("button", { name: /withdraw request/i })).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole("button", { name: /revise/i })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows a work-order increase as the new NTE total", () => {
|
||||||
|
renderCard(request({ raisedByVendor: false, requestedNTE: 90, currentNTE: 600 }));
|
||||||
|
|
||||||
|
expect(screen.getByText("Requested").nextElementSibling).toHaveTextContent(
|
||||||
|
"$690.00 (was $600.00)",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows a vendor request's requested total unchanged", () => {
|
||||||
|
renderCard(request({ raisedByVendor: true, requestedNTE: 900, currentNTE: 600 }));
|
||||||
|
|
||||||
|
expect(screen.getByText("Requested").nextElementSibling).toHaveTextContent(
|
||||||
|
"$900.00 (was $600.00)",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("treats a request without the raised-by flag as vendor-raised", () => {
|
||||||
|
renderCard(request({ raisedByVendor: undefined, requestedNTE: 900, currentNTE: 600 }));
|
||||||
|
|
||||||
|
expect(screen.getByText("Requested").nextElementSibling).toHaveTextContent(
|
||||||
|
"$900.00 (was $600.00)",
|
||||||
|
);
|
||||||
|
expect(screen.getByRole("button", { name: /withdraw request/i })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.each(["Pending", "ChangesRequested"])(
|
||||||
|
"shows a %s request raised from the work order read-only",
|
||||||
|
(status) => {
|
||||||
|
renderCard(request({ status, raisedByVendor: false, requestedNTE: 90, currentNTE: 600 }));
|
||||||
|
|
||||||
|
expect(screen.getByText(status)).toBeInTheDocument();
|
||||||
|
expect(screen.queryByRole("button", { name: /withdraw request/i })).not.toBeInTheDocument();
|
||||||
|
expect(screen.queryByRole("button", { name: /revise/i })).not.toBeInTheDocument();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
Loading…
Add table
Reference in a new issue