mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-06 21:42:06 +00:00
112 lines
3.9 KiB
TypeScript
112 lines
3.9 KiB
TypeScript
import { useMemo, useState } from "react";
|
|
import { useNavigate } from "react-router-dom";
|
|
import { useQuery } from "@tanstack/react-query";
|
|
import { useVendorPortal } from "@/app/v/_components/vendor-portal-context";
|
|
import { vendorPortalApi } from "@/domain/vendor-portal/api/vendor-portal-api";
|
|
import {
|
|
formatVendorPortalDateTime,
|
|
vendorPortalStatusClass,
|
|
} from "@/domain/vendor-portal/lib/status-helpers";
|
|
import type { VendorPortalDispatchSummary } from "@/domain/vendor-portal/types/vendor-portal";
|
|
import { queryKeys } from "@/infra/query-key/query-key";
|
|
|
|
const FILTERS = [
|
|
{
|
|
key: "open",
|
|
label: "Open",
|
|
match: (dispatch: VendorPortalDispatchSummary) =>
|
|
dispatch.status !== "Completed" &&
|
|
dispatch.status !== "Verified" &&
|
|
dispatch.status !== "Cancelled",
|
|
},
|
|
{
|
|
key: "completed",
|
|
label: "Completed",
|
|
match: (dispatch: VendorPortalDispatchSummary) =>
|
|
dispatch.status === "Completed" || dispatch.status === "Verified",
|
|
},
|
|
{
|
|
key: "cancelled",
|
|
label: "Cancelled",
|
|
match: (dispatch: VendorPortalDispatchSummary) => dispatch.status === "Cancelled",
|
|
},
|
|
{ key: "all", label: "All", match: () => true },
|
|
] as const;
|
|
|
|
export default function VendorPortalDashboardPage() {
|
|
const { token } = useVendorPortal();
|
|
const navigate = useNavigate();
|
|
const [filter, setFilter] = useState<(typeof FILTERS)[number]["key"]>("open");
|
|
|
|
const {
|
|
data: dispatches = [],
|
|
isLoading,
|
|
error,
|
|
} = useQuery({
|
|
queryKey: queryKeys.vendorPortal.dispatches(token),
|
|
queryFn: () => vendorPortalApi.listDispatches(token),
|
|
enabled: Boolean(token),
|
|
});
|
|
|
|
const activeFilter = FILTERS.find((item) => item.key === filter) ?? FILTERS[0];
|
|
const filtered = useMemo(() => dispatches.filter(activeFilter.match), [dispatches, activeFilter]);
|
|
|
|
return (
|
|
<>
|
|
<div className="vp-card">
|
|
<h2>Your Work</h2>
|
|
<div className="vp-muted" style={{ marginBottom: 12 }}>
|
|
Active dispatches and work orders assigned to your company.
|
|
</div>
|
|
<div className="vp-actions" style={{ marginTop: 0 }}>
|
|
{FILTERS.map((item) => (
|
|
<button
|
|
key={item.key}
|
|
type="button"
|
|
className={`vp-btn ${filter === item.key ? "vp-btn-primary" : "vp-btn-ghost"}`}
|
|
onClick={() => setFilter(item.key)}
|
|
>
|
|
{item.label}{" "}
|
|
<span style={{ opacity: 0.7, fontWeight: 400 }}>
|
|
({dispatches.filter(item.match).length})
|
|
</span>
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{isLoading && <div className="vp-card vp-muted">Loading dispatches…</div>}
|
|
{error && <div className="vp-error">{error.message}</div>}
|
|
|
|
{!isLoading && !error && filtered.length === 0 && (
|
|
<div className="vp-card vp-empty">No dispatches in this view.</div>
|
|
)}
|
|
|
|
<div className="vp-dispatch-list">
|
|
{filtered.map((dispatch) => (
|
|
<div
|
|
key={dispatch.id}
|
|
className="vp-dispatch-card"
|
|
onClick={() => navigate(`../dispatch/${dispatch.id}`)}
|
|
>
|
|
<div>
|
|
<div className="vp-dispatch-title">
|
|
{dispatch.dispatchNumber} — {dispatch.workOrderTitle || "(untitled)"}
|
|
</div>
|
|
<div className="vp-dispatch-meta">
|
|
WO#{dispatch.internalWONumber}
|
|
{dispatch.locationName ? ` · ${dispatch.locationName}` : ""}
|
|
{dispatch.locationCity ? `, ${dispatch.locationCity}` : ""}
|
|
{dispatch.locationState ? `, ${dispatch.locationState}` : ""}
|
|
{dispatch.scheduledDate
|
|
? ` · Scheduled ${formatVendorPortalDateTime(dispatch.scheduledDate)}`
|
|
: ""}
|
|
</div>
|
|
</div>
|
|
<span className={vendorPortalStatusClass(dispatch.status)}>{dispatch.status}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</>
|
|
);
|
|
}
|