mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-06 04:12:09 +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() {
|
// Owns the add/edit form lifecycle so the top-level controller stays within the
|
||||||
const { user } = useAuthContext();
|
// changed-file maintainability budget. Mutations are passed in so `isSaving` and
|
||||||
const canManage = hasUserRole(user?.userRoles, "scheduler") || isAdminUser(user?.userRoles);
|
// `closeEditor` share the exact instances the controller reports on.
|
||||||
const canDeactivate = isAdminUser(user?.userRoles);
|
function useServiceEditor(
|
||||||
const [status, setStatus] = useState<ServiceStatus>("active");
|
canonicalTrades: string[],
|
||||||
const [search, setSearch] = useState("");
|
canDeactivate: boolean,
|
||||||
|
createService: ReturnType<typeof useCreateService>,
|
||||||
|
updateService: ReturnType<typeof useUpdateService>,
|
||||||
|
) {
|
||||||
const [selected, setSelected] = useState<Service | null>(null);
|
const [selected, setSelected] = useState<Service | null>(null);
|
||||||
const [editorMode, setEditorMode] = useState<"add" | "edit" | null>(null);
|
const [editorMode, setEditorMode] = useState<"add" | "edit" | null>(null);
|
||||||
const [form, setForm] = useState<ServiceForm>(emptyForm);
|
const [form, setForm] = useState<ServiceForm>(emptyForm);
|
||||||
const [iconTouched, setIconTouched] = useState(false);
|
const [iconTouched, setIconTouched] = useState(false);
|
||||||
const [formError, setFormError] = useState("");
|
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
|
// 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
|
// 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 = () => {
|
const confirmDeactivate = () => {
|
||||||
if (!deactivateTarget) return;
|
if (!deactivateTarget) return;
|
||||||
deactivateService.mutate(deactivateTarget.id, {
|
deactivateService.mutate(deactivateTarget.id, {
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
setDeactivateTarget(null);
|
setDeactivateTarget(null);
|
||||||
setSelected(null);
|
editor.setSelected(null);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
@ -227,12 +255,12 @@ export function useServicesRegistryController() {
|
||||||
setStatus,
|
setStatus,
|
||||||
search,
|
search,
|
||||||
setSearch,
|
setSearch,
|
||||||
selected,
|
selected: editor.selected,
|
||||||
setSelected,
|
setSelected: editor.setSelected,
|
||||||
editorMode,
|
editorMode: editor.editorMode,
|
||||||
form,
|
form: editor.form,
|
||||||
formError,
|
formError: editor.formError,
|
||||||
trades,
|
trades: editor.trades,
|
||||||
templates,
|
templates,
|
||||||
filteredServices,
|
filteredServices,
|
||||||
services,
|
services,
|
||||||
|
|
@ -240,12 +268,12 @@ export function useServicesRegistryController() {
|
||||||
inactiveCount,
|
inactiveCount,
|
||||||
isLoading: servicesQuery.isLoading,
|
isLoading: servicesQuery.isLoading,
|
||||||
error: servicesQuery.error,
|
error: servicesQuery.error,
|
||||||
openCreate,
|
openCreate: editor.openCreate,
|
||||||
openEdit,
|
openEdit: editor.openEdit,
|
||||||
closeEditor,
|
closeEditor: editor.closeEditor,
|
||||||
updateForm,
|
updateForm: editor.updateForm,
|
||||||
toggleType,
|
toggleType: editor.toggleType,
|
||||||
save,
|
save: editor.save,
|
||||||
deactivateTarget,
|
deactivateTarget,
|
||||||
setDeactivateTarget,
|
setDeactivateTarget,
|
||||||
confirmDeactivate,
|
confirmDeactivate,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue