fix(services): use completion document templates

This commit is contained in:
Codex Review Integration 2026-09-16 19:13:00 -03:00
parent 0b63d882ab
commit b449df97e6
8 changed files with 68 additions and 7 deletions

View file

@ -31,6 +31,7 @@ export const API_PATHS = {
boardSearch: "workorders/board/search", boardSearch: "workorders/board/search",
boardPatch: (id: string | number) => `workorders/${id}/board`, boardPatch: (id: string | number) => `workorders/${id}/board`,
boardDetail: (id: string | number) => `workorders/${id}/detail`, boardDetail: (id: string | number) => `workorders/${id}/detail`,
completionTemplates: "workorders/completion-templates",
comments: (id: string | number) => `workorders/${id}/comments`, comments: (id: string | number) => `workorders/${id}/comments`,
commentItem: (workOrderId: string | number, commentId: string | number) => commentItem: (workOrderId: string | number, commentId: string | number) =>
`workorders/${workOrderId}/comments/${commentId}`, `workorders/${workOrderId}/comments/${commentId}`,

View file

@ -24,9 +24,9 @@ import {
} from "@mui/material"; } from "@mui/material";
import { Droplets, Hammer, Settings2, Wrench, Zap } from "lucide-react"; import { Droplets, Hammer, Settings2, Wrench, Zap } from "lucide-react";
import type { DropdownOption } from "@/domain/settings/dropdown-options/types/dropdown-option"; import type { DropdownOption } from "@/domain/settings/dropdown-options/types/dropdown-option";
import type { TaskTemplate } from "@/domain/settings/task-templates/types/task-template";
import { import {
SERVICE_WORK_ORDER_TYPES, SERVICE_WORK_ORDER_TYPES,
type CompletionDocTemplateOption,
type Service, type Service,
type ServiceWorkOrderType, type ServiceWorkOrderType,
} from "@/domain/services/types/service"; } from "@/domain/services/types/service";
@ -142,7 +142,7 @@ type EditorProps = {
form: ServiceForm; form: ServiceForm;
formError: string; formError: string;
trades: DropdownOption[]; trades: DropdownOption[];
templates: TaskTemplate[]; templates: CompletionDocTemplateOption[];
isSaving: boolean; isSaving: boolean;
onClose: () => void; onClose: () => void;
onSave: () => void; onSave: () => void;

View file

@ -1,8 +1,16 @@
import { apiGet, apiPost, apiPut } from "@/api/api"; import { apiGet, apiPost, apiPut } from "@/api/api";
import { API_PATHS } from "@/api/api-paths"; import { API_PATHS } from "@/api/api-paths";
import { handleApiResponse } from "@/api/handle-api-response"; import { handleApiResponse } from "@/api/handle-api-response";
import { mapService, mapServices } from "@/domain/services/mappers/service-mapper"; import {
import type { Service, ServiceInput } from "@/domain/services/types/service"; mapCompletionDocTemplates,
mapService,
mapServices,
} from "@/domain/services/mappers/service-mapper";
import type {
CompletionDocTemplateOption,
Service,
ServiceInput,
} from "@/domain/services/types/service";
const SERVICE_WORK_ORDER_TYPE_API = { const SERVICE_WORK_ORDER_TYPE_API = {
PM: 2, PM: 2,
@ -27,6 +35,11 @@ export const servicesApi = {
return mapServices(handleApiResponse(data)); return mapServices(handleApiResponse(data));
}, },
getCompletionTemplates: async (): Promise<CompletionDocTemplateOption[]> => {
const data = await apiGet<unknown>(API_PATHS.workOrder.completionTemplates);
return mapCompletionDocTemplates(handleApiResponse(data));
},
create: async (input: ServiceInput): Promise<Service> => { create: async (input: ServiceInput): Promise<Service> => {
const data = await apiPost<unknown>(API_PATHS.services.list, toApiInput(input)); const data = await apiPost<unknown>(API_PATHS.services.list, toApiInput(input));
return mapService(handleApiResponse(data)); return mapService(handleApiResponse(data));

View file

@ -1,4 +1,5 @@
import type { import type {
CompletionDocTemplateOption,
LinkedServiceTemplate, LinkedServiceTemplate,
Service, Service,
ServiceWorkOrderType, ServiceWorkOrderType,
@ -87,3 +88,26 @@ export function mapServices(raw: unknown): Service[] {
? payload.map(mapService).filter((service) => service.id !== "") ? payload.map(mapService).filter((service) => service.id !== "")
: []; : [];
} }
export function mapCompletionDocTemplates(raw: unknown): CompletionDocTemplateOption[] {
const payload = Array.isArray(raw)
? raw
: (() => {
const record = asRecord(raw);
return record.items ?? record.Items ?? record.data ?? record.Data;
})();
return Array.isArray(payload)
? payload.flatMap((item) => {
const record = asRecord(item);
const id = readId(record);
return id === ""
? []
: [
{
id,
name: readString(record, "name", "Name"),
} satisfies CompletionDocTemplateOption,
];
})
: [];
}

View file

@ -7,6 +7,8 @@ export interface LinkedServiceTemplate {
name: string; name: string;
} }
export type CompletionDocTemplateOption = LinkedServiceTemplate;
export interface Service { export interface Service {
id: string | number; id: string | number;
name: string; name: string;

View file

@ -2,10 +2,10 @@ import { useEffect, useMemo, useState } from "react";
import { useAuthContext } from "@/providers/auth-context"; import { useAuthContext } from "@/providers/auth-context";
import { getPrimaryUserRole, isAdminUser } from "@/lib/auth/user-utils"; import { getPrimaryUserRole, isAdminUser } from "@/lib/auth/user-utils";
import { useDropdownOptionsByCategory } from "@/domain/settings/dropdown-options/use-cases/use-dropdown-options-by-category"; import { useDropdownOptionsByCategory } from "@/domain/settings/dropdown-options/use-cases/use-dropdown-options-by-category";
import { useTaskTemplates } from "@/domain/settings/task-templates/use-cases/use-task-templates";
import type { Service, ServiceInput, ServiceWorkOrderType } from "@/domain/services/types/service"; import type { Service, ServiceInput, ServiceWorkOrderType } from "@/domain/services/types/service";
import { import {
useCreateService, useCreateService,
useCompletionDocTemplates,
useDeactivateService, useDeactivateService,
useServices, useServices,
useUpdateService, useUpdateService,
@ -95,7 +95,7 @@ export function useServicesRegistryController() {
const queryStatus = status === "active" ? true : status === "inactive" ? false : undefined; const queryStatus = status === "active" ? true : status === "inactive" ? false : undefined;
const servicesQuery = useServices(queryStatus); const servicesQuery = useServices(queryStatus);
const { data: trades = [] } = useDropdownOptionsByCategory("Trade"); const { data: trades = [] } = useDropdownOptionsByCategory("Trade");
const { data: templates = [] } = useTaskTemplates(); const { data: templates = [] } = useCompletionDocTemplates();
const createService = useCreateService(); const createService = useCreateService();
const updateService = useUpdateService(); const updateService = useUpdateService();
const deactivateService = useDeactivateService(); const deactivateService = useDeactivateService();

View file

@ -1,7 +1,11 @@
import { useMutation, useQuery, useQueryClient, type UseQueryResult } from "@tanstack/react-query"; import { useMutation, useQuery, useQueryClient, type UseQueryResult } from "@tanstack/react-query";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
import { servicesApi } from "@/domain/services/api/services-api"; import { servicesApi } from "@/domain/services/api/services-api";
import type { Service, ServiceInput } from "@/domain/services/types/service"; import type {
CompletionDocTemplateOption,
Service,
ServiceInput,
} from "@/domain/services/types/service";
import { queryKeys } from "@/infra/query-key/query-key"; import { queryKeys } from "@/infra/query-key/query-key";
export function useServices(isActive?: boolean): UseQueryResult<Service[], Error> { export function useServices(isActive?: boolean): UseQueryResult<Service[], Error> {
@ -12,6 +16,14 @@ export function useServices(isActive?: boolean): UseQueryResult<Service[], Error
}); });
} }
export function useCompletionDocTemplates(): UseQueryResult<CompletionDocTemplateOption[], Error> {
return useQuery({
queryKey: queryKeys.services.completionTemplates(),
queryFn: servicesApi.getCompletionTemplates,
staleTime: 5 * 60 * 1000,
});
}
export function useCreateService() { export function useCreateService() {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation({ return useMutation({

View file

@ -77,4 +77,13 @@ describe("servicesApi", () => {
supportedWorkOrderTypes: [2], supportedWorkOrderTypes: [2],
}); });
}); });
it("loads active Completion Document Template options from the completion route", async () => {
apiGet.mockResolvedValue([{ Id: 4, Name: "HVAC checklist", IsActive: true }]);
const templates = await servicesApi.getCompletionTemplates();
expect(apiGet).toHaveBeenCalledWith(API_PATHS.workOrder.completionTemplates);
expect(templates).toEqual([{ id: 4, name: "HVAC checklist" }]);
});
}); });