import { useEffect, useMemo, useState } from "react"; import { useAuthContext } from "@/providers/auth-context"; import { getPrimaryUserRole, isAdminUser } from "@/lib/auth/user-utils"; import { useDropdownOptionsByCategory } from "@/domain/settings/dropdown-options/use-cases/use-dropdown-options-by-category"; import type { Service, ServiceInput, ServiceWorkOrderType } from "@/domain/services/types/service"; import { useCreateService, useCompletionDocTemplates, useDeactivateService, useServices, useUpdateService, } from "@/domain/services/use-cases/use-services"; export type ServiceStatus = "all" | "active" | "inactive"; export type ServiceForm = Omit & { completionDocTemplateId: string | number | null; }; export const EMPTY_SERVICE_FORM: ServiceForm = { name: "", trade: "", iconKey: "", requiresCompletionDocument: false, completionDocTemplateId: null, supportedWorkOrderTypes: ["PM", "Reactive", "Emergency"], }; export const DEFAULT_ICON_BY_TRADE: Record = { HVAC: "droplets", Plumbing: "droplets", Electrical: "zap", "General Building": "hammer", }; export function buildServiceInput(form: ServiceForm): { input: ServiceInput | null; error: string; } { const name = form.name.trim(); if (!name || !form.trade || !form.iconKey) { return { input: null, error: "Service Name, Trade, and Icon are required." }; } if (!form.supportedWorkOrderTypes.length) { return { input: null, error: "Select at least one Work Order Type." }; } if (form.requiresCompletionDocument && form.completionDocTemplateId == null) { return { input: null, error: "Select a completion document template when the document is required.", }; } const templateId = form.completionDocTemplateId; return { input: { ...form, name, completionDocTemplateId: templateId == null || templateId === "" ? null : Number(templateId), }, error: "", }; } function emptyForm(): ServiceForm { return { ...EMPTY_SERVICE_FORM, supportedWorkOrderTypes: [...EMPTY_SERVICE_FORM.supportedWorkOrderTypes], }; } function formFromService(service: Service): ServiceForm { return { name: service.name, trade: service.trade, iconKey: service.iconKey, requiresCompletionDocument: service.requiresCompletionDocument, completionDocTemplateId: service.completionDocTemplate?.id ?? null, supportedWorkOrderTypes: [...service.supportedWorkOrderTypes], }; } export function useServicesRegistryController() { const { user } = useAuthContext(); const role = getPrimaryUserRole(user?.userRoles).toLowerCase(); const canManage = isAdminUser(user?.userRoles) || role === "scheduler"; const canDeactivate = isAdminUser(user?.userRoles); const [status, setStatus] = useState("active"); const [search, setSearch] = useState(""); const [selected, setSelected] = useState(null); const [editorMode, setEditorMode] = useState<"add" | "edit" | null>(null); const [form, setForm] = useState(emptyForm); const [iconTouched, setIconTouched] = useState(false); const [formError, setFormError] = useState(""); const [deactivateTarget, setDeactivateTarget] = useState(null); const queryStatus = status === "active" ? true : status === "inactive" ? false : undefined; const servicesQuery = useServices(queryStatus); const { data: trades = [] } = useDropdownOptionsByCategory("Trade"); const { data: templates = [] } = useCompletionDocTemplates(); const createService = useCreateService(); const updateService = useUpdateService(); const deactivateService = useDeactivateService(); const filteredServices = useMemo(() => { const needle = search.trim().toLowerCase(); if (!needle) return servicesQuery.data ?? []; return (servicesQuery.data ?? []).filter( (service) => service.name.toLowerCase().includes(needle) || service.trade.toLowerCase().includes(needle), ); }, [search, servicesQuery.data]); useEffect(() => { if (!iconTouched && form.trade) { setForm((current) => ({ ...current, iconKey: DEFAULT_ICON_BY_TRADE[current.trade] ?? "wrench", })); } }, [form.trade, iconTouched]); const openCreate = () => { setSelected(null); setForm(emptyForm()); setIconTouched(false); setFormError(""); setEditorMode("add"); }; const openEdit = (service: Service) => { setSelected(service); setForm(formFromService(service)); setIconTouched(true); setFormError(""); setEditorMode("edit"); }; const closeEditor = () => { if (!createService.isPending && !updateService.isPending) setEditorMode(null); }; const updateForm = (key: K, value: ServiceForm[K]) => { if (key === "trade") setIconTouched(false); setFormError(""); setForm((current) => ({ ...current, [key]: value })); }; const toggleType = (type: ServiceWorkOrderType) => { setForm((current) => { const supported = current.supportedWorkOrderTypes.includes(type) ? current.supportedWorkOrderTypes.filter((item) => item !== type) : [...current.supportedWorkOrderTypes, type]; return { ...current, supportedWorkOrderTypes: supported }; }); }; const save = () => { const validation = buildServiceInput(form); if (!validation.input) { setFormError(validation.error); return; } const input = validation.input; if (editorMode === "add") { createService.mutate(input, { onSuccess: (created) => { setEditorMode(null); setSelected(created); }, }); } else if (editorMode === "edit" && selected) { updateService.mutate( { id: selected.id, input }, { onSuccess: (updated) => { setEditorMode(null); setSelected(updated); }, }, ); } }; const confirmDeactivate = () => { if (!deactivateTarget) return; deactivateService.mutate(deactivateTarget.id, { onSuccess: () => { setDeactivateTarget(null); setSelected(null); }, }); }; return { canManage, canDeactivate, status, setStatus, search, setSearch, selected, setSelected, editorMode, form, formError, trades, templates, filteredServices, services: servicesQuery.data ?? [], isLoading: servicesQuery.isLoading, error: servicesQuery.error, openCreate, openEdit, closeEditor, updateForm, toggleType, save, deactivateTarget, setDeactivateTarget, confirmDeactivate, isSaving: createService.isPending || updateService.isPending, isDeactivating: deactivateService.isPending, }; }