shoc-frontend-new/src/app/(protected)/vendors/preferences.tsx
npal 736157467c refactor: move vendor-portal dispatch actions and 2 pages onto proper data hooks
Two groups of components were calling the API directly instead of using
this codebase's standard React Query hooks, with no shared caching and
duplicated submit/error-handling boilerplate.

- checklist-section.tsx, comments-section.tsx, signoff-section.tsx,
  uplift-requests-section.tsx: replace direct vendorPortalApi calls and
  hand-rolled submitting/error state with mutation hooks in the new
  use-vendor-portal-dispatch-actions.ts. uplift-requests-section keeps
  its existing multi-step evidence-upload orchestration intact and only
  swaps the 3 raw API calls for mutateAsync, since restructuring that
  flow around declarative mutation callbacks carried more risk than
  value. Caught and fixed a real bug along the way: two components were
  passing a prop callback directly as a mutation's onSuccess, which
  would have forwarded React Query's extra callback arguments into it.

- followups/index.tsx, vendors/preferences.tsx: split from a single
  file doing state, API calls, and rendering into a hook (owns data
  and handlers) plus small presentational components, matching the
  task-templates refactor pattern. Added a missing centralized query
  key (vendorOperations.sitePreferences) that preferences.tsx was
  building as a raw inline array.

- use-list-page-filters.ts: small shared hook for search/page/pageSize
  state, used by the new followups hook.

29 new/updated tests across all touched files; none of these had
coverage for their mutation/error paths before.
2026-09-25 10:53:23 -05:00

80 lines
2.4 KiB
TypeScript

import { Autocomplete, Box, Button, Paper, Stack, TextField } from "@mui/material";
import { Text } from "@/components/ui/text";
import { AddVendorPreferenceControls } from "./_components/add-vendor-preference-controls";
import { PreferenceRow } from "./_components/preference-row";
import { useVendorPreferencesEditor } from "./_components/use-vendor-preferences-editor";
export default function VendorPreferencesPage() {
const {
locationId,
selectLocation,
locations,
draft,
trade,
setTrade,
vendorId,
setVendorId,
vendors,
vendorOptions,
error,
isSaving,
save,
move,
add,
remove,
} = useVendorPreferencesEditor();
return (
<Box className="flex flex-col gap-4 p-4">
<Box>
<Text variant="title" as="h1">
Preferred vendors by site
</Text>
<Text variant="description" tone="muted">
Preferred vendors rank first, but availability, conflict, trade, active-state, and
permission checks still apply.
</Text>
</Box>
<Paper variant="outlined" className="p-4">
<Autocomplete
options={locations}
getOptionLabel={(option) => option.name}
value={locations.find((location) => location.id === locationId) ?? null}
onChange={(_event, option) => selectLocation(option?.id ?? "")}
renderInput={(params) => <TextField {...params} label="Site" />}
/>
</Paper>
<Text variant="error" when={Boolean(error)}>
Unable to load preferred vendors. Please try again.
</Text>
{locationId !== "" && (
<>
<AddVendorPreferenceControls
trade={trade}
onTradeChange={setTrade}
vendorOptions={vendorOptions}
vendors={vendors}
vendorId={vendorId}
onVendorChange={setVendorId}
onAdd={add}
/>
<Stack spacing={1}>
{draft.map((item, index) => (
<PreferenceRow
key={`${item.vendorId}-${item.trade ?? "all"}`}
item={item}
index={index}
total={draft.length}
onMove={move}
onRemove={remove}
/>
))}
</Stack>
<Button variant="contained" onClick={save} disabled={isSaving}>
{isSaving ? "Saving…" : "Save preferences"}
</Button>
</>
)}
</Box>
);
}