mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-09-30 06:53:12 +00:00
refactor(services): extract useServiceEditor to satisfy maintainability gate
useServicesRegistryController exceeded the changed-file max-lines-per-function budget (158 > 150). Move the add/edit form lifecycle into a useServiceEditor sub-hook; the controller's public return shape and behavior are unchanged.
This commit is contained in:
parent
ff4ecb37e4
commit
bbdf418ee3
1 changed files with 77 additions and 49 deletions
|
|
@ -87,47 +87,20 @@ function formFromService(service: Service): ServiceForm {
|
|||
};
|
||||
}
|
||||
|
||||
export function useServicesRegistryController() {
|
||||
const { user } = useAuthContext();
|
||||
const canManage = hasUserRole(user?.userRoles, "scheduler") || isAdminUser(user?.userRoles);
|
||||
const canDeactivate = isAdminUser(user?.userRoles);
|
||||
const [status, setStatus] = useState<ServiceStatus>("active");
|
||||
const [search, setSearch] = useState("");
|
||||
// Owns the add/edit form lifecycle so the top-level controller stays within the
|
||||
// changed-file maintainability budget. Mutations are passed in so `isSaving` and
|
||||
// `closeEditor` share the exact instances the controller reports on.
|
||||
function useServiceEditor(
|
||||
canonicalTrades: string[],
|
||||
canDeactivate: boolean,
|
||||
createService: ReturnType<typeof useCreateService>,
|
||||
updateService: ReturnType<typeof useUpdateService>,
|
||||
) {
|
||||
const [selected, setSelected] = useState<Service | null>(null);
|
||||
const [editorMode, setEditorMode] = useState<"add" | "edit" | null>(null);
|
||||
const [form, setForm] = useState<ServiceForm>(emptyForm);
|
||||
const [iconTouched, setIconTouched] = useState(false);
|
||||
const [formError, setFormError] = useState("");
|
||||
const [deactivateTarget, setDeactivateTarget] = useState<Service | null>(null);
|
||||
|
||||
const servicesQuery = useServices();
|
||||
// The backend rejects any Trade outside the canonical TradeCatalog (400
|
||||
// TradeInvalid), so the picker is sourced from the server-owned canonical
|
||||
// Trade reference exposed on Vendor/facets — the same list the vendor roster
|
||||
// form uses — never the editable DropdownOptions "Trade" category, whose
|
||||
// legacy seed values ("Plumbing", "General", ...) would fail on save.
|
||||
const facetsQuery = useVendorFacets();
|
||||
const canonicalTrades = useMemo(() => facetsQuery.data?.trades ?? [], [facetsQuery.data]);
|
||||
const { data: templates = [] } = useCompletionDocTemplates();
|
||||
const createService = useCreateService();
|
||||
const updateService = useUpdateService();
|
||||
const deactivateService = useDeactivateService();
|
||||
|
||||
const services = useMemo(() => servicesQuery.data ?? [], [servicesQuery.data]);
|
||||
const activeCount = services.filter((service) => service.isActive).length;
|
||||
const inactiveCount = services.length - activeCount;
|
||||
|
||||
const filteredServices = useMemo(() => {
|
||||
const needle = search.trim().toLowerCase();
|
||||
return services
|
||||
.filter((service) => (status === "active" ? service.isActive : !service.isActive))
|
||||
.filter(
|
||||
(service) =>
|
||||
!needle ||
|
||||
service.name.toLowerCase().includes(needle) ||
|
||||
service.trade.toLowerCase().includes(needle),
|
||||
);
|
||||
}, [search, services, status]);
|
||||
|
||||
// Keep the trade stored on the service being edited selectable even if it was
|
||||
// later deactivated in the canonical Trade catalog, so editing never silently
|
||||
|
|
@ -210,12 +183,67 @@ export function useServicesRegistryController() {
|
|||
}
|
||||
};
|
||||
|
||||
return {
|
||||
selected,
|
||||
setSelected,
|
||||
editorMode,
|
||||
form,
|
||||
formError,
|
||||
trades,
|
||||
openCreate,
|
||||
openEdit,
|
||||
closeEditor,
|
||||
updateForm,
|
||||
toggleType,
|
||||
save,
|
||||
};
|
||||
}
|
||||
|
||||
export function useServicesRegistryController() {
|
||||
const { user } = useAuthContext();
|
||||
const canManage = hasUserRole(user?.userRoles, "scheduler") || isAdminUser(user?.userRoles);
|
||||
const canDeactivate = isAdminUser(user?.userRoles);
|
||||
const [status, setStatus] = useState<ServiceStatus>("active");
|
||||
const [search, setSearch] = useState("");
|
||||
const [deactivateTarget, setDeactivateTarget] = useState<Service | null>(null);
|
||||
|
||||
const servicesQuery = useServices();
|
||||
// The backend rejects any Trade outside the canonical TradeCatalog (400
|
||||
// TradeInvalid), so the picker is sourced from the server-owned canonical
|
||||
// Trade reference exposed on Vendor/facets — the same list the vendor roster
|
||||
// form uses — never the editable DropdownOptions "Trade" category, whose
|
||||
// legacy seed values ("Plumbing", "General", ...) would fail on save.
|
||||
const facetsQuery = useVendorFacets();
|
||||
const canonicalTrades = useMemo(() => facetsQuery.data?.trades ?? [], [facetsQuery.data]);
|
||||
const { data: templates = [] } = useCompletionDocTemplates();
|
||||
const createService = useCreateService();
|
||||
const updateService = useUpdateService();
|
||||
const deactivateService = useDeactivateService();
|
||||
|
||||
const editor = useServiceEditor(canonicalTrades, canDeactivate, createService, updateService);
|
||||
|
||||
const services = useMemo(() => servicesQuery.data ?? [], [servicesQuery.data]);
|
||||
const activeCount = services.filter((service) => service.isActive).length;
|
||||
const inactiveCount = services.length - activeCount;
|
||||
|
||||
const filteredServices = useMemo(() => {
|
||||
const needle = search.trim().toLowerCase();
|
||||
return services
|
||||
.filter((service) => (status === "active" ? service.isActive : !service.isActive))
|
||||
.filter(
|
||||
(service) =>
|
||||
!needle ||
|
||||
service.name.toLowerCase().includes(needle) ||
|
||||
service.trade.toLowerCase().includes(needle),
|
||||
);
|
||||
}, [search, services, status]);
|
||||
|
||||
const confirmDeactivate = () => {
|
||||
if (!deactivateTarget) return;
|
||||
deactivateService.mutate(deactivateTarget.id, {
|
||||
onSuccess: () => {
|
||||
setDeactivateTarget(null);
|
||||
setSelected(null);
|
||||
editor.setSelected(null);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
@ -227,12 +255,12 @@ export function useServicesRegistryController() {
|
|||
setStatus,
|
||||
search,
|
||||
setSearch,
|
||||
selected,
|
||||
setSelected,
|
||||
editorMode,
|
||||
form,
|
||||
formError,
|
||||
trades,
|
||||
selected: editor.selected,
|
||||
setSelected: editor.setSelected,
|
||||
editorMode: editor.editorMode,
|
||||
form: editor.form,
|
||||
formError: editor.formError,
|
||||
trades: editor.trades,
|
||||
templates,
|
||||
filteredServices,
|
||||
services,
|
||||
|
|
@ -240,12 +268,12 @@ export function useServicesRegistryController() {
|
|||
inactiveCount,
|
||||
isLoading: servicesQuery.isLoading,
|
||||
error: servicesQuery.error,
|
||||
openCreate,
|
||||
openEdit,
|
||||
closeEditor,
|
||||
updateForm,
|
||||
toggleType,
|
||||
save,
|
||||
openCreate: editor.openCreate,
|
||||
openEdit: editor.openEdit,
|
||||
closeEditor: editor.closeEditor,
|
||||
updateForm: editor.updateForm,
|
||||
toggleType: editor.toggleType,
|
||||
save: editor.save,
|
||||
deactivateTarget,
|
||||
setDeactivateTarget,
|
||||
confirmDeactivate,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue