From a710e21281026287a869fca293407902aba4499f Mon Sep 17 00:00:00 2001 From: Codex Review Integration Date: Tue, 18 Aug 2026 13:09:33 -0300 Subject: [PATCH 01/10] fix(vendors): additive technician add for existing companies (SH-250, SH-246) --- .../_components/use-vendor-roster-form.ts | 37 ++- .../use-vendor-roster-selection.ts | 26 +- .../vendors/api/vendor-company-roster-api.ts | 22 +- .../vendors/mappers/vendor-roster-mapper.ts | 22 ++ .../use-save-vendor-company-roster.ts | 64 +++- .../vendors/use-vendor-roster-form.test.tsx | 302 +++++++++++++++++- .../api/vendor-company-roster-api.test.ts | 71 ++++ ...ve-vendor-company-roster-additive.test.tsx | 234 ++++++++++++++ 8 files changed, 756 insertions(+), 22 deletions(-) create mode 100644 src/test/domain/vendors/use-cases/use-save-vendor-company-roster-additive.test.tsx diff --git a/src/app/(protected)/vendors/_components/use-vendor-roster-form.ts b/src/app/(protected)/vendors/_components/use-vendor-roster-form.ts index 6bf89b1f..61f6f678 100644 --- a/src/app/(protected)/vendors/_components/use-vendor-roster-form.ts +++ b/src/app/(protected)/vendors/_components/use-vendor-roster-form.ts @@ -1,4 +1,5 @@ import { useCallback, useMemo, useState } from "react"; +import { toast } from "react-toastify"; import { useForm, useWatch, type FieldErrors } from "react-hook-form"; import { emptyVendorCompanyRosterForm, @@ -8,6 +9,7 @@ import { } from "@/domain/vendors/schemas/vendor-roster-schema"; import { useVendorCompanyRoster } from "@/domain/vendors/use-cases/use-vendor-company-roster"; import { + buildAdditiveRosterPatch, getSingleStatusOnlyChange, useSaveVendorCompanyRoster, } from "@/domain/vendors/use-cases/use-save-vendor-company-roster"; @@ -129,17 +131,38 @@ export function useVendorRosterForm({ clearConflict(); }, [clearConflict, createDefaults, reset, resetSelection]); - const committedRoster = mode === "update" ? routeRoster : selection.selectedRoster; - const isUpdate = mode === "update" || selection.selectedRoster != null; - const submit = (formValues: VendorCompanyRosterFormValues) => { setConflict(null); + const values = withoutBlankNewTechnicians(formValues); + if (mode === "create" && selection.selectedRoster != null) { + const selected = selection.selectedRoster; + if (!buildAdditiveRosterPatch(selected, values, selected.rowVersion)) { + toast.error("Enter at least one technician to add to this vendor."); + return; + } + save.mutate( + { + mode: "add", + values, + companyId: selected.companyId, + rowVersion: selected.rowVersion, + originalRoster: selected, + }, + { + onSuccess: (data) => onSuccess?.(data), + onError: (error) => { + if (isVendorRosterConflictError(error)) setConflict(error.conflict); + }, + }, + ); + return; + } save.mutate( { - mode: isUpdate ? "update" : "create", - values: withoutBlankNewTechnicians(formValues), - companyId: isUpdate ? (committedRoster?.companyId ?? companyId) : undefined, - rowVersion: isUpdate ? committedRoster?.rowVersion : undefined, + mode: mode === "update" ? "update" : "create", + values, + companyId: mode === "update" ? (routeRoster?.companyId ?? companyId) : undefined, + rowVersion: mode === "update" ? routeRoster?.rowVersion : undefined, originalRoster: mode === "update" ? routeRoster : undefined, }, { diff --git a/src/app/(protected)/vendors/_components/use-vendor-roster-selection.ts b/src/app/(protected)/vendors/_components/use-vendor-roster-selection.ts index cce00e18..4eb141b1 100644 --- a/src/app/(protected)/vendors/_components/use-vendor-roster-selection.ts +++ b/src/app/(protected)/vendors/_components/use-vendor-roster-selection.ts @@ -1,6 +1,7 @@ import { useCallback, useRef, useState } from "react"; import { useQueryClient, type UseQueryResult } from "@tanstack/react-query"; import { + emptyRosterTechnician, emptyVendorCompanyRosterForm, type VendorCompanyRosterFormValues, } from "@/domain/vendors/schemas/vendor-roster-schema"; @@ -53,6 +54,20 @@ export function toFormValues(roster: VendorCompanyRoster): VendorCompanyRosterFo }; } +export function toCompanyFormValues(roster: VendorCompanyRoster): VendorCompanyRosterFormValues { + return { ...toFormValues(roster), technicians: [] }; +} + +function withCompanyFieldsOnly( + roster: VendorCompanyRoster, +): (current: VendorCompanyRosterFormValues) => VendorCompanyRosterFormValues { + return (current) => ({ + ...toCompanyFormValues(roster), + technicians: + current.technicians.length > 0 ? current.technicians : [{ ...emptyRosterTechnician }], + }); +} + export function useVendorRosterSelection({ mode, reset, @@ -91,7 +106,7 @@ export function useVendorRosterSelection({ try { const roster = await fetchRosterByCompany(company.companyId); if (requestIdRef.current !== requestId) return; - reset(toFormValues(roster)); + reset(withCompanyFieldsOnly(roster)); setSelectedRoster(roster); clearConflict(); retryTargetRef.current = null; @@ -107,7 +122,11 @@ export function useVendorRosterSelection({ (nextName = "") => { requestIdRef.current += 1; if (selectedRoster) { - reset({ ...emptyVendorCompanyRosterForm, name: nextName }); + reset((current) => ({ + ...emptyVendorCompanyRosterForm, + name: nextName, + technicians: current.technicians, + })); } setSelectedRoster(null); clearConflict(); @@ -134,7 +153,6 @@ export function useVendorRosterSelection({ void fetchRosterByCompany(selectedRoster.companyId) .then((roster) => { if (requestIdRef.current !== requestId) return; - reset(toFormValues(roster)); setSelectedRoster(roster); retryTargetRef.current = null; }) @@ -143,7 +161,7 @@ export function useVendorRosterSelection({ setLoadErrorMessage(RELOAD_ERROR_MESSAGE); }); } - }, [clearConflict, fetchRosterByCompany, mode, query, reset, selectedRoster]); + }, [clearConflict, fetchRosterByCompany, mode, query, selectedRoster]); const retryLoad = useCallback(() => { const target = retryTargetRef.current; diff --git a/src/domain/vendors/api/vendor-company-roster-api.ts b/src/domain/vendors/api/vendor-company-roster-api.ts index dd9c978a..fb051bbb 100644 --- a/src/domain/vendors/api/vendor-company-roster-api.ts +++ b/src/domain/vendors/api/vendor-company-roster-api.ts @@ -1,14 +1,16 @@ import { isHTTPError } from "ky"; import { API_PATHS } from "@/api/api-paths"; -import { apiGet, apiPost, apiPut } from "@/api/api"; +import { apiGet, apiPatch, apiPost, apiPut } from "@/api/api"; import { handleApiResponse } from "@/api/handle-api-response"; import { mapRosterConflict, mapVendorCompanyRoster, + mapVendorRosterAdditivePatchToBackend, mapVendorRosterToBackend, } from "@/domain/vendors/mappers/vendor-roster-mapper"; import { VendorRosterConflictError } from "@/domain/vendors/lib/vendor-roster-conflict"; import type { VendorCompanyRoster } from "@/domain/vendors/types/vendor"; +import type { VendorRosterAdditivePatch } from "@/domain/vendors/mappers/vendor-roster-mapper"; const STALE_MESSAGE = "This company was changed by another session. Reload the latest version and try again."; @@ -97,4 +99,22 @@ export const vendorCompanyRosterApi = { throw error; } }, + + addTechnicians: async ( + companyId: string | number, + patch: VendorRosterAdditivePatch, + ): Promise => { + try { + const data = await apiPatch( + API_PATHS.vendorCompanyRoster.byCompany(companyId), + mapVendorRosterAdditivePatchToBackend(patch), + ); + return mapVendorCompanyRoster(handleApiResponse(data)); + } catch (error) { + if (isHTTPError(error) && error.response.status === 409) { + await throwRosterConflict(error); + } + throw error; + } + }, }; diff --git a/src/domain/vendors/mappers/vendor-roster-mapper.ts b/src/domain/vendors/mappers/vendor-roster-mapper.ts index 8dd450c4..e4de67dd 100644 --- a/src/domain/vendors/mappers/vendor-roster-mapper.ts +++ b/src/domain/vendors/mappers/vendor-roster-mapper.ts @@ -134,6 +134,28 @@ export function mapVendorRosterToBackend(values: unknown): Record; +} + +export function mapVendorRosterAdditivePatchToBackend( + patch: VendorRosterAdditivePatch, +): Record { + const item = asRecord(patch); + const payload: Record = { + rowVersion: readString(item, "rowVersion", "RowVersion"), + addTechnicians: patch.addTechnicians.map((technician) => { + const mapped = mapRosterTechnicianToBackend(technician); + delete mapped.id; + return mapped; + }), + }; + if (patch.companyFields) payload.companyFields = { ...patch.companyFields }; + return payload; +} + function mapBlockedWorkOrder(raw: unknown): VendorRosterBlockedWorkOrder { const item = asRecord(raw); return { diff --git a/src/domain/vendors/use-cases/use-save-vendor-company-roster.ts b/src/domain/vendors/use-cases/use-save-vendor-company-roster.ts index 37532220..1d1c4865 100644 --- a/src/domain/vendors/use-cases/use-save-vendor-company-roster.ts +++ b/src/domain/vendors/use-cases/use-save-vendor-company-roster.ts @@ -11,7 +11,7 @@ import type { import { queryKeys } from "@/infra/query-key/query-key"; export interface SaveVendorCompanyRosterInput { - mode: "create" | "update"; + mode: "create" | "update" | "add"; values: VendorCompanyRosterFormValues; companyId?: string | number | null; rowVersion?: string; @@ -65,6 +65,55 @@ export function getSingleStatusOnlyChange( return changed.length === 1 ? changed[0] : null; } +export interface VendorRosterAdditivePatchInput { + rowVersion: string; + addTechnicians: Array<{ + contactName: string; + phone: string; + email: string; + preferredContact?: string; + tradeSpecialties: string; + isActive: boolean; + }>; + companyFields?: Record; +} + +function hasTechnicianContent(technician: { + contactName: string; + phone: string; + email: string; + tradeSpecialties: string; +}): boolean { + return Boolean( + technician.contactName.trim() || + technician.phone.trim() || + technician.email.trim() || + technician.tradeSpecialties.trim(), + ); +} + +export function buildAdditiveRosterPatch( + roster: VendorCompanyRoster | null | undefined, + values: VendorCompanyRosterFormValues, + rowVersion: string, +): VendorRosterAdditivePatchInput | null { + const addTechnicians = values.technicians.filter( + (technician) => technician.id == null && hasTechnicianContent(technician), + ); + const companyFields: Record = {}; + if (roster) { + for (const field of COMPANY_FIELDS) { + if (values[field] !== roster[field]) companyFields[field] = values[field]; + } + } + if (addTechnicians.length === 0 && Object.keys(companyFields).length === 0) return null; + return { + rowVersion, + addTechnicians, + ...(Object.keys(companyFields).length > 0 ? { companyFields } : {}), + }; +} + export function useSaveVendorCompanyRoster(): UseMutationResult< VendorCompanyRoster, unknown, @@ -108,17 +157,26 @@ export function useSaveVendorCompanyRoster(): UseMutationResult< if (!rowVersion) throw new Error("Row version is required to update"); return vendorCompanyRosterApi.update(companyId, values, rowVersion); } + if (mode === "add") { + if (companyId === undefined || companyId === null || companyId === "") { + throw new Error("Company id is required to add technicians"); + } + if (!rowVersion) throw new Error("Row version is required to add technicians"); + const patch = buildAdditiveRosterPatch(originalRoster, values, rowVersion); + if (!patch) throw new Error("Nothing to add to this vendor company"); + return vendorCompanyRosterApi.addTechnicians(companyId, patch); + } return vendorCompanyRosterApi.create(values); }, onSuccess: (data, variables) => { void queryClient.invalidateQueries({ queryKey: queryKeys.vendors.all }); - if (variables.mode === "update" && variables.companyId) { + if ((variables.mode === "update" || variables.mode === "add") && variables.companyId) { void queryClient.invalidateQueries({ queryKey: queryKeys.vendors.roster("companyId", variables.companyId), }); } toast.success( - variables.mode === "update" ? "Vendor company updated" : "Vendor company created", + variables.mode === "create" ? "Vendor company created" : "Vendor company updated", ); }, onError: (error) => { diff --git a/src/test/app/(protected)/vendors/use-vendor-roster-form.test.tsx b/src/test/app/(protected)/vendors/use-vendor-roster-form.test.tsx index 2e9f8f1f..80091636 100644 --- a/src/test/app/(protected)/vendors/use-vendor-roster-form.test.tsx +++ b/src/test/app/(protected)/vendors/use-vendor-roster-form.test.tsx @@ -12,15 +12,19 @@ vi.mock("@/domain/vendors/api/vendor-company-roster-api", () => ({ })); vi.mock("@/domain/vendors/use-cases/use-vendor-company-roster", () => ({ - useVendorCompanyRoster: () => ({ - data: undefined, - isLoading: false, - isError: false, - error: null, - refetch: vi.fn(), - }), + useVendorCompanyRoster: () => rosterQueryResult, })); +vi.mock("react-toastify", () => ({ toast: { success: vi.fn(), error: vi.fn() } })); + +let rosterQueryResult: { + data: VendorCompanyRoster | undefined; + isLoading: boolean; + isError: boolean; + error: Error | null; + refetch: ReturnType; +} = { data: undefined, isLoading: false, isError: false, error: null, refetch: vi.fn() }; + vi.mock("@/domain/vendors/use-cases/use-vendor-facets", () => ({ useVendorFacets: () => ({ data: { companies: [], trades: [] } }), })); @@ -36,6 +40,7 @@ vi.mock("@/domain/vendors/use-cases/use-save-vendor-company-roster", async () => }); import { useVendorRosterForm } from "@/app/(protected)/vendors/_components/use-vendor-roster-form"; +import type { VendorCompanyRosterFormValues } from "@/domain/vendors/schemas/vendor-roster-schema"; import type { VendorCompanyRoster, VendorFacetCompany } from "@/domain/vendors/types/vendor"; const company: VendorFacetCompany = { @@ -81,6 +86,72 @@ const secondRoster: VendorCompanyRoster = { email: "dispatch@metro.test", }; +const rosterWithTechnicians: VendorCompanyRoster = { + ...roster, + technicians: [ + { + id: 11, + contactName: "Ray Holt", + phone: "(314) 555-0122", + email: "", + preferredContact: "Phone", + tradeSpecialties: "Plumbing", + isActive: true, + totalJobs: 0, + }, + { + id: 12, + contactName: "Amy Santiago", + phone: "(314) 555-0133", + email: "", + preferredContact: "Text", + tradeSpecialties: "HVAC", + isActive: true, + totalJobs: 0, + }, + ], +}; + +const newTechnician = { + contactName: "Dana Kim", + phone: "(314) 555-0144", + email: "dana@test.test", + preferredContact: "Text" as const, + tradeSpecialties: "HVAC", + isActive: true, +}; + +function submitValues( + technicians: VendorCompanyRosterFormValues["technicians"], +): VendorCompanyRosterFormValues { + return { + name: "Gateway Plumbing", + companyPhone: "(314) 555-0100", + email: "dispatch@gateway.test", + address: "1 Main St", + city: "St. Louis", + state: "MO", + zip: "63101", + googleMapsUrl: "", + notes: "", + technicians, + }; +} + +function resetRosterQuery(): void { + rosterQueryResult = { + data: undefined, + isLoading: false, + isError: false, + error: null, + refetch: vi.fn(), + }; +} + +beforeEach(() => { + resetRosterQuery(); +}); + function createClient(): QueryClient { return new QueryClient({ defaultOptions: { queries: { retry: false } } }); } @@ -316,3 +387,220 @@ describe("useVendorRosterForm stale-selection handling", () => { expect(result.current.name.field.value).toBe("Draft Vendor"); }); }); + +describe("useVendorRosterForm additive add flow (SH-250)", () => { + beforeEach(() => { + rosterGet.mockReset(); + saveMutate.mockReset(); + }); + + it("issues an additive add — never update — when an existing company is selected", async () => { + rosterGet.mockResolvedValueOnce(roster); + const { result } = renderHook( + () => { + const form = useVendorRosterForm({ mode: "create", startWithTechnician: true }); + const technician = useController({ + control: form.control, + name: "technicians.0.contactName", + }); + return { form, technician }; + }, + { wrapper: makeWrapper(createClient()) }, + ); + + await act(async () => { + await result.current.form.selectCompany(company); + }); + await act(async () => { + result.current.technician.field.onChange("Dana Kim"); + }); + act(() => { + result.current.form.submit(submitValues([newTechnician])); + }); + + expect(saveMutate).toHaveBeenCalledTimes(1); + const input = saveMutate.mock.calls[0]?.[0] as { mode: string }; + expect(input.mode).toBe("add"); + expect(saveMutate).toHaveBeenCalledWith( + expect.objectContaining({ + companyId: 5, + rowVersion: "rv-1", + originalRoster: roster, + values: expect.objectContaining({ technicians: [newTechnician] }), + }), + expect.any(Object), + ); + }); + + it("still creates via POST when no existing company is matched", () => { + const { result } = renderHook( + () => useVendorRosterForm({ mode: "create", startWithTechnician: true }), + { wrapper: makeWrapper(createClient()) }, + ); + + act(() => { + result.current.submit(submitValues([newTechnician])); + }); + + expect(saveMutate).toHaveBeenCalledTimes(1); + expect(saveMutate.mock.calls[0]?.[0]).toEqual( + expect.objectContaining({ mode: "create", companyId: undefined, rowVersion: undefined }), + ); + }); + + it("blocks submit when the selected company has nothing to add", async () => { + rosterGet.mockResolvedValueOnce(roster); + const { result } = renderHook(() => useVendorRosterForm({ mode: "create" }), { + wrapper: makeWrapper(createClient()), + }); + + await act(async () => { + await result.current.selectCompany(company); + }); + act(() => { + result.current.submit(submitValues([])); + }); + + expect(saveMutate).not.toHaveBeenCalled(); + }); + + it("keeps the entered technician and retries with the fresh rowVersion after reload", async () => { + rosterGet.mockResolvedValueOnce(roster); + const { result } = renderHook( + () => { + const form = useVendorRosterForm({ mode: "create", startWithTechnician: true }); + const technician = useController({ + control: form.control, + name: "technicians.0.contactName", + }); + return { form, technician }; + }, + { wrapper: makeWrapper(createClient()) }, + ); + + await act(async () => { + await result.current.form.selectCompany(company); + }); + await act(async () => { + result.current.technician.field.onChange("Dana Kim"); + }); + act(() => { + result.current.form.submit(submitValues([newTechnician])); + }); + expect(saveMutate.mock.calls[0]?.[0]).toEqual(expect.objectContaining({ rowVersion: "rv-1" })); + + rosterGet.mockResolvedValueOnce({ ...roster, rowVersion: "rv-2" }); + await act(async () => { + result.current.form.reload(); + }); + await waitFor(() => expect(result.current.form.selectedCompanyId).toBe(5)); + + act(() => { + result.current.form.submit(submitValues([newTechnician])); + }); + expect(saveMutate.mock.calls[1]?.[0]).toEqual( + expect.objectContaining({ mode: "add", rowVersion: "rv-2" }), + ); + }); + + it("does not regress the Edit Vendor reconcile path (mode update)", () => { + rosterQueryResult = { + data: rosterWithTechnicians, + isLoading: false, + isError: false, + error: null, + refetch: vi.fn(), + }; + const { result } = renderHook(() => useVendorRosterForm({ mode: "update", vendorId: 7 }), { + wrapper: makeWrapper(createClient()), + }); + + act(() => { + result.current.submit(submitValues([...rosterWithTechnicians.technicians, newTechnician])); + }); + + expect(saveMutate).toHaveBeenCalledTimes(1); + expect(saveMutate.mock.calls[0]?.[0]).toEqual( + expect.objectContaining({ + mode: "update", + companyId: 5, + rowVersion: "rv-1", + originalRoster: rosterWithTechnicians, + values: expect.objectContaining({ + technicians: [...rosterWithTechnicians.technicians, newTechnician], + }), + }), + ); + }); +}); + +describe("useVendorRosterForm technician autopopulation (SH-246)", () => { + beforeEach(() => { + rosterGet.mockReset(); + saveMutate.mockReset(); + }); + + it("does not autopopulate technician fields when a company is selected", async () => { + rosterGet.mockResolvedValueOnce(rosterWithTechnicians); + const { result } = renderHook( + () => { + const form = useVendorRosterForm({ mode: "create" }); + const technicians = useController({ control: form.control, name: "technicians" }); + const name = useController({ control: form.control, name: "name" }); + return { form, technicians, name }; + }, + { wrapper: makeWrapper(createClient()) }, + ); + + await act(async () => { + await result.current.form.selectCompany(company); + }); + + const technicians = result.current.technicians.field.value; + expect(technicians).toEqual([ + { + contactName: "", + phone: "", + email: "", + preferredContact: "Phone", + tradeSpecialties: "", + isActive: true, + }, + ]); + expect(technicians).not.toContainEqual(expect.objectContaining({ contactName: "Ray Holt" })); + expect(technicians).not.toContainEqual( + expect.objectContaining({ contactName: "Amy Santiago" }), + ); + expect(result.current.name.field.value).toBe("Gateway Plumbing"); + expect(result.current.form.selectedCompanyId).toBe(5); + }); + + it("keeps a typed technician when the company selection is cleared to free text", async () => { + rosterGet.mockResolvedValueOnce(roster); + const { result } = renderHook( + () => { + const form = useVendorRosterForm({ mode: "create", startWithTechnician: true }); + const technician = useController({ + control: form.control, + name: "technicians.0.contactName", + }); + return { form, technician }; + }, + { wrapper: makeWrapper(createClient()) }, + ); + + await act(async () => { + await result.current.form.selectCompany(company); + }); + await act(async () => { + result.current.technician.field.onChange("Dana Kim"); + }); + + act(() => { + result.current.form.clearSelectedCompany("Draft Vendor"); + }); + + expect(result.current.form.selectedCompanyId).toBeNull(); + expect(result.current.technician.field.value).toBe("Dana Kim"); + }); +}); diff --git a/src/test/domain/vendors/api/vendor-company-roster-api.test.ts b/src/test/domain/vendors/api/vendor-company-roster-api.test.ts index 1bad51e4..32fb5aeb 100644 --- a/src/test/domain/vendors/api/vendor-company-roster-api.test.ts +++ b/src/test/domain/vendors/api/vendor-company-roster-api.test.ts @@ -4,11 +4,13 @@ import { API_PATHS } from "@/api/api-paths"; const apiGet = vi.fn(); const apiPost = vi.fn(); const apiPut = vi.fn(); +const apiPatch = vi.fn(); vi.mock("@/api/api", () => ({ apiGet: (...args: unknown[]) => apiGet(...args), apiPost: (...args: unknown[]) => apiPost(...args), apiPut: (...args: unknown[]) => apiPut(...args), + apiPatch: (...args: unknown[]) => apiPatch(...args), })); vi.mock("ky", () => ({ @@ -38,6 +40,7 @@ describe("vendorCompanyRosterApi", () => { apiGet.mockReset(); apiPost.mockReset(); apiPut.mockReset(); + apiPatch.mockReset(); }); it("fetches by vendorId or companyId with the correct query params", async () => { @@ -121,4 +124,72 @@ describe("vendorCompanyRosterApi", () => { await expect(vendorCompanyRosterApi.create({ name: "Solo Co" })).rejects.toBe(generic); }); + + it("patches the additive payload without a technician id and without PUT", async () => { + apiPatch.mockResolvedValueOnce({ data: { companyId: 5, name: "Solo Co" } }); + + await vendorCompanyRosterApi.addTechnicians(5, { + rowVersion: "rv-1", + addTechnicians: [ + { + id: 99, + contactName: "Adam", + phone: "3145550198", + email: "", + preferredContact: "Phone", + tradeSpecialties: "Plumbing", + isActive: true, + }, + ], + }); + + expect(apiPatch).toHaveBeenCalledWith( + API_PATHS.vendorCompanyRoster.byCompany(5), + expect.objectContaining({ + rowVersion: "rv-1", + addTechnicians: [expect.objectContaining({ contactName: "Adam", phone: "(314) 555-0198" })], + }), + ); + const body = apiPatch.mock.calls[0]?.[1] as Record; + expect(body).not.toHaveProperty("companyFields"); + expect((body.addTechnicians as Array>)[0]).not.toHaveProperty("id"); + expect(apiPut).not.toHaveBeenCalled(); + expect(apiPost).not.toHaveBeenCalled(); + }); + + it("includes companyFields on the additive patch when provided", async () => { + apiPatch.mockResolvedValueOnce({ data: { companyId: 5, name: "Solo Co" } }); + + await vendorCompanyRosterApi.addTechnicians(5, { + rowVersion: "rv-2", + addTechnicians: [ + { contactName: "Adam", phone: "", email: "", tradeSpecialties: "", isActive: true }, + ], + companyFields: { notes: "Updated notes" }, + }); + + expect(apiPatch).toHaveBeenCalledWith( + API_PATHS.vendorCompanyRoster.byCompany(5), + expect.objectContaining({ + rowVersion: "rv-2", + companyFields: { notes: "Updated notes" }, + }), + ); + }); + + it("throws a stale conflict on a 409 from the additive patch", async () => { + apiPatch.mockRejectedValueOnce(httpError(409, { message: "rowversion mismatch" })); + + await expect( + vendorCompanyRosterApi.addTechnicians(5, { + rowVersion: "rv-1", + addTechnicians: [ + { contactName: "Adam", phone: "", email: "", tradeSpecialties: "", isActive: true }, + ], + }), + ).rejects.toSatisfy((error: unknown) => { + if (!isVendorRosterConflictError(error)) return false; + return error.conflict.kind === "stale"; + }); + }); }); diff --git a/src/test/domain/vendors/use-cases/use-save-vendor-company-roster-additive.test.tsx b/src/test/domain/vendors/use-cases/use-save-vendor-company-roster-additive.test.tsx new file mode 100644 index 00000000..61c431e6 --- /dev/null +++ b/src/test/domain/vendors/use-cases/use-save-vendor-company-roster-additive.test.tsx @@ -0,0 +1,234 @@ +import { act, renderHook, waitFor } from "@testing-library/react"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import type { ReactNode } from "react"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +const rosterAdd = vi.fn(); +const rosterUpdate = vi.fn(); +const rosterCreate = vi.fn(); + +vi.mock("@/domain/vendors/api/vendor-company-roster-api", () => ({ + vendorCompanyRosterApi: { + addTechnicians: (...args: unknown[]) => rosterAdd(...args), + update: (...args: unknown[]) => rosterUpdate(...args), + create: (...args: unknown[]) => rosterCreate(...args), + }, +})); + +vi.mock("@/domain/vendors/api/vendors-api", () => ({ + vendorsApi: { update: vi.fn() }, +})); + +vi.mock("react-toastify", () => ({ toast: { success: vi.fn(), error: vi.fn() } })); + +import { + buildAdditiveRosterPatch, + useSaveVendorCompanyRoster, +} from "@/domain/vendors/use-cases/use-save-vendor-company-roster"; +import type { VendorCompanyRoster } from "@/domain/vendors/types/vendor"; +import type { VendorCompanyRosterFormValues } from "@/domain/vendors/schemas/vendor-roster-schema"; + +const roster: VendorCompanyRoster = { + companyId: 5, + rowVersion: "rv-1", + name: "Gateway Plumbing", + companyPhone: "(314) 555-0100", + email: "dispatch@gateway.test", + address: "1 Main St", + city: "St. Louis", + state: "MO", + zip: "63101", + googleMapsUrl: "", + notes: "Preferred vendor", + technicians: [ + { + id: 11, + contactName: "Ray Holt", + phone: "(314) 555-0122", + email: "", + preferredContact: "Phone", + tradeSpecialties: "Plumbing", + isActive: true, + totalJobs: 0, + }, + ], +}; + +const newTechnician = { + contactName: "Dana Kim", + phone: "(314) 555-0144", + email: "dana@test.test", + preferredContact: "Text" as const, + tradeSpecialties: "HVAC", + isActive: true, +}; + +function formValues( + overrides: Partial = {}, +): VendorCompanyRosterFormValues { + return { + name: roster.name, + companyPhone: roster.companyPhone, + email: roster.email, + address: roster.address, + city: roster.city, + state: roster.state, + zip: roster.zip, + googleMapsUrl: roster.googleMapsUrl, + notes: roster.notes, + technicians: [newTechnician], + ...overrides, + }; +} + +describe("buildAdditiveRosterPatch", () => { + it("carries only the newly entered technician, never a full roster snapshot", () => { + const patch = buildAdditiveRosterPatch(roster, formValues(), "rv-1"); + + expect(patch).not.toBeNull(); + expect(patch?.addTechnicians).toEqual([newTechnician]); + expect(patch?.addTechnicians[0]).not.toHaveProperty("id"); + }); + + it("ignores blank technician rows", () => { + const patch = buildAdditiveRosterPatch( + roster, + formValues({ + technicians: [ + { + contactName: "", + phone: "", + email: "", + preferredContact: "Phone", + tradeSpecialties: "", + isActive: true, + }, + newTechnician, + ], + }), + "rv-1", + ); + + expect(patch?.addTechnicians).toEqual([newTechnician]); + }); + + it("sends only changed company fields", () => { + const patch = buildAdditiveRosterPatch( + roster, + formValues({ notes: "New notes", address: "2 Oak Ave" }), + "rv-1", + ); + + expect(patch?.companyFields).toEqual({ notes: "New notes", address: "2 Oak Ave" }); + }); + + it("omits companyFields entirely when nothing changed", () => { + const patch = buildAdditiveRosterPatch(roster, formValues(), "rv-1"); + + expect(patch).not.toHaveProperty("companyFields"); + }); + + it("returns null when there is nothing to add or change", () => { + const patch = buildAdditiveRosterPatch( + roster, + formValues({ + technicians: [ + { + contactName: "", + phone: "", + email: "", + preferredContact: "Phone", + tradeSpecialties: "", + isActive: true, + }, + ], + }), + "rv-1", + ); + + expect(patch).toBeNull(); + }); +}); + +function createWrapper() { + const client = new QueryClient({ defaultOptions: { queries: { retry: false } } }); + return function Wrapper({ children }: { children: ReactNode }) { + return {children}; + }; +} + +describe("useSaveVendorCompanyRoster routing", () => { + beforeEach(() => { + rosterAdd.mockReset(); + rosterUpdate.mockReset(); + rosterCreate.mockReset(); + }); + + it("add mode issues the additive patch and never the reconcile PUT", async () => { + rosterAdd.mockResolvedValueOnce(roster); + const { result } = renderHook(() => useSaveVendorCompanyRoster(), { + wrapper: createWrapper(), + }); + + act(() => { + result.current.mutate({ + mode: "add", + values: formValues(), + companyId: 5, + rowVersion: "rv-1", + originalRoster: roster, + }); + }); + await waitFor(() => expect(result.current.isSuccess).toBe(true)); + + expect(rosterAdd).toHaveBeenCalledTimes(1); + expect(rosterAdd).toHaveBeenCalledWith( + 5, + expect.objectContaining({ + rowVersion: "rv-1", + addTechnicians: [newTechnician], + }), + ); + expect(rosterUpdate).not.toHaveBeenCalled(); + expect(rosterCreate).not.toHaveBeenCalled(); + }); + + it("update mode still issues the full reconcile PUT", async () => { + rosterUpdate.mockResolvedValueOnce(roster); + const { result } = renderHook(() => useSaveVendorCompanyRoster(), { + wrapper: createWrapper(), + }); + + act(() => { + result.current.mutate({ + mode: "update", + values: formValues({ + technicians: [{ id: 11, ...newTechnician }, newTechnician], + }), + companyId: 5, + rowVersion: "rv-1", + originalRoster: roster, + }); + }); + await waitFor(() => expect(result.current.isSuccess).toBe(true)); + + expect(rosterUpdate).toHaveBeenCalledWith(5, expect.anything(), "rv-1"); + expect(rosterAdd).not.toHaveBeenCalled(); + }); + + it("create mode still issues the POST", async () => { + rosterCreate.mockResolvedValueOnce(roster); + const { result } = renderHook(() => useSaveVendorCompanyRoster(), { + wrapper: createWrapper(), + }); + + act(() => { + result.current.mutate({ mode: "create", values: formValues() }); + }); + await waitFor(() => expect(result.current.isSuccess).toBe(true)); + + expect(rosterCreate).toHaveBeenCalledTimes(1); + expect(rosterAdd).not.toHaveBeenCalled(); + expect(rosterUpdate).not.toHaveBeenCalled(); + }); +}); From 4d1f02b87dd3e4e071bc0e299b8bfbce91eb30d7 Mon Sep 17 00:00:00 2001 From: Codex Review Integration Date: Tue, 18 Aug 2026 13:13:04 -0300 Subject: [PATCH 02/10] test(vendors): e2e asserts additive PATCH and no destructive PUT from add flow (SH-250, SH-246) --- e2e/vendors/vendors.spec.ts | 86 +++++++++++++++++++++++++++++-------- 1 file changed, 69 insertions(+), 17 deletions(-) diff --git a/e2e/vendors/vendors.spec.ts b/e2e/vendors/vendors.spec.ts index ad1a89e8..f5520d1d 100644 --- a/e2e/vendors/vendors.spec.ts +++ b/e2e/vendors/vendors.spec.ts @@ -65,6 +65,8 @@ interface MockState { listUrls: string[]; createdBody?: Record; updatedBody?: Record; + patchedBody?: Record; + patchedCompanyId?: string; deletedId?: string; } @@ -175,6 +177,52 @@ async function mockVendorApi( return; } + if (request.method() === "PATCH" && pathCompanyId) { + state.patchedBody = request.postDataJSON(); + state.patchedCompanyId = pathCompanyId; + const anchor = vendorRecords.find((vendor) => String(vendor.CompanyId) === pathCompanyId); + if (!anchor) { + await fulfillJson(route, { message: "Vendor roster not found" }, 404); + return; + } + const added = Array.isArray(state.patchedBody.addTechnicians) + ? (state.patchedBody.addTechnicians as Array>) + : []; + await fulfillJson(route, { + companyId: anchor.CompanyId, + rowVersion: "rv-patched", + name: anchor.CompanyName, + companyPhone: anchor.CompanyPhone, + email: anchor.Email, + address: anchor.Address, + city: anchor.City, + state: anchor.State, + zip: anchor.Zip, + googleMapsUrl: anchor.GoogleMapsUrl, + notes: anchor.Notes, + technicians: [ + ...vendorRecords + .filter((vendor) => vendor.CompanyId === anchor.CompanyId) + .map((vendor) => ({ + id: vendor.Id, + contactName: vendor.ContactName, + phone: vendor.Phone, + email: vendor.Email, + preferredContact: vendor.PreferredContact ?? "Phone", + tradeSpecialties: vendor.TradeSpecialties, + isActive: vendor.IsActive, + totalJobs: vendor.TotalJobs, + })), + ...added.map((technician, index) => ({ + id: 900 + index, + totalJobs: 0, + ...technician, + })), + ], + }); + return; + } + if (request.method() === "PUT" && pathCompanyId) { state.updatedBody = request.postDataJSON(); const technicians = Array.isArray(state.updatedBody.technicians) @@ -409,6 +457,11 @@ test.describe("Vendor directory prototype parity", () => { "https://maps.google.com/gateway", ); await expect(page.getByLabel("Preferred Contact")).toHaveCount(0); + await expect(page.getByLabel("Technician name (optional)")).toHaveCount(1); + await expect(page.getByLabel("Technician name (optional)")).toHaveValue(""); + await expect( + page.getByRole("dialog", { name: /Add Vendor/ }).getByText("Adam Whyte"), + ).toHaveCount(0); await page.getByRole("button", { name: "Add technician" }).click(); await page.getByLabel("Technician name (optional)").last().fill("New Technician"); const tradeInput = page.getByRole("combobox", { name: "Add Trade" }).last(); @@ -420,27 +473,26 @@ test.describe("Vendor directory prototype parity", () => { await page.getByLabel("Notes (optional)").fill("Created in browser E2E"); await page.getByRole("button", { name: "Add Vendor", exact: true }).last().click(); await expect(page.getByRole("dialog", { name: /Add Vendor/ })).toHaveCount(0); - expect(state.updatedBody).toMatchObject({ - name: "Gateway Plumbing", - companyPhone: "(314) 555-0100", - notes: "Created in browser E2E", - rowVersion: "rv-1", - }); - expect(state.updatedBody?.technicians).toEqual( - expect.arrayContaining([ - expect.objectContaining({ - contactName: "New Technician", - tradeSpecialties: "Plumbing, HVAC", - }), - ]), - ); - const submittedTechnicians = Array.isArray(state.updatedBody?.technicians) - ? (state.updatedBody.technicians as Array>) + expect(state.patchedCompanyId).toBe("101"); + expect(state.patchedBody).toMatchObject({ rowVersion: "rv-1" }); + expect(state.patchedBody?.companyFields).toEqual({ notes: "Created in browser E2E" }); + expect(state.patchedBody?.addTechnicians).toEqual([ + expect.objectContaining({ + contactName: "New Technician", + tradeSpecialties: "Plumbing, HVAC", + }), + ]); + const patchedTechnicians = Array.isArray(state.patchedBody?.addTechnicians) + ? (state.patchedBody.addTechnicians as Array>) : []; - const newTechnician = submittedTechnicians.find( + const newTechnician = patchedTechnicians.find( (technician) => technician.contactName === "New Technician", ); expect(newTechnician?.preferredContact).toBeUndefined(); + expect(patchedTechnicians.some((technician) => technician.contactName === "Adam Whyte")).toBe( + false, + ); + expect(state.updatedBody).toBeUndefined(); await page.getByRole("button", { name: "View vendor Gateway Plumbing" }).click(); const detailDrawer = page.locator(".MuiDrawer-paper").last(); From 9624c264de0346fcc55a6ca322f516259ff7de9c Mon Sep 17 00:00:00 2001 From: Codex Review Integration Date: Tue, 18 Aug 2026 14:42:01 -0300 Subject: [PATCH 03/10] fix(vendors): send only user-edited company fields on additive add (SH-250) Adversarial review found a last-writer-wins window: after a stale 409 the roster is refetched while the form keeps its pre-conflict values, so the form-vs-roster diff resent our stale value for any company field another user changed in that window, silently reverting their edit. Transmit only fields the user actually edited, taken from react-hook-form dirty state. Selecting a company calls reset(), so loaded values are never dirty and only genuine edits are sent. dirtyFields is resolved during render: formState is a Proxy that only tracks properties read at render time, so reading it inside the submit callback returned empty. The vendors E2E caught that; unit tests could not, since they pass the flags in directly. --- e2e/vendors/vendors.spec.ts | 7 ++++ .../_components/use-vendor-roster-form.ts | 34 ++++++++++++++++++- .../use-save-vendor-company-roster.ts | 23 ++++++++++++- ...ve-vendor-company-roster-additive.test.tsx | 31 ++++++++++++++++- 4 files changed, 92 insertions(+), 3 deletions(-) diff --git a/e2e/vendors/vendors.spec.ts b/e2e/vendors/vendors.spec.ts index f5520d1d..b348fd21 100644 --- a/e2e/vendors/vendors.spec.ts +++ b/e2e/vendors/vendors.spec.ts @@ -475,7 +475,14 @@ test.describe("Vendor directory prototype parity", () => { await expect(page.getByRole("dialog", { name: /Add Vendor/ })).toHaveCount(0); expect(state.patchedCompanyId).toBe("101"); expect(state.patchedBody).toMatchObject({ rowVersion: "rv-1" }); + // Only the field the user actually typed into is transmitted. Address/phone/email were + // populated by selecting the company and never edited, so they must not be sent — otherwise a + // concurrent edit by another user to any of them would be silently reverted on resubmit. expect(state.patchedBody?.companyFields).toEqual({ notes: "Created in browser E2E" }); + expect(state.patchedBody?.companyFields).not.toHaveProperty("companyPhone"); + expect(state.patchedBody?.companyFields).not.toHaveProperty("address"); + expect(state.patchedBody?.companyFields).not.toHaveProperty("email"); + expect(state.patchedBody?.companyFields).not.toHaveProperty("name"); expect(state.patchedBody?.addTechnicians).toEqual([ expect.objectContaining({ contactName: "New Technician", diff --git a/src/app/(protected)/vendors/_components/use-vendor-roster-form.ts b/src/app/(protected)/vendors/_components/use-vendor-roster-form.ts index 61f6f678..96f5e3b7 100644 --- a/src/app/(protected)/vendors/_components/use-vendor-roster-form.ts +++ b/src/app/(protected)/vendors/_components/use-vendor-roster-form.ts @@ -12,8 +12,35 @@ import { buildAdditiveRosterPatch, getSingleStatusOnlyChange, useSaveVendorCompanyRoster, + type EditedCompanyFields, } from "@/domain/vendors/use-cases/use-save-vendor-company-roster"; import { useVendorRosterResolver } from "./use-vendor-roster-resolver"; + +const EDITABLE_COMPANY_FIELDS = [ + "name", + "companyPhone", + "email", + "address", + "city", + "state", + "zip", + "googleMapsUrl", + "notes", +] as const; + +/** + * Company fields the user actually typed into, taken from react-hook-form dirty state. + * Selecting a company calls reset(), so loaded values are not dirty; only genuine edits are. + * The additive PATCH must send these rather than a form-vs-roster diff, which would resend + * stale values for fields another user changed while a conflict was being resolved. + */ +function pickEditedCompanyFields(dirtyFields: Record): EditedCompanyFields { + const edited: EditedCompanyFields = {}; + for (const field of EDITABLE_COMPANY_FIELDS) { + if (dirtyFields[field] === true) edited[field] = true; + } + return edited; +} import { toFormValues, useVendorRosterSelection } from "./use-vendor-roster-selection"; import { useVendorFacets } from "@/domain/vendors/use-cases/use-vendor-facets"; import { isVendorRosterConflictError } from "@/domain/vendors/lib/vendor-roster-conflict"; @@ -106,6 +133,10 @@ export function useVendorRosterForm({ mode: "onChange", }); const { control, handleSubmit, reset, formState } = form; + // react-hook-form exposes formState through a Proxy that only tracks properties read during + // render. Reading dirtyFields inside the submit callback would not subscribe and would come + // back empty, so it is resolved here on every render. + const editedCompanyFields = pickEditedCompanyFields(formState.dirtyFields); const watched = useWatch({ control }); const clearConflict = useCallback(() => setConflict(null), []); @@ -136,7 +167,7 @@ export function useVendorRosterForm({ const values = withoutBlankNewTechnicians(formValues); if (mode === "create" && selection.selectedRoster != null) { const selected = selection.selectedRoster; - if (!buildAdditiveRosterPatch(selected, values, selected.rowVersion)) { + if (!buildAdditiveRosterPatch(selected, values, selected.rowVersion, editedCompanyFields)) { toast.error("Enter at least one technician to add to this vendor."); return; } @@ -147,6 +178,7 @@ export function useVendorRosterForm({ companyId: selected.companyId, rowVersion: selected.rowVersion, originalRoster: selected, + editedCompanyFields, }, { onSuccess: (data) => onSuccess?.(data), diff --git a/src/domain/vendors/use-cases/use-save-vendor-company-roster.ts b/src/domain/vendors/use-cases/use-save-vendor-company-roster.ts index 1d1c4865..ca7fa247 100644 --- a/src/domain/vendors/use-cases/use-save-vendor-company-roster.ts +++ b/src/domain/vendors/use-cases/use-save-vendor-company-roster.ts @@ -16,8 +16,12 @@ export interface SaveVendorCompanyRosterInput { companyId?: string | number | null; rowVersion?: string; originalRoster?: VendorCompanyRoster; + /** Company fields the user actually edited. See buildAdditiveRosterPatch. */ + editedCompanyFields?: EditedCompanyFields; } +export type EditedCompanyFields = Partial>; + export interface SaveVendorCompanyRosterContext { conflict?: unknown; } @@ -92,10 +96,20 @@ function hasTechnicianContent(technician: { ); } +/** + * Builds the additive PATCH body for the Add flow. + * + * `editedCompanyFields` must carry the fields the user actually touched. A plain + * form-vs-roster diff is unsafe here: after a stale-409 the roster is refetched while the form + * keeps the values loaded before the conflict, so a field another user changed in that window + * would look "different" and be sent back with our stale value, silently reverting their edit. + * Only fields the user edited are ever transmitted. + */ export function buildAdditiveRosterPatch( roster: VendorCompanyRoster | null | undefined, values: VendorCompanyRosterFormValues, rowVersion: string, + editedCompanyFields: EditedCompanyFields = {}, ): VendorRosterAdditivePatchInput | null { const addTechnicians = values.technicians.filter( (technician) => technician.id == null && hasTechnicianContent(technician), @@ -103,6 +117,7 @@ export function buildAdditiveRosterPatch( const companyFields: Record = {}; if (roster) { for (const field of COMPANY_FIELDS) { + if (editedCompanyFields[field] !== true) continue; if (values[field] !== roster[field]) companyFields[field] = values[field]; } } @@ -134,6 +149,7 @@ export function useSaveVendorCompanyRoster(): UseMutationResult< companyId, rowVersion, originalRoster, + editedCompanyFields, }: SaveVendorCompanyRosterInput) => { if (mode === "update") { const statusChange = originalRoster @@ -162,7 +178,12 @@ export function useSaveVendorCompanyRoster(): UseMutationResult< throw new Error("Company id is required to add technicians"); } if (!rowVersion) throw new Error("Row version is required to add technicians"); - const patch = buildAdditiveRosterPatch(originalRoster, values, rowVersion); + const patch = buildAdditiveRosterPatch( + originalRoster, + values, + rowVersion, + editedCompanyFields, + ); if (!patch) throw new Error("Nothing to add to this vendor company"); return vendorCompanyRosterApi.addTechnicians(companyId, patch); } diff --git a/src/test/domain/vendors/use-cases/use-save-vendor-company-roster-additive.test.tsx b/src/test/domain/vendors/use-cases/use-save-vendor-company-roster-additive.test.tsx index 61c431e6..f03c2a27 100644 --- a/src/test/domain/vendors/use-cases/use-save-vendor-company-roster-additive.test.tsx +++ b/src/test/domain/vendors/use-cases/use-save-vendor-company-roster-additive.test.tsx @@ -112,16 +112,45 @@ describe("buildAdditiveRosterPatch", () => { expect(patch?.addTechnicians).toEqual([newTechnician]); }); - it("sends only changed company fields", () => { + it("sends only company fields the user edited", () => { const patch = buildAdditiveRosterPatch( roster, formValues({ notes: "New notes", address: "2 Oak Ave" }), "rv-1", + { notes: true, address: true }, ); expect(patch?.companyFields).toEqual({ notes: "New notes", address: "2 Oak Ave" }); }); + it("never sends a company field the user did not edit, even when it differs from the roster", () => { + // Regression: after a stale-409 the roster is refetched while the form keeps pre-conflict + // values. A form-vs-roster diff would resend our stale value for a field another user changed + // in that window, silently reverting their edit. Only edited fields may be transmitted. + const rosterAfterOtherUserEditedPhone = { ...roster, companyPhone: "314-555-9999" }; + + const patch = buildAdditiveRosterPatch( + rosterAfterOtherUserEditedPhone, + formValues({ companyPhone: roster.companyPhone, notes: "New notes" }), + "rv-2", + { notes: true }, + ); + + expect(patch?.companyFields).toEqual({ notes: "New notes" }); + expect(patch?.companyFields).not.toHaveProperty("companyPhone"); + }); + + it("omits companyFields when values differ but nothing was edited", () => { + const patch = buildAdditiveRosterPatch( + roster, + formValues({ notes: "Drifted under us" }), + "rv-1", + {}, + ); + + expect(patch).not.toHaveProperty("companyFields"); + }); + it("omits companyFields entirely when nothing changed", () => { const patch = buildAdditiveRosterPatch(roster, formValues(), "rv-1"); From 9a066092b3c619124e21c4f84d75b16f817c468f Mon Sep 17 00:00:00 2001 From: Codex Review Integration Date: Tue, 18 Aug 2026 17:28:48 -0300 Subject: [PATCH 04/10] fix(vendors): correct additive add reload, phone and contact (SH-250, SH-246) Three review findings on the additive add path: - Create-mode reload refreshed selectedRoster (including rowVersion) but left the form company fields on their pre-conflict values, so a retry diffed stale fields against the reloaded roster and could overwrite the concurrent company update that caused the 409. Reload now resets the form from the fresh roster, matching the select path. - mapVendorRosterAdditivePatchToBackend copied companyFields verbatim while the reconcile write mapper canonicalizes companyPhone, so PATCH and PUT could send different phone shapes for the same input. - emptyRosterTechnician seeded preferredContact "Phone" even though that control was removed from the form, so filling the first blank row submitted a fabricated value. append already omitted it; both paths now agree. --- .../use-vendor-roster-selection.ts | 7 +++- .../vendors/mappers/vendor-roster-mapper.ts | 10 +++++- .../vendors/schemas/vendor-roster-schema.ts | 5 ++- .../vendors/use-vendor-roster-form.test.tsx | 35 ++++++++++++++++++- 4 files changed, 53 insertions(+), 4 deletions(-) diff --git a/src/app/(protected)/vendors/_components/use-vendor-roster-selection.ts b/src/app/(protected)/vendors/_components/use-vendor-roster-selection.ts index 4eb141b1..4bd467f1 100644 --- a/src/app/(protected)/vendors/_components/use-vendor-roster-selection.ts +++ b/src/app/(protected)/vendors/_components/use-vendor-roster-selection.ts @@ -153,6 +153,11 @@ export function useVendorRosterSelection({ void fetchRosterByCompany(selectedRoster.companyId) .then((roster) => { if (requestIdRef.current !== requestId) return; + // SH-250: refresh the visible company fields alongside rowVersion. + // Refreshing only selectedRoster left the form on pre-conflict values, + // so a retry diffed stale fields against the reloaded roster and could + // overwrite the concurrent company update that caused the 409. + reset(withCompanyFieldsOnly(roster)); setSelectedRoster(roster); retryTargetRef.current = null; }) @@ -161,7 +166,7 @@ export function useVendorRosterSelection({ setLoadErrorMessage(RELOAD_ERROR_MESSAGE); }); } - }, [clearConflict, fetchRosterByCompany, mode, query, selectedRoster]); + }, [clearConflict, fetchRosterByCompany, mode, query, reset, selectedRoster]); const retryLoad = useCallback(() => { const target = retryTargetRef.current; diff --git a/src/domain/vendors/mappers/vendor-roster-mapper.ts b/src/domain/vendors/mappers/vendor-roster-mapper.ts index e4de67dd..641042ef 100644 --- a/src/domain/vendors/mappers/vendor-roster-mapper.ts +++ b/src/domain/vendors/mappers/vendor-roster-mapper.ts @@ -152,7 +152,15 @@ export function mapVendorRosterAdditivePatchToBackend( return mapped; }), }; - if (patch.companyFields) payload.companyFields = { ...patch.companyFields }; + if (patch.companyFields) { + // SH-250: match the reconcile write path, which canonicalizes companyPhone. + // Copying companyFields verbatim let additive patches send a different phone + // shape than PUT for the same user input. + const companyFields = { ...patch.companyFields }; + if (companyFields.companyPhone !== undefined) + companyFields.companyPhone = toCanonicalPhone(companyFields.companyPhone); + payload.companyFields = companyFields; + } return payload; } diff --git a/src/domain/vendors/schemas/vendor-roster-schema.ts b/src/domain/vendors/schemas/vendor-roster-schema.ts index a429d69e..9358b12b 100644 --- a/src/domain/vendors/schemas/vendor-roster-schema.ts +++ b/src/domain/vendors/schemas/vendor-roster-schema.ts @@ -83,11 +83,14 @@ export const vendorCompanyRosterUpdateSchema = vendorCompanyRosterSchema.and( export type VendorCompanyRosterUpdateValues = z.infer; +// SH-250: the preferred-contact control was removed from the form, so seeding a +// value here fabricated one on submit. `append` already omits it; the schema +// keeps it optional, so both paths now agree and the backend receives it only +// when a real value exists. export const emptyRosterTechnician: RosterTechnicianValues = { contactName: "", phone: "", email: "", - preferredContact: "Phone", tradeSpecialties: "", isActive: true, }; diff --git a/src/test/app/(protected)/vendors/use-vendor-roster-form.test.tsx b/src/test/app/(protected)/vendors/use-vendor-roster-form.test.tsx index 80091636..777363fd 100644 --- a/src/test/app/(protected)/vendors/use-vendor-roster-form.test.tsx +++ b/src/test/app/(protected)/vendors/use-vendor-roster-form.test.tsx @@ -503,6 +503,37 @@ describe("useVendorRosterForm additive add flow (SH-250)", () => { ); }); + it("refreshes the visible company fields on reload, not just rowVersion", async () => { + rosterGet.mockResolvedValueOnce(roster); + const { result } = renderHook( + () => { + const form = useVendorRosterForm({ mode: "create", startWithTechnician: true }); + const name = useController({ control: form.control, name: "name" }); + return { form, name }; + }, + { wrapper: makeWrapper(createClient()) }, + ); + + await act(async () => { + await result.current.form.selectCompany(company); + }); + expect(result.current.name.field.value).toBe("Gateway Plumbing"); + + // Someone else renamed the company, which is what caused the 409. + rosterGet.mockResolvedValueOnce({ + ...roster, + rowVersion: "rv-2", + name: "Gateway Plumbing & Drain", + }); + await act(async () => { + result.current.form.reload(); + }); + + // Reload must not leave the form on the pre-conflict name, or the retry + // would diff stale fields and overwrite the concurrent update. + await waitFor(() => expect(result.current.name.field.value).toBe("Gateway Plumbing & Drain")); + }); + it("does not regress the Edit Vendor reconcile path (mode update)", () => { rosterQueryResult = { data: rosterWithTechnicians, @@ -562,11 +593,13 @@ describe("useVendorRosterForm technician autopopulation (SH-246)", () => { contactName: "", phone: "", email: "", - preferredContact: "Phone", tradeSpecialties: "", isActive: true, }, ]); + // SH-250: the blank row must not carry a fabricated preferredContact, since + // the control was removed from the form and `append` omits it too. + expect(technicians[0]).not.toHaveProperty("preferredContact"); expect(technicians).not.toContainEqual(expect.objectContaining({ contactName: "Ray Holt" })); expect(technicians).not.toContainEqual( expect.objectContaining({ contactName: "Amy Santiago" }), From 1305bf35dd5a3e9aee02c4e5c7f117078e3993fb Mon Sep 17 00:00:00 2001 From: Codex Review Integration Date: Wed, 19 Aug 2026 10:26:28 -0300 Subject: [PATCH 05/10] fix(vendors): bypass the query cache when reloading after a conflict (SH-250) The reload fix re-seeded the form from queryClient.fetchQuery, which inherits the global 5-minute staleTime. A 409 never invalidates that key - useSaveVendorCompanyRoster invalidates only in onSuccess and its onError returns early for conflicts - and the Add flow has no mounted observer on it, so reload returned the cached roster with the same stale rowVersion and every retry 409'd again. Update mode was unaffected because it recovers via query.refetch(), which bypasses staleTime. Pass staleTime: 0 on that fetch, since its sole purpose is the freshest rowVersion. The regression test uses a client with the app's real staleTime; the default test client uses 0, which masked this entirely. --- .../use-vendor-roster-selection.ts | 7 +++ .../vendors/use-vendor-roster-form.test.tsx | 46 +++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/src/app/(protected)/vendors/_components/use-vendor-roster-selection.ts b/src/app/(protected)/vendors/_components/use-vendor-roster-selection.ts index 4bd467f1..24d51ce8 100644 --- a/src/app/(protected)/vendors/_components/use-vendor-roster-selection.ts +++ b/src/app/(protected)/vendors/_components/use-vendor-roster-selection.ts @@ -87,6 +87,13 @@ export function useVendorRosterSelection({ queryClient.fetchQuery({ queryKey: queryKeys.vendors.roster("companyId", id), queryFn: () => vendorCompanyRosterApi.get({ companyId: id }), + // SH-250: this fetch exists to obtain the freshest rowVersion, so it must not + // serve the global 5-minute staleTime cache. A conflict never invalidates this + // key (useSaveVendorCompanyRoster invalidates only on success, and its onError + // returns early for conflicts), and the Add flow has no mounted observer on it. + // Without staleTime: 0 the Reload button re-seeds the same stale rowVersion and + // the retry 409s again indefinitely. + staleTime: 0, }), [queryClient], ); diff --git a/src/test/app/(protected)/vendors/use-vendor-roster-form.test.tsx b/src/test/app/(protected)/vendors/use-vendor-roster-form.test.tsx index 777363fd..b56aa714 100644 --- a/src/test/app/(protected)/vendors/use-vendor-roster-form.test.tsx +++ b/src/test/app/(protected)/vendors/use-vendor-roster-form.test.tsx @@ -156,6 +156,17 @@ function createClient(): QueryClient { return new QueryClient({ defaultOptions: { queries: { retry: false } } }); } +/** + * SH-250: mirrors the app's real staleTime (src/lib/query/query-client.ts). The default + * test client uses staleTime 0, which silently masks cache-staleness defects on the + * reload path — the conflict recovery must not depend on an empty cache. + */ +function createCachingClient(): QueryClient { + return new QueryClient({ + defaultOptions: { queries: { retry: false, staleTime: 5 * 60 * 1000 } }, + }); +} + function makeWrapper(client: QueryClient) { return function Wrapper({ children }: { children: ReactNode }) { return {children}; @@ -534,6 +545,41 @@ describe("useVendorRosterForm additive add flow (SH-250)", () => { await waitFor(() => expect(result.current.name.field.value).toBe("Gateway Plumbing & Drain")); }); + it("reload bypasses the cache so the retry carries the fresh rowVersion", async () => { + rosterGet.mockResolvedValueOnce(roster); + const { result } = renderHook( + () => useVendorRosterForm({ mode: "create", startWithTechnician: true }), + { wrapper: makeWrapper(createCachingClient()) }, + ); + + await act(async () => { + await result.current.selectCompany(company); + }); + expect(rosterGet).toHaveBeenCalledTimes(1); + + act(() => { + result.current.submit(submitValues([newTechnician])); + }); + expect(saveMutate.mock.calls[0]?.[0]).toEqual(expect.objectContaining({ rowVersion: "rv-1" })); + + // The 409 never invalidates this key, and in the Add flow nothing else observes it. + // Without staleTime: 0 on the reload fetch, the cached rv-1 is served straight back + // and every retry 409s again. + rosterGet.mockResolvedValueOnce({ ...roster, rowVersion: "rv-2" }); + await act(async () => { + result.current.reload(); + }); + + await waitFor(() => expect(rosterGet).toHaveBeenCalledTimes(2)); + + act(() => { + result.current.submit(submitValues([newTechnician])); + }); + expect(saveMutate.mock.calls[1]?.[0]).toEqual( + expect.objectContaining({ mode: "add", rowVersion: "rv-2" }), + ); + }); + it("does not regress the Edit Vendor reconcile path (mode update)", () => { rosterQueryResult = { data: rosterWithTechnicians, From 90a5256deedcde41e6ba35710a9d937027377a29 Mon Sep 17 00:00:00 2001 From: Codex Review Integration Date: Wed, 19 Aug 2026 13:18:17 -0300 Subject: [PATCH 06/10] fix(vendors): gate deactivation prompt on saved status (SH-255) The drawer's Active toggle asked to deactivate whenever the switch moved from on to off, reading the unsaved form value. An inactive vendor toggled on and back off without saving therefore raised "Deactivate this vendor" for a vendor that was already inactive. Gate on the persisted technician status from the loaded roster instead, falling back to the clicked row. Deactivation is a property of the saved vendor, not of the toggle. --- .../_components/vendor-detail-drawer.tsx | 13 +++-- .../vendors/vendor-detail-drawer.test.tsx | 58 +++++++++++++++++++ 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/src/app/(protected)/vendors/_components/vendor-detail-drawer.tsx b/src/app/(protected)/vendors/_components/vendor-detail-drawer.tsx index 74d6ef02..fdb627be 100644 --- a/src/app/(protected)/vendors/_components/vendor-detail-drawer.tsx +++ b/src/app/(protected)/vendors/_components/vendor-detail-drawer.tsx @@ -254,10 +254,13 @@ function DrawerEditor({ const selectedIndex = technicians.findIndex( (technician) => technician.id != null && String(technician.id) === String(vendor.id), ); - const selectedTotalJobs = - roster?.technicians.find( - (technician) => technician.id != null && String(technician.id) === String(vendor.id), - )?.totalJobs ?? vendor.totalJobs; + const persistedTechnician = roster?.technicians.find( + (technician) => technician.id != null && String(technician.id) === String(vendor.id), + ); + const selectedTotalJobs = persistedTechnician?.totalJobs ?? vendor.totalJobs; + // Deactivation is a property of the saved vendor, not of the toggle: flipping an + // inactive vendor on and back off without saving must not ask to deactivate it. + const persistedIsActive = persistedTechnician?.isActive ?? vendor.isActive; if (form.isLoading) { return ; @@ -310,7 +313,7 @@ function DrawerEditor({ checked={Boolean(field.value)} slotProps={{ input: { "aria-label": "Active status" } }} onChange={(_event, checked) => { - if (field.value && !checked) onRequestDeactivation(vendor); + if (persistedIsActive && !checked) onRequestDeactivation(vendor); else field.onChange(checked); }} /> diff --git a/src/test/app/(protected)/vendors/vendor-detail-drawer.test.tsx b/src/test/app/(protected)/vendors/vendor-detail-drawer.test.tsx index df329a4a..3029e06c 100644 --- a/src/test/app/(protected)/vendors/vendor-detail-drawer.test.tsx +++ b/src/test/app/(protected)/vendors/vendor-detail-drawer.test.tsx @@ -1,4 +1,5 @@ import { screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; import { describe, expect, it, vi } from "vitest"; import { VendorDetailDrawer } from "@/app/(protected)/vendors/_components/vendor-detail-drawer"; import { renderWithProviders } from "@/test/test-utils"; @@ -134,3 +135,60 @@ describe("VendorDetailDrawer selected-technician display", () => { expect(screen.queryByRole("switch", { name: "Active status" })).not.toBeInTheDocument(); }); }); + +describe("VendorDetailDrawer deactivation prompt", () => { + const inactiveVendor: VendorListItem = { ...vendor, isActive: false }; + + it("does not prompt to deactivate a saved-inactive vendor toggled on and back off", async () => { + useVendorCompanyRoster.mockReturnValue( + rosterWith([ + { id: 1, contactName: "Adam", phone: "314-555-0198", isActive: false, totalJobs: 5 }, + ]), + ); + const onRequestDeactivation = vi.fn(); + + renderWithProviders( + , + { route: "/vendors", withAuth: false }, + ); + + const toggle = screen.getByRole("switch", { name: "Active status" }); + await userEvent.click(toggle); + expect(toggle).toBeChecked(); + + await userEvent.click(toggle); + + expect(toggle).not.toBeChecked(); + expect(onRequestDeactivation).not.toHaveBeenCalled(); + }); + + it("prompts to deactivate a saved-active vendor when the toggle is switched off", async () => { + useVendorCompanyRoster.mockReturnValue( + rosterWith([ + { id: 1, contactName: "Adam", phone: "314-555-0198", isActive: true, totalJobs: 5 }, + ]), + ); + const onRequestDeactivation = vi.fn(); + + renderWithProviders( + , + { route: "/vendors", withAuth: false }, + ); + + await userEvent.click(screen.getByRole("switch", { name: "Active status" })); + + expect(onRequestDeactivation).toHaveBeenCalledWith(vendor); + }); +}); From 038ce263da3ec14b0607ae0a3ae40963e427e9ed Mon Sep 17 00:00:00 2001 From: Codex Review Integration Date: Wed, 19 Aug 2026 13:22:11 -0300 Subject: [PATCH 07/10] fix(vendors): vendor detail parity with the design prototype (SH-253) Three divergences in the vendor detail drawer: - The header rendered the company as a subtitle under a title that had already fallen back to the company, printing the same name twice when the row carried no technician. The subtitle is now dropped when it would repeat the title. - The Google Maps link appeared only when a URL was stored, unlabelled, so an empty value left no trace of the field. It is now a labelled field below Address that reads em-dash when unset, as every other field in the panel does. - The status sat immediately beside Total Jobs. It now sits opposite it at the right edge, and reuses the vendors table's dot-plus-text badge so the state never reads by colour alone. --- .../_components/vendor-detail-drawer.tsx | 60 ++++++++------ .../_components/vendor-status-badge.tsx | 20 +++++ .../vendors/_components/vendors-table.tsx | 22 +----- .../vendors/vendor-detail-drawer.test.tsx | 79 +++++++++++++++++++ 4 files changed, 137 insertions(+), 44 deletions(-) create mode 100644 src/app/(protected)/vendors/_components/vendor-status-badge.tsx diff --git a/src/app/(protected)/vendors/_components/vendor-detail-drawer.tsx b/src/app/(protected)/vendors/_components/vendor-detail-drawer.tsx index 74d6ef02..0c490350 100644 --- a/src/app/(protected)/vendors/_components/vendor-detail-drawer.tsx +++ b/src/app/(protected)/vendors/_components/vendor-detail-drawer.tsx @@ -17,6 +17,7 @@ import { Switch, } from "@mui/material"; import { VendorRosterConflictAlert } from "./vendor-roster-conflict-alert"; +import { VendorStatusBadge } from "./vendor-status-badge"; import { VendorRosterFormFields } from "./vendor-roster-form-fields"; import { VendorRosterLoadErrorAlert } from "./vendor-roster-load-error"; import { useVendorRosterForm } from "./use-vendor-roster-form"; @@ -81,6 +82,13 @@ function DrawerHeader({ technician?: VendorRosterTechnician; onClose: () => void; }) { + const title = technician?.contactName || vendor?.contactName || roster?.name || "Vendor company"; + const company = roster?.name || vendor?.companyName || "Vendor details"; + // The company is a subtitle under the technician's name. When there is no + // technician the title already falls back to the company, and repeating it + // would render the same line twice. + const subtitle = company === title ? null : company; + return ( - {technician?.contactName || vendor?.contactName || roster?.name || "Vendor company"} - - - {roster?.name || vendor?.companyName || "Vendor details"} + {title} + {subtitle !== null && ( + + {subtitle} + + )} @@ -118,17 +128,24 @@ function CompanySection({ roster }: { roster: VendorCompanyRoster }) { - {Boolean(roster.googleMapsUrl) && ( - - - Open in Google Maps - - )} + + + Google Maps + + {roster.googleMapsUrl ? ( + + + Open in Google Maps + + ) : ( + — + )} + ); } @@ -154,23 +171,14 @@ function TechnicianSection({ technician }: { technician: VendorRosterTechnician )} - + {technician.totalJobs ?? 0} Total Jobs - - - Status - - - + diff --git a/src/app/(protected)/vendors/_components/vendor-status-badge.tsx b/src/app/(protected)/vendors/_components/vendor-status-badge.tsx new file mode 100644 index 00000000..7fee8970 --- /dev/null +++ b/src/app/(protected)/vendors/_components/vendor-status-badge.tsx @@ -0,0 +1,20 @@ +import { Box, Stack } from "@mui/material"; +import { Text } from "@/components/ui/text"; + +/** Dot plus text, so the status never reads by colour alone. */ +export function VendorStatusBadge({ isActive }: { isActive: boolean }) { + return ( + + + {isActive ? "Active" : "Inactive"} + + ); +} diff --git a/src/app/(protected)/vendors/_components/vendors-table.tsx b/src/app/(protected)/vendors/_components/vendors-table.tsx index db588cff..e524630c 100644 --- a/src/app/(protected)/vendors/_components/vendors-table.tsx +++ b/src/app/(protected)/vendors/_components/vendors-table.tsx @@ -18,6 +18,7 @@ import { TableRow, Tooltip, } from "@mui/material"; +import { VendorStatusBadge } from "./vendor-status-badge"; import { Text } from "@/components/ui/text"; import type { VendorListItem } from "@/domain/vendors/types/vendor"; @@ -50,23 +51,6 @@ function stopPropagation(event: MouseEvent): void { event.stopPropagation(); } -function VendorStatus({ isActive }: { isActive: boolean }) { - return ( - - - {isActive ? "Active" : "Inactive"} - - ); -} - interface VendorTableRowProps { row: VendorListItem; onOpenDetail: (row: VendorListItem) => void; @@ -177,7 +161,9 @@ function VendorTableRow({ row, onOpenDetail, onOpenEdit }: VendorTableRowProps) {row.totalJobs ?? 0} - + + + { expect(screen.queryByRole("switch", { name: "Active status" })).not.toBeInTheDocument(); }); }); + +describe("VendorDetailDrawer design parity", () => { + it("renders the company once when there is no technician name to head the panel", () => { + useVendorCompanyRoster.mockReturnValue(rosterWith([])); + + renderWithProviders( + , + { route: "/vendors", withAuth: false }, + ); + + const heading = screen.getByRole("heading", { level: 2, name: "Gateway Plumbing" }); + expect(heading).toBeInTheDocument(); + // The header block holds the title alone — no subtitle repeating the company. + expect(heading.parentElement?.children).toHaveLength(1); + }); + + it("keeps the company subtitle under a technician name", () => { + useVendorCompanyRoster.mockReturnValue( + rosterWith([{ id: 1, contactName: "Adam", phone: "", isActive: true, totalJobs: 5 }]), + ); + + renderWithProviders(, { + route: "/vendors", + withAuth: false, + }); + + expect(screen.getByRole("heading", { level: 2, name: "Adam" })).toBeInTheDocument(); + expect(screen.getAllByText("Gateway Plumbing").length).toBeGreaterThan(0); + }); + + it("labels the Google Maps field below Address even when no URL is stored", () => { + useVendorCompanyRoster.mockReturnValue( + rosterWith([{ id: 1, contactName: "Adam", phone: "", isActive: true, totalJobs: 5 }]), + ); + + renderWithProviders(, { + route: "/vendors", + withAuth: false, + }); + + expect(screen.getByText("Google Maps")).toBeInTheDocument(); + expect(screen.queryByRole("link", { name: /Open in Google Maps/ })).not.toBeInTheDocument(); + }); + + it("links out to Google Maps when a URL is stored", () => { + const roster = rosterWith([ + { id: 1, contactName: "Adam", phone: "", isActive: true, totalJobs: 5 }, + ]); + roster.data.googleMapsUrl = "https://maps.google.com/?q=1+Industrial+Pkwy"; + useVendorCompanyRoster.mockReturnValue(roster); + + renderWithProviders(, { + route: "/vendors", + withAuth: false, + }); + + expect(screen.getByRole("link", { name: /Open in Google Maps/ })).toHaveAttribute( + "href", + "https://maps.google.com/?q=1+Industrial+Pkwy", + ); + }); + + it("puts the status opposite Total Jobs and drops the redundant Status label", () => { + useVendorCompanyRoster.mockReturnValue( + rosterWith([{ id: 1, contactName: "Adam", phone: "", isActive: true, totalJobs: 5 }]), + ); + + renderWithProviders(, { + route: "/vendors", + withAuth: false, + }); + + const row = screen.getByText("Total Jobs").closest("div")?.parentElement; + expect(row).not.toBeNull(); + expect(getComputedStyle(row as Element).justifyContent).toBe("space-between"); + expect(row).toHaveTextContent("Active"); + expect(screen.queryByText("Status")).not.toBeInTheDocument(); + }); +}); From 22b03b8ac08f39fdace3a151d936b26688b6d5f8 Mon Sep 17 00:00:00 2001 From: Codex Review Integration Date: Wed, 19 Aug 2026 13:36:37 -0300 Subject: [PATCH 08/10] fix(vendors): deactivation dialog follows the approved design (SH-254) The dialog's wording, its actions and its behaviour all diverged from the design prototype. It refused deactivation outright when the vendor had open work orders, which SH-44 and SH-82 left as an open question rather than a decided requirement. Deactivation with open work orders now proceeds on explicit confirmation: the design's title and body copy, the affected work orders listed as links, and "Deactivate anyway" in place of a disabled button. The API call carries confirmOpenWorkOrders so the server-side guard is cleared deliberately rather than removed. --- .../_components/use-vendor-deactivation.ts | 17 ++++--- .../vendor-deactivation-dialog.tsx | 41 ++++++++------- src/domain/vendors/api/vendors-api.ts | 7 ++- .../vendors/use-cases/use-delete-vendor.ts | 10 +++- .../(protected)/vendors/vendors-list.test.tsx | 51 ++++++++++++++++--- 5 files changed, 92 insertions(+), 34 deletions(-) diff --git a/src/app/(protected)/vendors/_components/use-vendor-deactivation.ts b/src/app/(protected)/vendors/_components/use-vendor-deactivation.ts index b63291be..5870f7db 100644 --- a/src/app/(protected)/vendors/_components/use-vendor-deactivation.ts +++ b/src/app/(protected)/vendors/_components/use-vendor-deactivation.ts @@ -38,13 +38,18 @@ export function useVendorDeactivation(onSuccess?: () => void): VendorDeactivatio const confirm = () => { if (!target || target.id == null) return; setError(null); - deleteVendor.mutate(target.id, { - onSuccess: () => { - setTarget(null); - onSuccess?.(); + // The dialog has shown whatever open work orders exist, so confirming here is + // the explicit confirmation the API requires to deactivate past them (SH-254). + deleteVendor.mutate( + { id: target.id, confirmOpenWorkOrders: (impact?.openWorkOrders.length ?? 0) > 0 }, + { + onSuccess: () => { + setTarget(null); + onSuccess?.(); + }, + onError: (err: Error) => setError(err.message || "Failed to deactivate vendor"), }, - onError: (err: Error) => setError(err.message || "Failed to deactivate vendor"), - }); + ); }; return { diff --git a/src/app/(protected)/vendors/_components/vendor-deactivation-dialog.tsx b/src/app/(protected)/vendors/_components/vendor-deactivation-dialog.tsx index cf673b6c..a56ca3b5 100644 --- a/src/app/(protected)/vendors/_components/vendor-deactivation-dialog.tsx +++ b/src/app/(protected)/vendors/_components/vendor-deactivation-dialog.tsx @@ -8,11 +8,13 @@ import { DialogContent, DialogContentText, DialogTitle, + Link, List, ListItem, Stack, Typography, } from "@mui/material"; +import { Link as RouterLink } from "react-router"; import type { VendorDeactivationImpact, VendorListItem } from "@/domain/vendors/types/vendor"; interface VendorDeactivationDialogProps { @@ -26,6 +28,15 @@ interface VendorDeactivationDialogProps { onConfirm: () => void; } +function deactivationMessage(companyName: string | undefined, openCount: number): string { + const name = companyName ? `"${companyName}"` : "This vendor"; + if (openCount === 0) { + return `${name} will no longer be selectable for new work orders.`; + } + const plural = openCount === 1 ? "work order" : "work orders"; + return `${name} will no longer be selectable for new work orders. It still has ${openCount} open ${plural} — they'll keep it as-is unless you reassign them.`; +} + export function VendorDeactivationDialog({ target, isLoading, @@ -36,16 +47,16 @@ export function VendorDeactivationDialog({ onClose, onConfirm, }: VendorDeactivationDialogProps) { - const hasBlockingImpact = Boolean(impact && !impact.canDeactivate); + const openWorkOrders = impact?.openWorkOrders ?? []; + const hasOpenWorkOrders = openWorkOrders.length > 0; return ( - Deactivate Vendor + Deactivate this vendor? - Deactivate "{target?.companyName}"? Existing work-order and audit history will - be preserved. + {deactivationMessage(target?.companyName, openWorkOrders.length)} {isLoading && ( @@ -63,19 +74,13 @@ export function VendorDeactivationDialog({ )} - {impact != null && !isLoading && !impact.canDeactivate && ( - - This vendor cannot be deactivated because it still has open work orders. - - )} - - {impact != null && impact.openWorkOrders.length > 0 && ( + {hasOpenWorkOrders && ( - Open work orders ({impact.openWorkOrders.length}) + Open work orders ({openWorkOrders.length}) - {impact.openWorkOrders.map((wo) => ( + {openWorkOrders.map((wo) => ( - + {wo.workOrderNumber ? `${wo.workOrderNumber} — ${wo.workOrderTitle || "Untitled"}` : wo.workOrderTitle || `Work order ${wo.workOrderId}`} - + {Boolean(wo.status) && ( {wo.status} @@ -113,12 +118,12 @@ export function VendorDeactivationDialog({ Cancel diff --git a/src/domain/vendors/api/vendors-api.ts b/src/domain/vendors/api/vendors-api.ts index 467a3a3b..5afbc13e 100644 --- a/src/domain/vendors/api/vendors-api.ts +++ b/src/domain/vendors/api/vendors-api.ts @@ -101,8 +101,11 @@ export const vendorsApi = { return mapVendor(handleApiResponse(data)); }, - delete: async (id: string | number): Promise => { - await apiDelete(`${API_PATHS.rest.vendors}/${id}`); + // SH-254: confirmOpenWorkOrders tells the API the caller has been shown the + // vendor's open work orders and chose to proceed. Without it the API still blocks. + delete: async (id: string | number, confirmOpenWorkOrders = false): Promise => { + const suffix = confirmOpenWorkOrders ? "?confirmOpenWorkOrders=true" : ""; + await apiDelete(`${API_PATHS.rest.vendors}/${id}${suffix}`); }, getDeactivationImpact: async (id: string | number): Promise => { diff --git a/src/domain/vendors/use-cases/use-delete-vendor.ts b/src/domain/vendors/use-cases/use-delete-vendor.ts index 0457cc7e..17f8ae11 100644 --- a/src/domain/vendors/use-cases/use-delete-vendor.ts +++ b/src/domain/vendors/use-cases/use-delete-vendor.ts @@ -3,11 +3,17 @@ import { toast } from "react-toastify"; import { vendorsApi } from "@/domain/vendors/api/vendors-api"; import { queryKeys } from "@/infra/query-key/query-key"; -export function useDeleteVendor(): UseMutationResult { +export interface DeleteVendorInput { + id: string | number; + confirmOpenWorkOrders?: boolean; +} + +export function useDeleteVendor(): UseMutationResult { const queryClient = useQueryClient(); return useMutation({ - mutationFn: (id: string | number) => vendorsApi.delete(id), + mutationFn: ({ id, confirmOpenWorkOrders = false }: DeleteVendorInput) => + vendorsApi.delete(id, confirmOpenWorkOrders), onSuccess: () => { void queryClient.invalidateQueries({ queryKey: queryKeys.vendors.all }); toast.success("Vendor deactivated"); diff --git a/src/test/app/(protected)/vendors/vendors-list.test.tsx b/src/test/app/(protected)/vendors/vendors-list.test.tsx index cbfa47f9..ae5a18d3 100644 --- a/src/test/app/(protected)/vendors/vendors-list.test.tsx +++ b/src/test/app/(protected)/vendors/vendors-list.test.tsx @@ -177,7 +177,7 @@ describe("VendorsListPage", () => { expect(screen.getByRole("heading", { level: 2, name: "Adam Whyte" })).toBeInTheDocument(); }); - it("blocks deactivation when the preflight reports open work orders", async () => { + it("offers deactivate-anyway when the preflight reports open work orders", async () => { setupDefaults(); useVendorCompanyRoster.mockReturnValue({ data: activeRoster, @@ -213,13 +213,52 @@ describe("VendorsListPage", () => { await userEvent.click(screen.getByRole("switch", { name: "Active status" })); expect( - screen.getByText(/cannot be deactivated because it still has open work orders/), + screen.getByText( + /will no longer be selectable for new work orders\. It still has 1 open work order —/, + ), ).toBeInTheDocument(); - expect(screen.getByText(/Boiler repair/)).toBeInTheDocument(); - expect(screen.getByRole("button", { name: /^Deactivate$/ })).toBeDisabled(); - expect(mutate).not.toHaveBeenCalled(); + expect(screen.getByRole("link", { name: /Boiler repair/ })).toHaveAttribute( + "href", + "/workorders/101", + ); + + const confirm = screen.getByRole("button", { name: "Deactivate anyway" }); + expect(confirm).toBeEnabled(); + await userEvent.click(confirm); + + expect(mutate).toHaveBeenCalledWith({ id: 1, confirmOpenWorkOrders: true }, expect.anything()); }); + it("deactivates without the confirmation flag when nothing is linked", async () => { + setupDefaults(); + useVendorCompanyRoster.mockReturnValue({ + data: activeRoster, + isLoading: false, + isError: false, + error: null, + refetch: vi.fn(), + }); + useVendorDeactivationImpact.mockReturnValue({ + data: { vendorId: 1, canDeactivate: true, openWorkOrders: [] }, + isLoading: false, + error: null, + }); + useVendorsList.mockImplementation((params: { isActive?: boolean; pageSize?: number }) => { + if (params.pageSize === 1) return result([], 1); + return params.isActive ? result([activeVendor], 1) : result([], 0); + }); + + renderWithProviders(, { route: "/vendors", withAuth: false }); + + await userEvent.click(screen.getByRole("button", { name: "Edit vendor Gateway Plumbing" })); + await userEvent.click(screen.getByRole("switch", { name: "Active status" })); + + expect(screen.queryByText(/It still has/)).not.toBeInTheDocument(); + await userEvent.click(screen.getByRole("button", { name: /^Deactivate$/ })); + + expect(mutate).toHaveBeenCalledWith({ id: 1, confirmOpenWorkOrders: false }, expect.anything()); + }, 10_000); + it("preserves inline edits when deactivation is cancelled", async () => { setupDefaults(); useVendorCompanyRoster.mockReturnValue({ @@ -247,7 +286,7 @@ describe("VendorsListPage", () => { await userEvent.type(company, "Draft Company Name"); await userEvent.click(screen.getByRole("switch", { name: "Active status" })); await userEvent.click( - within(screen.getByRole("dialog", { name: "Deactivate Vendor" })).getByRole("button", { + within(screen.getByRole("dialog", { name: "Deactivate this vendor?" })).getByRole("button", { name: "Cancel", }), ); From 4e5863def5204bb3dffa9e63cefd2c5c8fb372fc Mon Sep 17 00:00:00 2001 From: Codex Review Integration Date: Wed, 19 Aug 2026 13:43:02 -0300 Subject: [PATCH 09/10] test(vendors): refresh the vendor detail visual baseline (SH-253) Regenerated in the CI Playwright image so the baseline matches the container's rendering. Only vendor-detail changed; the list and edit baselines are untouched, which confirms extracting the table's status badge left that view pixel-identical. --- .../vendors.visual.spec.ts/vendor-detail.png | Bin 118789 -> 119542 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/e2e/__screenshots__/vendors/vendors.visual.spec.ts/vendor-detail.png b/e2e/__screenshots__/vendors/vendors.visual.spec.ts/vendor-detail.png index 1beb0a150691ff7429d430df8450af305b139058..1993c2c7ffe5dd1a417d7e0e4a4e067e96664076 100644 GIT binary patch delta 70250 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 delta 69491 zcmb6ARajiX(gq5XKyVB0?oJ@MBm{SNcXzji6Fj(kaCdiy00Dx#ySvVrWPf{~bMs&P zeX*XI>3LRnbys&)z4g{z0ZecXOm%B7(9Kc$jIy!Mn>i>^#5k6;+p+bL*8z=JHY11v z71l&a6s90yn*XgJK`7Vx3+}2(()$8P8k1xhp<%}@E{Bta%e8MwjAL<>ncjFsyr67~ zAqPG*Y6lWo-)uq9&CSi(A$!I1QIt-bYtc$VBppGe-NWlS5vj!Vrv1v}Oxw&1Fij)L zVT}K@JR_Ie8zZ=Ox2~q!a!u`)lXiQ1i^BhqUQ<&eM(pM7=JDu~-F24@dQ$uL?VGM{ z*79k|N*5?>h&a<*JXgNBv(tb78xoR?i;GLw+ijI-NB*~Ocgvk`w=Z`48VpHZXC3Er zT3sP%=+5;hZ+q`B8r(Z{oe{&rfH23F_Wb;7DZQ88dD}-zHMRRHUmwso=elvzvYyxJ zZ77j9gb8OyhfkNs1*!9lBX{sShzS8ydx-xuQ_5KJK>v+MwOLN~mxnu0gst6@^Avqm zCl+D9f_gO<-a7`}3}RSld3L`>1T5w^iu96i zePQ7{$InlP%x7(K`KtMTkgJluzC?8F>EoIegQoaLCr7`=#zYRGu?`OuT5F*wX(=fw zi%n%^k?EvmWWMgw0ITLUHs}!p#6I;VFH@)yyu7?qtVR%ibtq5-5Z+Hv`#(8r)z9Rhql$_rt{{bQu?Al84qo=Jhg-HUr~P^95i;v0F$n)Q_9Zx)E8M2=$!J8#{@qy4!2?Ou8y{6+>Zn@m$S6ERx z{KI=lX&Q_U0M+>ZGuN7(BNBAYW563ls1WH*yvWTj??LzjI^}mN&k6fOSNDVu=3~C} z5#uOplU%EBmsOPI6>s_~G7+mWc8u~1;41<*zF9d<|KuywshwlDQ#7r%jC&#}cs zMSr`xy3WnTG>Me?JZy?_b92YXM{Luq*HQCW6!&ccT+YQDoYX~CRm`M4?cR^IFE8z# zYn|IC5pmJu>70TO8{Z;ou|?YSGXhS^%lkr)^PQseo>gj^3@^6^*yetXj-H&INrv!Cqu2JGH++;(TOyHwjLe@JVkI&sWVSP3RK z9vdJ#5LWZZoiNP(D4~KCChYX`=FqsAx>gEbW^tQl{n_6j!Q=4*oos26;q_L#C&fDm z2)~x+Q5iko+u3>Fi$3Ck!9mT6xx50YuFI&Z{44y*uCtM%x2JesAbh)7ouTFQxM`xM zr@y}+#`hYP2;Hb|5vG6fmM5&eY`Oy1YyO&oTeP`oNM4PRkE z=`h68mi08o8cnv0@Ky{-Z-<|^xBKE#-nNpv<}4asRgZ7y0U}BU9Ego_45!Hs!668) zeRr`}%iYwB7YSHZHH~{XWsX4({TuA0W5jK^p@rh*WkVB20qGJotW*<82IHE@6X5*n zE@;>~oLPOUY1 zrVw#e-E5=|-23Rew52;zX67l19cD&xd4ieuJk(-ldkaOq>rLm|Je~HhN5U(MMh52b zNp+2G$hAL7KlAnpi*=GdWb&V8LITKwl^m32RQM&ARY<;S*#$F(MsQ0BX|dKhP{{=a z4Fwf3D%5F6bl)6B`B+z$_thxV3(knfXfhK_zY7!c8J*&7+?Iq{#u-`rWb&&*=WqYA z7@>M<{-$B-O~KvNP*76hC9K+PtFl!;|EK6t{5@Dxso5_>8EQYW)kq#gq5vx~nT`4^ z3(L#+*x1;*nZ{@_L+FT(o*lN)r!S9_F-&@$*0)D0K0ZFCbsmO)s4crHDi|3VZARCc_u!s=$jRB-|5EAFk%4*C`RVy{dNur?WlMME^6xD z`Ra&*y$MZ`&dCo?RO6LKoHyC|2Z$Sb_DVuw{=c2+mKy( zJW4W*+wOqcqR-mlj}&69AOp;h>-|ss;n*qfF@nUG;m4A@_KCl%T z8A*Exou^E;Zp^^azw zr$?AZf|zi;=p%#AL=Ks~qNST{Uni&Xx5n1U2=q2`sOx6`hSz>eas=;LMZ z_s^*%VNSZgJ%^=bibhs|`w(4E9v+cQ8|qC+o@=e~D`))tBOt~68+D>5w^hOL&~fGi z9s$Br<0-NA!4+0owC*Ylp_bk@L(0i6k(?vCKYS!#*A&tpQn&kCKSzDHLBud$%Mx$Gr0KA~x5)v%b#mbT=%B+#tAJ9!q%2**FxUj&cE2CXLXJEnr zjg{kwn8Gjtt=uRD7k2cQ_HUx@dT0;hX35Ao<7TQ>Q{d$$wKe8P*`f2I`54LZ)W)f- zZb`wmu@O09_?L&Y(yX+6%)tEju4wOIB)Q0r!=VUHZODyJ6r+qQd9w_-B&Mc^t0*W$ zBw&+?MVNnP^lDj+9^cn&$Dm{G$UASE!tr={yCMZ}m>7#SHf!KfHH=WT6ijZ(#y>ru z1aQm?BLHu*gOi`0*V5VLQb#9q@l!9&(v*C^kZgdZgOD8s`#e~@NbD64i>q!15D+X# ze?_ZTpq|<66Ry?QDT3753APuE5H+I_36`?|g^sw7yBUqwljEcOW0-PjyM3}h&Kgp~ zjL@d0-#Juxkt&R!k1r2Lh+?(*6O){)W}O<4=bT?^DWBFt#;F>R$w+nkjxZZJ(5>Om z9~3l;0eq(@x?dXCmqhqjvO?k+X0I>q1U+@OP1!#i>L$yCO5?_zV+KBn*T_nz+vTui z#K%!&DztXW3wA{e|GjCzZ9>S*7#TSWTbl`cvai|MUUsX`NJ6+aY7DKjdOGs$8E~v& z(qg#L@F`}|itvIarMZhL%piLgQ==?JF!%p9M^!Yt!ze325uJ&c@ zA0xFY2gXG6gNmA@^7E(Hutr&5mz}iQ^iGj~H8u`0NV&sWgL3&*Uc-Ciw%92>(0F~H zf-dMBCdfg$10a>8hDy1bBG!v_S|ivxkKwgRE(~U+Am!{k7VRQ=d*w5$J%cT0{Pfj=X`j z@1r7H1s);V+@+!vMdr1u(P<$)(Olb6+(VH+P^ZGJ6TN;%;UYRa(_)2*Iytx=Cf6>>Ns)cfT58YjK5Xp@0<(>P3&2MBkZw4Qi42b;n6@}{4Z))`r=fMG) znDcV2l?0msni{rFYZ5k zoKdxBgB(3LD5}W7M8l9F&RKn-2DDdK%L~VikIO8Kz;oSoD810(#!m1GW0d0f2sUd6)j}X@sOJVxakd(5eSC7#D#Mb8^7;=OX4~^~n=O4R36&NaR@**54r6MIGq5 z9^$?D(P8%dPEJ%q*V0`R(&^s5l@rV|7NDujgaSLNk*RyJOGBxhU9SUF`2 zKQzrS{`@Hs<{MgrlBG5&XNt&IqCF@=7NDPsxA}@X7@eX^3O_VC{HJ#x(fyy#c&MqV zN$mOXF<8i7_Mt#=cd5#IoB$)t?`#KgDC4!x=VnY@e z(b+EvdB#uzzGwFPnQp_zpF#{eL4hDC8Hq3~yr`P>r#a4=VRnEmX%hDJ^leBG)L=9u zTcK*yghEY8x%QS%e0bX$8yh>X3*_e+uG{E!HPk!rTF=TO8*phD_7g*6B0*uuXc85*NbYp1YVmmL?V83g0|~Je8q9_ z@Sf%cv$GX#Yi`U;Ox{k;+V<21@@?8P}J06upu`)@DFUEsK?YRc8r zR8&dn`m!vyEX(W6~q}*vEo^u~&%PXL-HkR6oz{n_6w9y~8JN_FF1dlRBiT4z>jM$E60iof;Vl zX%7#pQE9x7p~xrSv`!rJ+e&+aE78&wnO73xgB(+*fL#7_RYIcZ$OO`zMSQjL5#=ue zi2siI1krl)>2U;>i5QG&_yAh=Bz3GfQ>XjP#YOcxaY}Kc)x0e?<>ZKlb57|ISH+@r zBUC&s2K?>KO&&T_q9iWkdDC6`xM`9}g)05h56OZd=IkkO6u9cXjYQMSv& ze6obSF9;I3K#_a9fxRN~kAW=D2CP94m4S!i#w#OIVoBPqMSLah;bLb+sEn5PZ8%q& zTT{Zks;xy%72_vNOdT4F1>G@|^0ARQdB8FaC%ZTBM`049gOB5Z{cwHfub^Ip!U_X5 zt((>_b|SaB^bOwEwI@(;%gZ;HjYp@WwNeO_tIz3eaM!`}`zaJ_cUMOpT9zgFl8S|E zP|<{$RK|`pCU>Gcb+gxvquzgvS`P;m16etvG2kWYPeb@ldE!bp({5rucncYhV; z3^e=p3KPAq33Sa8UjX}U>oXQEkb!{%1N5EOr*^rHWiC0nxeoVdjuooBEG+idYPL(w z4t)-zMcZ7JO-)VJ)%JP9RPlg`iAiIVTaKR*(=4B@oVRy|tXsVshomdi*Ds5bLhs8F z;JO!WiQY%ou$C7UQ9}=5!Vg4Znrr$G^xxdx70N!Tr}FYJnyHvR4(%e*KDCpxdBaNE zfvShAT}5hH39|Z`bW@jZRn^))|41ugJ)|^zKQl0p)_ZLWE7l8>@u?WN#gK(ku7Rt; zi)HylMw$0+=s;Y|?n_6pxkDNnKY_Ao&;FkHX?11i4^$tG$|E){@`YB$kn6l% zR}#{@qYLMh(nmGdxE~z6y2@C{y%|l^HI1jo6fhEMC`O*9asuJ{r=TP*1hlmDh+}`W z9_H~Kw1cB~aGMbD{{rN+w0zh_Fs;MQypU^gwYENC20U(#^*~DrXPDrq4~(LPML-7% z(9(t>IDnCeRnKEavRGIXq^_6UDtfxR#H31$cw>#l(lO>X#90D7yw>~udqxe#@3kR| zqv>gB>T{e^?v|F4&PI7s!?6)`RMYF;@1J&E8ZAGSC`%A40YvEN`9CdULjAoV{p!~F zGUV=WoV`}g{?ykm(&eB8@9gYko!KS?N)s1x!o>Ya^uVoBJFoc7h|9d`L;fyHURqj% zG)|lX?6PUY@*Ym0kP37|*^f*m(l{*TL?AY9s#_gUM&xlM@b=3 zsiEY0q5m14%eMV8BrFXKXcl)YEwv)y&rPc80&dM~tbBY9iHarC5^&j8&8t}>o59Pc z6T`zP-pW%SZ`f47sd=OrO`|5Py5op)kLi0ubZhK7Lq_ZWu(GsH7%>I|h(jew$;pCT zT;DARhtm5E(Rekkn&Uprq-)`oQoP3!c&0YSgPYsKH~0`wF&!G+IsLKX;iJregAiYeSOJ~^<8*ubn9unTwcET4=@TmY6O!D z8*uT2ZBZ%9|Rhi4~Jx;(eVNDB3ni^ z;@I#RvXU%@(W6&cn-Ex6h$@NqEfh7lh4%MI0de8(c5Y6} zs5X=H>_*OMOBcv!dk~4Gj|S`v9o}tx$#&!VTAv$}IyZ-|N+nE|sT!J@ckw0hx3T(K3j9{Pf9vO07ouDBeAm?VIA4?1T;#S&XvLC4V4$I(AR)m@2kHf|J1Yx zFPLNx3}{AYX6dsWaJw#LEz%^GD&b~3@(FeK{dE*4#b{9BBUv(nRORChv;EBa2jVP& z4k1TH4f$r;th|zzQ%aV*k5u2oEDChFyJ99E$9Rv_=nX!h|CKI7Z*Xvs%BEkKIS(Lq z-|#)D*I;`gw`OUp0 zP^on9`thkk6{Fd#tojKO+GCE6#RvjxZS<)O!lrL5G9!4+4-*h+J>=4G56b zhho^7qmJT#$A7@cnED&(O*me6(ODQ71r8WWNJt!>ok>bdkB*J?{r%5>+QiRC6t1}V z__nq-t+aHwSQ=VdILHmQt*dJ4awn;;lkLko zPHF*{eOe*cYGoFy)r$$x{8i%#-Q1nKp}B*vVHxy<^k-lj!-7+`KC%!^~uI9 zpZabNBzHP2&rc1GfqZto)?bmKA=F{@k&z(^(Sm9eIK;yx2tqkw|Gjl?b>@1T_Y9nd z^JC>`Y-!o8Y&-u-xv)4y4BHOs30?5M@a))xe)R|ICL%=jmoF&D5Yen09DCOv3KUtP z|Gk0*h}?f%2fcwEV^=GKukTu$9Ze)$T*i%J_%B1(K^&U?2JQY|zEsW2tc~qoBOxLe z7XIw-4-y-7cDtvbNa^3o*Q*iAmDkIrQ?2e+M&J@9hnS z82ZIGdHEIWKz*X*ew+k+*_kHPYpUYnMl#6B_1>3n{q^ftdgn{Q)YQ~DXan^2w!(%3q5st%tSk8eJwL>u;%)zlMez9qha*** zP51U3=qgsG%l5o-s0Uu}me2XXv878O*?e8(R#a4wYi}^j@!F@9`(Lc!rl}}fD3};%Kkce& zcifjd`||nt_?X!9g44*zC`RD!;q2<_SO7Q`1l?8vAt52{?R-tkN-Y2gJw}k{@rOLe zWtrY8@v7+N@VH*fVbG9L3#eO$)5qerROLXm^SOgKp+c7s->MsYxKY7}I}(8zY7F7$ zgLMWg!e2o*>d5|oS{+`t7n==UpU;7djfv0}5^Pwg6IWjFj2YxvLdhVup}uy* z0q$b|*&K3uw)~0?pDRr;y!u?x^v2n-CrmgMJMsxOfJ8*B>4WZf+oAq`>675bDYxqX zIN_~eS z({^~=Jt#zie#L<$Y+J_=IIx>=)p-mw6n8i;pl#3MF=WrlzV=F7Gr>7?r-~q-`Xi^T2 zPkARgX;Qwl30c z9w?(4%i>XQ!>5A(Wek@}G1V8pHa3EDNcFTcmeOt;UuMgIs*bm1F{0kRumQubhoXv#XyCzzb6{W~wg>f_ zDgQ1;_esB@gQ)1>jbnjgSY~@lN(!z2drLuPW;>>5+r`#fr)1l~J(N)muo3BX3-Ajx z62+0uEf_iAh@e$APe*AM!?A6LCsnZMop;hLN+<7ICO9@_l@tj^&?rjB1Xu!oV!(%I zsQbp7i9uUWKv2!%Y&R%V)Dl*OZK7$4NKwrb)N*SGVG>sr>AUkDpeb0=S1tzI_ zFR&R+*?V{gBWuAP7j581q7&Q_su3po?IX@$zSYmPlW7&8^GgULe(>-)W_@&l`(o|V}jc;dnt6;}{ymt4pU);#Uq$w&&P6f7@ z%2eCaKmu(<(=(J?wp(0%=YPHkgiQ?9;Z+uBxgj}XUlxHcZlI>r~)5NXen?a$_kaJte#LAzDERwS!VVO z-41`Ed{J%!3Qf9TwWyVhRBi_Nc}mQ=-I{A$hrCpjg$x`N&}8)gvzC9KN*VT;m?ffe z4oUv+dgTVJDC5J6P!!-$ELaEQLZ3VMIE)4zUYrARevL`S5zL0Z9MXC^=JotSGoso* zD_&V%;V@4;UJN(6{u;mf`F5~HheJB`lR^QLa8=gGT|l9$z)_7nNvoACD?t)2a+?(b zi^`wS^|_xe^J+nw1;bwb^c!L!%aOF_BI|jTpWQGIM&~F-a&h*{+;)mVOpJWLCB^n^ zhz1cl5Um|C#*Wv(#qwvLDzc{aQb9+^-uUO_5nOoUr379BR5lldU2~>++Hi}tu5<;D zBV!8g`6Xg-@(-RXzX%HW%(%UdZ+-q3`|;nEi!pClV>W9hv9Wxob&#u=F)A72N6dF) z<+?Ag%*VL=@#ds*#_+h9ZkUK1V0yI-C3EM50X>!>-=!dLWEg7Es_DzFI|3so#0Bpl zO>8?w#i`y4tC#`vM4qRxj~Cq98PcJZQH_FcX~I>mnF^$X-MB0>%f#1_h>x&|XTviX zX9q_PEbY=2xs>RXx-jI!(E?ooWx)4b=dgo7{tZG$1Nh7Uo=t3i@V;&kIBs=$Q52{F zu@ywp1?lb5Wk9p-^0Lb&qlMw&AlY^-5jIqToViwo4lLh5{z6)J!7KE zwKrX*pDZ$;AC3K#2Z}6t@RqD;DNQ9y6%ja zwn;VJN=Q7ldAaCgf?{!l^-iF()^bxfOMglhO z$qg@%Rxf+A1ELxC>SOC=7A~8HKw5vwV2-+u`^eQ6)X0OCT-U*<)U*p41J_EYO_|$e zA|>~Y`*Z8C;w4l8eJ2fet3uXCw)R(}4wuj8M0>xE)$BBX!X}5bS7a+E+{Zr7m(vo0 zrZKC}mhTM^u)AEx_u9<~=W464_@4_uA=z22-boZcq;1_O>3v7{+qST>0ytBp9l2Fn z0Kiev5p18Im4$6Nb-_U;IN2P}4fQ;|kSC*tDMR-KZeWRiy66vCln8!)wsna~auE^j zA8gy!QhUq5APV2hrAbBsS2v%{L7A!Xpg0*gbk0nzMp|shjSTc^weurGna6pZxN@vu zxxl8^s>ZxgoHo*EL&8}gF-hNWGYUIZq4?v8;LDs>)U%Qcz7pl8-hOL2lMtB^VdmTy z!_uJNMYio6iNuGG8vx9wPwH86>r`0%vn#`# zQh}aS7e`J^d(0{Pe34IniIG&!;S%_7oXU=yJs~bEh7ys-y@Uj@fW? zN36MTkGmYt>9jtgozyQ{b9|IIs@FVeZt^;w?@Y?hcFW)ad)I-1f&Tg|Q`S`9m0lpy z$9jH|JO5&2tN>>*jHpMBZ+SAwB_zUdAV}V~=0jfK!nU3ObAEh2!4iazl9J}qQdqQs z9BsUQ6G^8W&ML92OD5kksvn;0w3}HyZ3*n%>b+(?9>>r=ShWT6U!FpRwDJYflCNX% zH&AUm&5Rq`fz4s$JsPdzFCMk!H&;7U$m;+EDEG+jq4-F20X;MsP5)?|Mx^Sg%0oUj z>+|C#lp9`vpxo~ri9VgB!8h;g%MtDJrJ1m1O^w$NiJYE>QFb_BO~ubqvJ|M zpeBRmND6g6lX6)fnqs!#@?3v^+8~M;Ku<2f=SL z2U^$@mustP0zX|+r$OToz?|2qwKr|+ckd4*a36nijtw$mnoJgF_-K?xBAL=(ocLSb z=-A)&hVQ#!G864sf&oN^--d08M+eGv%loZ_@+e&R_t$ws*$riBVK0A(jaR+d?a|+p z3Q_U$9Akc^rLpc06>uioE;;xk@+j=`H~}E*OzkM`=F5!4VO3~Z_!%gfjs9r@b~NHG zuGng>S}O(M6wZI_CYXEPAx6)mJVI+~2hpx)P&mxqx^TqoBpvF9VAzXBMMa%5F;@ww zqZ1Q19d5~>@AA5yNq>!t4-F+e?*P4ZcW>m0EMnWPZhcCsTFXdCV0UnDaB)F-*~`ra zy%0t5tmrhfwwkVY`VgVSrzD7n1HuQYziq~M^+mh`B| zCU$bf3MLLBL|#=U|nYRnzHxOhe@8v>UhA;VUu1aryb@g>89uT1fGq*utP|Gaahl!b)Q;_+*0Q9mi$PB_x zL!jQ#|F#ztfl7+O!a z>lkoPO{Aoxsc_#6#?B9>i7~gd?L6PV4;S7M1U**$y(4I}-OtV^^X-jK(`wz;J+NmT z+W2DxFDWFA=Iokt%E}N3iQujF+*t&>&XbXWP@~hd&);01`R~{E9b5Q_CZCDDQ7gd6 z(d+X7wqCfZ?{-wvd;g8ID}K<^Jwi(hH+M@!mitwSPMh1c6jv7%VDkHkj3t3Fw3kQb7*}U!S#CfU z0s@+v9m1RHw}ri|klJG>be*R}E3{E-8cIFFWmzA&YOh)<&**&vRoS&-LVJGY)2feu zIlWr4Vf2Imlk4SHo^sgteL|#LnB`OHMj@YePF4$TW5upzUEQKJ`Jegk$z{BF2og%T z_zX?>zK{ynCd7%X+Wm2c7CAzNmJI~@mGukPPkD?#hlXO*)bZ)XyTT&ht0=ehhmFbn z*-2G`;DR&Mw2@5dBfv#dQOzoJ(cA9%xjP{7$+uro!2+9_@iYA;c@=*~4x&hz%-p@F zPk#~n9zh09!PumdD?zv8wIOZ!v3SYGQ5_GB(c+PD0}cXfAk5u@mW{tVI&?RHYuJE% zB51!c)%7AlcnA}lk>Gtkd7iE~WQwod>8&B*;Lx)*08WHX2_?K=0YFuGY#Kic3f@k# zvh^GuO~59%fL^MyWzRBzM3q7BEs*%FxTC{prZ55zKfSiDjzbK(-TUQ|BTCe3j8RWy zxmY8g6)TvKJ=32lPbbd>5gLFTMb2~4^*$8TYuhcOG4R;%Uta-p1m<Rd>tY6>^T+&~J=UvF4wjaqV3-Wo1{=G| z{p6BbUbe>eR1;%8=Xv_~?;r7Xum#iyx>gNKYinziQt&85Ix=1D1u@yuD~RMtm`GhSmPL8N2{!<6X%T|ihRey zDk2G2qH%m0dl~~f1&zFqz&WMEp{!szv0WHw_ff61NmfYScU2{z>#K{#ib)JrsJ1{vlqV zxjzI+k483Bm%wZoY&)-F>Fr&5j?xjxR6=4BKD7JT=xU7S-TWD@PWbU_*^vLwr`|M- zA@;*M;A+bYh!tebtCwP0NP(By@Kc(fnTgNHAdulib=Iwrrz!jc=FjT^w+-_sCxSm> zV|80g*)_k*u2SzEN=r&A(k1#}jpdzKf0S6ejL%-O=5YQLBVwA9;u_IWLW^z(WerA5 z(U(o@xLb2o%hWme`Y^*4R+!YldJ;-(Gcr1%+2#r$(Fwon&;8aw)0jh}!NP}yA;m&@ zbFZbtlq^|L%Il4zBOH7XopGV>`TVq`x$Jve_MDG>+SkV(we`Wo+uK`9OUq?EH!rV% zsA*r{1qt5J(a}*jwuc#v8573{Jh|V%D#i8Po2XbEMliSk`GYZ`;d-`m3(lS)r^E3; zU?c;4uQzPyl>T4*Onl1Jsw{0SJT7uWqeP8NP4|NFIrSm@EH;kWAk4DFV}aBCt0FAR zE9T`N#1}2>LSIlNa6=brS-mewg%Ry9+wN*r~ zXnwTg>0Oorj->4uL!VP4G-5bu6pLxSr%3^w44__iYG<(LQb8#@B9Gbob1UuGlDC1X zv(w!ESo@>uHDVN<87+)H$54BO8570Lp_;FY;VhfP*66IB?gXne3E#V+{>3xaY@E1$ z7wnZrrdIwL7;G+dQ}5?w9sBE=5Aj(tjw21&%&E;nKZjecwlaqI`s?H*%Eveu1d=WP|=DKM)MD$ zAvW46$t=cyI)ihJdnC-v*qO5V=(h(0YPvp*JPHF)P0QE8%)Tw)IFGrOFCVlQ>2A9n7QqGDq?lW`*CxK4a3`!CmQW!83~T=cao7 zSmT{ey%)#%%GEkTBAyh$d>eHBf5T^f-8bnH^b-nw z_VqpIzh7ZY9_tem2o2QG&{WjWz(ulZ*FER#y3Jf$yTHlFGZE2h@?|Lcoy=e&=~%mJ zyWR!r4&TDuH03Ai5a8>Ah7W19UgdEp#^)%vv$lTP2t=uBe)$AA*W~Bd<>r305id}@ zffd8{{unVJmCUsGblDg-bQ%LZu`T^|Cj=jU_Z>{+d4r1ug2RMm54b`@LVD{)AtA_Q zy|36g1a`;O-Jx(HvTH8q4G`OL{sC?Rm=qFk8_(-rPj-{m&&FQgw`*%Z#mC1}smYfl zsOh;-^1R9da&qSWCX*G z1;^3JDL*Ple*3T}`|DS@QIf0c>rSxhyltWYiY3Z@eSKhT9SjG1HugotL|9o_VM7^Q zQ(}X_6+5;zHjE53?MI{Zx?ZnQ;Cei}fh!Ym<|Xu*!v<__z_bT2uO<+|p|3Cc_L#YO z^XVAuA|>0dz0`S~O ^a%h1Q_Td)tnUOcn7=;WPJZrg@7E{xEdwb?xkuumi55MXr zZI;=F9*;M5k1v^8`Qfw=LEfv2x6vr_w_MWw~Ah~^`>q8xhL=BMDJy5ziI6W zXc!{$A~>bQia$6L^9s=R7{L6?Y_bJi$_)-eCHz?d9qZ-wQc5MYKd*V|bsn1DJoJG} z4@g+=SDvpID{j{B)x7%-DUKXMKA_0Zsa_3e9vUsUno_* zdyvXE1JUkZ1|PT|4qUJ}K0E{{C1OX|uM-*%l)deL7pj(x$C8rn?t1;k72wgi4~ZHa zz@@g#tA-Ro1Cy|LSa!JX3(DH?cd@6Mceuc`O)+p#RMoL}wEgGtmG~_sJK1f75un`QA7SKqo3SRLNvb80eN9EJsDLpDpZZR0*5QvftRkPB^?J$llN^I@UTsc z8=}$r^9PBr_V;gq#;*igI^m$=x-tY4sDqorh6YR#{3!)GCq5iyqyo%^3|z883{@3E zVbBES>KFEp@lwBICwJxFab=Ga*ylJy?naP(il1y=t2iDW_}Qve5@2|RkwOKL#2&)xa@%bc5h{YTh`^XBcl zu&j^SX^oApIO6ZSaX(+9Vq)0mIEiA|>qBG=$ak}HjD5kx$@>fN`(6LWCH(uztOb`W z()=C?C-wb=?|}*r2gjw`e>n%;k5+>Mc$;jZuw31X%xL!du>~ssCI^0G%$wo^LGR5X z^>h8^>HhCmzf?%%gSK~ASndP=mqqA)w4JjM&R$!5Ht6tS9|j|H|7~VX8j#2w!<(|_EF|NZnnS|8Tb{%@_~zt+7d8#Im%_#)PE`9Pj^FD$U8 z!bT7Emr=ms;l4xv`o9#$#;=qnKO?W}3j+kU<%Wpyn_zCw{}1&75!{G7mH%JkLf@m_*m$U!g2+a%5}z9>)Jxy_p>p5c1`u|-Ckm7G z?k^M;*i;)*P;pXo#AJy}5Ap@}5#NUx&KQk3L%HG!<)3lhb7y+?B_m)u{=cImEQ6`; zDZjTbG<4n$8YLJ-VuyZE`!%i?6A6D6G01c5BU{y8n)=N;VcR%}H$0IPOrJ(pc z!x-Y75lT|Ol{$^?PmHwb$lAJE15t|Ao9Gw|We6lWMNeXP|63-k^03D@a(vcp>>+&v zVTf*zO)$;xt6YwWz4LFvv)awu>x1<%c*9d^7Ec=n#;+jJ8Q40}Bcvct0)bFY9H|Jo zm3sBKBJ9taK8%s5RbMrF7rq-gf9bB~h@dbnylX^mL0c0~@68xrAu7gk zR)*Oy;n+Dkdy&*|UVXUXcxleUt6i0EpYx}>P=y^$Pxcjmq9JDR{*n9-ds4mH6p?H2 zFZCvaShfkh*URNwgU{K4>rEgVKtb>du?uD3xSJwA$GR`4-m4#HQ7wP3Px4acNwdI@ zbCzl{_g}DJXQ0-)0T$sAg`Mk2^zsDbw7_Et6EC;Gq`0?7%ngA>I!aD4Eng=W{liL@ z4P$-;&|aAfgvohD+v829+l(dBug>chW10zWPmc`Jwrq7^2#5i0b4jIAcB{o&e;c%2dCyNg{0u$yAWsdb72Z;+Zw7PaJD{~_Wp9yUy z8S%4R1E*tx5z$aFWl+)nfUdq!6z$gKZv+|tq*!@Z?HiimTEiK5m1cynp-}XNg<;eg zQk6n%O6+b_%>Pb*WB0ef|Bc;wJ4Q#czYmSUNt^6ajm znd?{2604&~hH!vE{ySfcz6H|#^lxIviUhL={8YV!(ciA4%C<*^9zdj32QlO_Om=u9%+3=wpLY-S3`i0 zBc|gYQtMscidg@XCEO@i(r6K+qAF~5ABddqykC3PtY`!mZS3?;Dcscz5p^9`^8T&z zyWRtxXp@ru1{PkvPxX&%dx^C&RzjeYnX7ORPT&cn2}K(?I<2^H*Ir=YklOlfia3U$ zp-fC8GhuJzpgTej_Fg;atLp>o8@O^T@_AoTarSTE)Qt;cy3M~27;f!snp0?Q4s&JI z_%Tz2&*&u6CPBSKReW1!nDD_{EO7W?|8G70oH9sYK@a;~umpNFhTP%Yg=#ni^)evR z`2BAcHa0lmVtBeeAR#ytu2X^kq5sbI=DgoJzet)Q*{sZv57|x%K#hn|GAA<#5N49g zWo;X%$zNXP-&s_c1v$=M^q1qZk$ivPf(!m||9RmUdeOGbN$dC^mYd5kfu}`-!nuBN ztirl!;8G!7kR)#JPhGb!wBv~aW$vYvKMmTW6n27Bbjr1Qbx0WUCQWyJ}*2X!W_Gn#$dGf2mx zFm9RzcACA(NL1f67{E!ye{H{Wwe9{d@dpvwEjgK4H-nh*<-SXAp~b36w+bJx7EB$+ z-Ht4-Zfd$`ju8mqzzLC%lZ%;KsWq_z1I{_)RPiHcoySF9cV|AA10Z>fUB{NcJU0Lg zR&Zrbw4J@-%Fy!@P3|W9oQ2afW$S{;9^DS(pvRVhLpG}d?A;?=0M^E+h+fnp{CT22 zOp=nl!-2AcX*lY^U5IK*6go?vY$RNzL=5Hwk&{9~oK5RzrHe>M(l9Ppo&TVY(pQ6WfXlJA%vmY{F# z51{*LYJ42iD!2CNq~&@Ml9a?wPk%CRQO0+%`ie5XsIKmq4Uso4F;P{%qRe!6+{SA^ z75G^QJe{xne7XP)%IvKZoPslQ#JE3dK7=c*+me5hVHSD7=G)$R4Ms=srdU>)_Kr03 zrZsNzUe#!y1a{#lCwCqF0a#emu3g7UUR~y;ue6KxcHqKn?67%JE6zGIe0rL- zLhWHpR5N=ExuU+R%3*h$M6=fW0csFgMon?s%hnn(P>={JWKJO}?F2|`b9QT4UaT4kuFsYHoS5E0If>qF4ik%$ENKOYJX#iYCudC=^=e?Zh{K zb?XHse8+CmwbV`F9H;kdTV}@C?FJ>=*KhYQJ%N zMI$razpm^61^&9@{%-7Cp!EOf=vTq>of32ND*I9D%H78SE{PD;=Bs%uEH>+}4P7TE zwpQBOBE|t4r4f;le72e9py2!GS>Hmfv<*EK)%gltb5hi|aZ_g8$el9aaUl|T$a!vg zjF8A{{|9G^$dzVC(UXMX4RO-zJf1LDey3%yX<`r!AH}oG9JEwlYeR7 zvy})Y&f7$|t~5($bf4WOUB|{ruK7R#98ME@*|o2eg?s`-%WF_Bv=IVIvoZi#3QVl` zbPkW3BfQlR9I`e?J8HQ0Im%B6YwWyUG|vC1uPS0YTp!IzK=KN>f%>cBUm~pf*=S>J zh}^b(&c-*7Pgz-6AYy`|OZo2Zu3Dp1i+6w12^yBu(xQ!qL<0}^!^+AExsZ9|_dL~v zB&dF1H$Lp(4-PhjSOU@sU;Z1Weoq$58_&(n-Q60y6^eZIC=wqZ&(a`7{bgrs3#Sc? zn*bQlwc z3(D|x2v~HS^S@z;vmSCjRCCOKw0+r`6DBCsj`i-JBT|boAwT?b#q7B$`MF9Npw)Ki39FRWWYbKe*jEutF z2>?#s7HQLOBNt22M)8v`fgS?yJzCI-$Eo7eH;xaUH?bRjZ$ix(CRPyFytJ*Sfg|9+ z_TXn<um}g}g@wepCZ%zj0FG z$NblkEKZZMx!U4L9q+XP!#&d(n&qih3(;2@b{%(~pDDM}4waRqRRW0ehS>%-Ra05n->XmD;n1v<(NaRZLcWD=A*pCE3S*+>wq{Qhe59bO zZKO?C z8yY83Jf~OJ_Un3{5#Y%RzyI)z+<${ zFKAPt%K9p?JbMY zFk;rkGmKlS8fBWNdZW~{Vdj3O>41Tv6pg9*81@%AAM7YibT8N;((z75_3M)7^4Wpw zEh#*om2Voy4K2$HCv0>)dd6-4^l4BIrp^ZpJ2WoAN)wOL>i7<$WGpEFCaQFUGlMdx zhT10_mcu+BRp|q=4fP4y%U|desaKOTJZ)|yPVgqhqMkW1yBO?-+Ov>dyX;Nyj+}$3 z_ojYSy2QbXiS~0(0N|S!6BEM^igj(2b?cj0b8fQO-<$rW{$rS}*y;O@pmEi$-%8Bb zF^2Zil0?e%{$=bS&c{GtfvQBr z()U)v!29g;KV}%7+u0dYP$I4vYihqEEa)|DM31yY9_3!m1gJZUOID?lL zs4!Qj+IqZNPM|HOBA+55HAymQq7e`nx>dG1ha;`sMIkwQTNO_6R>ERBq|`Ye^)36u2yU@%s$~c-w>Q_FwnnO9HhYuN zcOtujp7qr{yM7FyUt|FEp&nN_*ei<2>_x9M!(MdPI$h4Q2alyC=qvc+$sYY?3gYmv zOq>47xiEi^?!#)dl`oubnO<{U?V_>dEk&3pDXvu{BPE@Ff$~VbD{^!2ItLZ`W!G-p z=i;ATZ#)WA1@1%M-sk|+gt=hJ5KaQetpN$jwtcc<=F12W7xXh&ka<*RuT0NwMhr^h zmD+Gx|5zk`#6R8~6HyPG&9Q}P7L4r;ZkKCLg%9SRG z$3f0(>j-=Z3)4rv!{$@?-2@llRn4oz|Bb*ov(MP>ja|cd+9wMkk`DP$h@$yh)r{fo zt}Qt282P*)&n%ROqh;JC5HkY3pn%uN6FZr+vU=39iL$XUh@YP&{(*|dp6E5Je|}@D z?y1jx*Bs2-*!;=%Bf|CULy}f%dXZ`bAh-z|y6pl2z zw>nLw9_>t+AxM%W$c!S{lVlhajGJ|rh-xCfNm7uwpKNjrOn70#(-tNi@KzFUS9Hsk zj*x?>n_d3R6_^cUGM?chG}wMNe}}l@w-j7F%o&4a=XeoGPY^wCD#b8BLXbc61(56A zpW1jc#PKH0=QPlIa-4;J2=0_ulIV_Eda}3&y|3}^9&#m*z++u_S|sM((Etb6XxSPi zW*ipDvfu5q+ZPNu{#%rB-v?M&0@FcD1!&8be_9Wvv${9DH;8-^rY#_Sr%gM9=zWt@ zT-Lg`SNtnYazmE)aMan;=&1Y6N{5cXCd9gR{L|BQQq?liasOmVkG zca*ACZ=<`Vi|ciu+SW`5(u49oga5XEg~KIG=(xHG2E1S--s(K43Yb{n@np$zpXLe? z+HpcKb}?KQ?YQn3uvpFt_@48M_P*(7W5ANI0bBX+=@!|y3Pow2cSj>ZAq!ZN&Js2I zFE!y$_{i0}7poI&`(&(~HeqQZ0ZrCI&m?be5TEYDo>N^});HkaI8PHPlkx<3>n^R} z;z~=4nU(HAflpBlGGVrUKz*&JYz_(=ajJ52jqy$^D_W$CWf~6+M3mI-s|h@WMq9=) z*hN#L@xb&4&d)+~J>vXoA@^z{K)A`HR@c6#G0c)?Vwo3PocUpT8h0cPBBnR?g5@KZ zXevVA6+$BB`c(5oFLe|J%y#sOk<*>{?O4g%JTYw=famB`)E9An-j6Rc2arz z3a(~bC}n@HZDYf#$=oVw+OQpR8L!MUMsT%O!N-#DeITub%(AAbb|#Yv!Ezk~fv=$E z4OMX?`n+j2gXUvvQR@i?wMD6@Y{_I?$`>*moN9Jhc&~%RlVnA+iv?H3b2%6;Ua{&? zd*VC=V4{A7WpPHQs&2ryFN@s4ou;j(FrHznz^sSt25$JxVophm^+J=TbE3`4-PHou z-r?E|Pgp%se?pL5z-vh5JPplNt4+(vY@ykz-^0Uz621>^|t zM^WC-^6Tq}<+N>I$+n4+d6e(>xjg2JsVpp&0OunP7HXN3i<(0#%y(lNRS~w1PApun z)(nv$SBRN%KT!UNd&{ck7)W82P3WcHHU9F!Cw$t%^W!zTqETl=eJ$H zQX5$#zrIhRmqpXRdQMbpU{+q^ zSOQMMe8ew5N@C?`E5w--Cr9*Ny$a;yvi!B2z#BLeSG>`9pY)ae*q*tX=w`d7{4nNw?03t0)Aj@{m>CK(HXxUbuq za!T7Ox^P=9eB1C~*$6HgOu1a^GopVYhgAY!0a2rziNH19)S4dFr+K8s+HG7mx|ADl z+aWxS5i1VcpTO&ajSuVXUuTvDS!KyR@%A}I#38il+a9U&SUB`ulL&vz4hLe8_3{_7 zACsO(xQ?Z0-N1^yU=|Z&!Cc_O910J5Wz-#`dia@ zz2((oe20m(r|>N7q??YpR^!u416@_ssRYi1NzxtE%&HF*RwE-ITjpgl2du->uMwsM zQmka{Mry_o&1J~1bTSs|zr3HhlD>v@!}vKutn9 z5;12=L=7;Rr5{_YwJ)%<)BAE4}IScGo-I>t%uCR+CIX^i(_iyCkhYm|oZTAc|> zM8RDQz+aDvBK1WSy+{Q3q74Ir zRAE)Tft92Vzb{BfkbAG*!PNa^e_DA^Zt;~Yn1DBSXS8SFpkjuv!=9%Vl^5fHeed(JX1Hl03Q*Xlu(pQ zW+!4h{ho2@@r9GIvyZ$lZI)IxoN#jAlB^MKwJ-=5J$BS4xw*J#6voKgQAi(zey_7Iq3TMEN!d=CW z`E{Z3J9Of#L0xZh5^HgwCgbCA+1`;MvmNp}(u}{N*!hL^UyR2k)d0p4TT#Y>Y}8 zP{xV{Z4~rs0m4{&@HTCOJ2F`$YT$m*;S(CJ8$_Vs`@_Ow_x)w=D3%2U_Ye2qp5)ME z+%M)aOB+PL`1twreMkZ=9}ouMh-UA z7p+?$N(jnXP}O$xclrinWjR+_)6mdkjHQpBU+rk5ufKkA?=-+QV(RMZ+7|)gX=w>P zKbO}+J~TV)#j??H91~_PZ70ML!NkO*YlI3YIXpkTO4Mn@_Nb5oNUD^)4KHD)sM~EG4S}E%SjPlS40Q zmdpG7Ei`JrcwKzh3ky=tK%TQL+(%!ktwWtQThQeuNA}s6ec!^_lRB?v{-&F`=Rlc3 zAwIfvL3?Gg2bQMUt|7SdO^)gs@m+*D76fzX{0T-@ZfEkg*KRd;J}uVb9l%7IOKAmc zl9^rkc_7uNML4gDB4#V?sYYHup~VvS+O3UTp;OFYZHlI=vfMc>Y#aJBPi>~=Pukl* z;BP8~@we@-qta}ZbRiTdpIbMDCgzJ5o!?F@_SI;zKhV8?$wa^X#qpkrbT^V?9p9X*Fdk)-r*)_kC*S*LWqQs)@va!Yl{> z+q)KDW;1OR`g5wu}E7aBX-8>*C>MssMBlkS#pA3 zuzxPhY`}}G-1#vOOS!Lh2~ZCPo8I2Q*79TZg(|`cn|8g7 z7w>a@gPjv+Cj1f%7lrL8Iet(8LhML{*?lxI65u+cKvzzTBeF|Y>@qq)%%5FORaLX= zYz!@vnt*xhoV5E+bZ+c!?+*ImeaF|Y#P5ThIHMwwgY}XhlRQ<}>Zx-MKG`+zewxtUrM}=L*oJ*QD$ljW1 z^ZvHsk0oC%udQ8WaeV|j5E9uV+8#%m$VoVmZ4o@r`kfuy4zAWBYc2U-h)PQC13FK~ z;cPmiunm0ATVqYfcV_PH^I?ZJi{)!zk~k=vFzzO^%L9y3fHtrQ@_cu(@<*9GLcVwk z1vI@|T9(YE$?57wT6;+-(v+5x`|=}FOQdw`Gq^{A*}YcUa_g0|JG^X#oCx%JGx65ptN49hUJPN z%nbEit^~!|mPgosZ&J zC^gbGUDSzU5TXoDa?$A`_85s(SZ7wbktZ{+Ohq|usae=)Q0!JejR;mGoLHst^wYn zG$*tK652&2$uJ-X-lefNOo(DgRY`k=^oa@OGO&2eSIfubE~XJny{(7P&}hGTOfDWb zfoLKPL3^>R>-Uc`w4J8nb8-%Rihe}$T|vP(dlafnKJ*NVoBh3)`#LK;hCpOc4wJ=r zH8Nxf?^Y9K!@l9^s#eBL0n8Nu|IiJGs>qkfsi}n$!QgnfxUA70WZ#&2fTGpw*5|I) z*4F*9HtPkl1-fO&u{b8T%>gP}SqUj2j^C;_4J<@N8@*V+KoDHD*@SR-xb)nd`d@?j zLUhJi#6-*4o<%BM%jVf2t>6Mx0$tm)&&t7^W<|c`<>@shI{H@tNf5NZpbBSc=9*-* z_~HkR>Ust#A^E$(N4TI3KTmkVWz3Wr>I2a^a&B7jcz#?2OJqH2Z{X71 zt{T44t2uAV-BWvMYimQ32Cx5%)VOIEM??@qq6w3Su`#*2ei=U7gjXZ3>9Iq8U;n7ptVCE|>+SM2`wP5}UaGD! zy*NFvygW8VWqO9TkE*XAqlm)sRzlmf;;r_(u-V2m{$=*ZZA_I_MeTNY$+k#)y)8+b&b?*|2E*siH+?ygyd#xs zw2>738!K{*OygvaSs3^C=g(?2Ma(JI_+DOLKpbGRr|d=c=nfXHGw9&J+4-74tL+Os z3BvdE;o02(MJ!InM#|!L*^@e_Wfy>`sKy!;H2HTEOG{pz-wi+r`Y;U*BJV}{m?;Qc zX{L|al|oZL^Rj%rd%t|Qvfy#8(tO&hx$-ztNBT>dGM>CDsdeV}?;s5xK|Wp7Lg?k) zLaVpMMYcrYgX=lPno@E{h~kY#A9B_&r=w9 z8;yaNy;Azt&A#I-EG-)wn0hM3^LxW~qM*nx&d%C#fx(eqlwx&Ulg$TkQ|3(L^BBKo zGXh%)%qi9!K8h%Od(1U+<9Ai>zbnG^vZLVHn17Q?&@@Y58kZRpI{5qQE+BGcSyoJZ znNBRc(7)ILc{G+qUseb+F_x>m&3UjX&8pzYz3=(DNR>hCnmrF_}PR(H7-5maS-QkeST2t-_oL(@H#x>Bdc;PRS z-nHN@vabh@XxX{k z^ED3@aUKKtk7!vWKr4H!=-&P65Hp5kyJS#l#~5orMWgO|)ek>tviCu&<~0`N6#8~D zD&y4eBEV0rKkMr7-ZV!BBk|k#l{)2hq&Pw_Y;>FY9(&Cq@8VbovH)WR2I)+!kr{_B`4X%s^yoXofkk7?QdcSXb6V}Foa9sJ|#$Cp!)Sl zViCoD&02_=y0BcCqC;7WQ^`ZQkK~R@QR>RHn2bX8o_gyO?bAyu-%2@5o-YL_F0J?& zNHgFYufFZKiJD(*e+)y>`O;e|HCgXdteQgeot*5gPU`FTu=Jcway|mw>q#4Nf&+E} zGuqd;c+WRvfaylP99UPkfSo@gdFPR7_*g|E8*ZuCT0j;JXnZ5z z{+ei{unXEOm6fVdQ-2^OQgIt?^SFe9JUSVd$IF?P^CJ)Eds|1IIHrn?F!E$e`+@J3 z>3(8d&~zE8hP`W9NK`U$zm3;9)?b6fdBaY+DDWA~$%V{Wzhv?EP5O(_ot&I(ZqBK4 zqSlBzv~Q5>j7jsXwi*2MN6yAZ*W3H)!g+mTyp^CK@t-~Ye7X3 z1+Vuv+i&*m(V)EAmn^W0fk7g#p#dW=h%^>Npq0G%`zoUsN>eQ-_1z=LU?63EWZz$u7Q|`3Fb)#m(5)4tbTKioe;PL{(#=|jw@~~0K0Y$;V+~Q92 zm6v9cojuKt3Zhwa1120A^Gfy8ad;wRjgO{?&78RK-O?cV?e6QRMUY(ogF+aVBKkuzgHy}MroQjX2;R=F$n;MLqrC=0 zzZpY0%5*b-5@~C67d@V1Aq|G{6zzAOzEJ2AT5)0m4VQvH^EO@b4RAuDysd2HK0{6y zJ!ll$))g?z&z3K(Le*L4H@IHK+ACUyU%fkrH4(Y&so<@nK)#CcVU2O_W=R!2p;W;G z1CYZvq@qszD4tBH= zn(MpJ>TyyxW4W4wyJ0uZBV5b2 z1Geq{5f5C0suT;0)>th#UwjWNL+@Jlc=o!u$r;K96+)x>$xu5K|MbQ2c&ZH0LVFB6 zdjLN>I>62Wz;j=3I9BEi8GG^9_8091zER`n(_WFe#+Vuc3VpYGvSL%QXwgC|VO1M4 zZZW(H62+r=MwmM&c8djTukX_rEr_|lzlCVq!+j3EU2=xY2^+_No9a6e7!s!m_HE7o z+FJ2#Xy?tKs|OPe8<*s1$yOCBZ=h_1`VoN4aK5w5@d{1R{PaLIl-PV&%4^$BfnS-HS5 zQ)_ikjJi(SaI+3}=^CxJxEWQ_FyD)OI(`G~zE1?J6=0-wbhvwaisO=91x;UG)@*fm za1@?;&t`qIz%&Zt+)$p~cKw4PV;QAjqG14M{rKqe ze1KajvTw`YdO2*9B(;`w2sY|kuNr;y8eM)DF4B@;ah)x-BG1m9017AV}l&%17AQVhH2zQR+ z8Z)${!hXT3?&_QcC}?Ff?S+RJsODZZEu`GL^I%87WB(ExmK>b+Y}E5-NLZl;%1Co5Sfr4e_7u@s=B{_yew=Z{}mR%jO4n#W%vAt zMB(ex9(M*s7||$o7PhO9a<|`C|DWH$3@vfL#%4|ZkcSl<92}>`+psupa{9)TNMNx3pOl+P;PObP>1!H7sb|E^2HV zWX0?zZX{OZmltuyzR>v}t>Z{0Q+){7#SOcUg^i6#r`M1%oE<_lJnXOn$%oIcE4H&8 zpTs4wu@P)z$&^#f*j=Y1@WH^f(@V*tLW?gElZkuXNL934Yzqi|jH|*~yx(h*R*{O= zRH<*B%r!1u&|SIIYa#RdpTn(6CD$n{eTwW;1a8XdSrbxHQlg^13xxal`1EH5ELmrG zSSa~&L}MP9+m5!A>`=SfY?SB3=?uYIV?>ei~GOl`u&zc_C=qXYd>nnC>u97H!ExY#I9UB zh%6cFg+fiM2bNnr%u;xRSxHh3l+*!9P>+d{m?e}5t7_BsVWLzzTPTTse7d})*(B(L zD(b#;F&p&=Yn0`JkLsY{9BfSME0s5qjL2O@!@{t3YwSedj^{4L75i*+iTu@)vooOb z1oRN;g~6s0shgKstO=IXEKQ3rG{`z|XE&%r2Ch&OwZIx`c5K2Fhx+C0s>ZDwUQ$Ye+wge4B`0!}y19`& zK|j!Z0L$5H{8uaG8=!}bd*5_dS{F=2-bLjY?_>>F4d0E@vFb$>(FAh2ZlqY8GcqUT z+@4I^BjW9TLB{-lw>6OXu=DZd1n4thj<=*QARdYl^m|G4=%xHYhmxX;9KO1Vy+TA( zwvb9b#k9n{d;k46nki>qisL=NiS$(LzE9J=h%=TiQBd#S>w{tEImfWQdGtw7Pp@Fm zv)7YCNKZ?nj!%~m1##B|nk&jv)+UUxJzbB7UAbnl5xstT7HuCCX=S2L!csekgA0qU zUj5J}!hki!6G+fVi_Rrr7*dj(ovG~NIY4rJ_=ZO$F_)Bxh5;Pt4`?=50yEB7Un1z; z?*oMcYN%qu*JKDQLRIsfTc)P0Kjh{IAb$T-pzEm|;hq16Vpj8h9i?W>iqmY&A1%qW z)1jHGE3nC3j9AUqwLDKU29n~iWaS3RyZ^gwcX`5LTh|rmHJDK|Y8eS2L=;a^7ET61 zUPcqz0nzzIo%4g;X7g~`aaqLN*+1S#yuQJosc36B{=GKg4lC@(_BJfHN2odqU(6Tr zWX&4K5+*0id^g%S&ph1k?HlZ7L>6Ik((Rc~;&QJ$7^u-OI($Q?D9sZ@#nO~qm$riY zJ~kkYG2jwO+Y;LT;2KhrIXw(CL+Q%FIJ?-{g z&MHAA87vvHSI{5UOX`HX`i zBu|?}Kr>i)92Qj?tEyg!3gvfnz1wUa`8;(M^uM`IPGR;;$z1X`z{*G8^ zyV`xp3w85+1!Vm|3~rDG^U@dV$ZDmj)MC^dva630Cjald4I?w*NxS)%VQkAD80jyO zf3eF>L1<)I8;xFMZK`KTK~D5Gam`%Om)Ee2^BWGl-YisXiA(eON#ox3+q4LAO1%*0 zr1sb!#!fwSw#CKrGAe+xpZuL1a#&JMW%HXu-2WxdE2iDX$_%R~qjElYZ(V5=52Jh& z#isfEW@Xtp)^M9f7GEAl@v^&azh~y1QD8Y`^mEA{gWDbPStN@GJ#*-@Gtb{JbEC(^|r+>*|)G<6BWxo2A)m z#&9p*b(P3FkE>2e;u2k%$2T7LznJ%h@4~+{YAEOy8HoKL@1hAkCd^NLx;&_gGzW-C z^xfRecJ~Cd>r(?5tuGrl5$lxNZX7SlrO|!``5$5Zdyeh9p$$90a)v*R} zbI(6)b=5?Wd3jplCsZ0I#ZfKSzK{O#>ML^#hC6t8^3IdegKnYG5{>M(U&!z$-^dY%+5A>ZN;@YlrExV?tJ1?S}` z!ltk9$axs%)QR`Qz<{ht<{)?#IghmRu3sy7^IeW-Z9ZxNJFSs1aUOxm0fZoiJPiIF zLR$Q!1XSVgalgY__I}@RgbS0sDev%J;M>m!5_7)i82z~~h-}J_69k##Hy6W#?O8MX z9v&VkshSt=OO{RnhtB+;0~hS_O#bIs`M>BG`D&`e3yh|v5n#n19zS8%%k${}1OKrEh&!Brz# zEhF7wt&} zQdE%M((vu5>A=#7x2~zKs81N#)kod!obueCJ)!^-V31y5MVwrAij7b9->mJ!NSS8cYbSJOcdL{Y5!jr5s2b7#y;qL zZ@_mW+qFTrH# z>+XRuR^II*!gCQ+5)xn`z`p!e>86>Fx`lQ(_b+v|y@8B4%xixoP`=Tw>)pf;Rk{#3 z{HozQyKAgrH0p64&%^9Wgi;C8`M6@>YWrpI+vi77oY-Op@7C#c{)YazCHamuZ_-c5 z`E{1S`D;zfoCz0xT5aqmMrodg?os%_JMO3C`NG{x?%LA*8XKBtfr|dz1jBBHwg%4o zKrBT(_cky41zO#oZnlnhN6^_nNHToy99^M&md?O3k$x>JA;QR1Mt)`%M1KhjJrQv{C{1xU^;IKN9 z)ZEC%%cUWOkgV=`7{HSm8U%L{y_2 ziG(c5ziil8XTH#N#9F$G)U`UWE0>_y-MR6g8x(r+j#9Mm`%%w`3K?hT3so2%d>1R3 zK0-lz>t~xTAM3iv4f&Ah%!F(%;O2agkqlFme-NjPRoOWL9aTS-%=SM2LQ`dw-g?Ue zOX0|?>h>N>A*b)^X^o(m%61_om*;TA)W1o`#`|#ft2?#-KwB#*l0W0mlYzcs6(iGH z=GhjXCa7V{W;J9%EVW7nkcega8hTFzo7=x}cen0wUz71E@N zWqJy}6nHI*QC3P?P1HxFg0+V`ra@e#8VrozsJ- zJ zs2PCUaNKOGMu8$N!}ckz;b(4k%Kh{@gIclGMMxiFTz)Zt)+4R$#4t#{l4Nnyl@PJw z&HE;_n`cEk0~>{@HEpX^W`;_6THECpqeX6ny!9}VM@Wp41!LEHE#8)@oiz)GUz%U| zI_+0H3ZJl^e=S_bDw8YUdbr`aWB>V(<#BpjP-Ly+WNab;YLgjk1pl2McY?o))!!Zm zX72*M$GjO#V^ujltSPQLwS3mQX|197VQWkf0hH*@;4zX>&pensEYpn;fsIm&m?sz0 zPgc)m#^uDyiYBLVbzF$o9tj0MS0dtwV2wHFN%%=nw4?GYurPZf_9dxM{Kf#btv%cwVRx>hwB2IXQC~2XsVFOEHV!u$$k1!yHbMNTsSEu zVvNi$YTsp#LfJuRQC~7RE+JH}&qMS z8aNF``JZvYcOnmc9B>+x0_t5tz}D`B8?k9F&Q(rf-=nILL39JQcI6bl zSl@ZEV73d#;4vSKBx!`T1@uzr9bWNno;Pl&^JL7d+#y#X;-SF#&7pv<+D|;vU z3R>-H5b-362IT3R%x*u-daRf?7%0wf26OY4;DuOc-gIqkEVf4NiS>@L4E-9M78hoh z;Bm*M9>CAuf;h9LP-mui$*gQ-IOa6D2v+P4Mn4gu{E4WJtKV@*R?O#z3?HK9X2;Kf(*9?IG8PIS)=r@EED%#6j;w!FX<287A2z-^uL;dJqaK(H_dUfxoD@Bd>nvhEAQv=I}-M|PTH2!fcp7+n~ z6%n4Zl4IDSiPlCqfzKAb<@9(KqsTs1ZE{^%Gt}dH8TbSIjTf>3bK+Fzc zQ= zB0BIaZ&FONcyFLue6IUB!6LCIi|ec_5lbcCthJ6{>qwHxTAim$C_V8rIsndqjuf#i zI7AKCho%+7oC9#Mq%Oe9S7O4&9(!ES-3?5B6HV^vRz9BmXk~?P?Tew; z06rF(NwN3C1N<(ldt}9Oa{dE~m#9cTf`#dYVAJ0J<@EoveUl+2#h{UaH1zq{E|Q2Q z7W~w?AF0%BS-Km1uE~cMe!PeJbKD_5m$B=x@D2&lpu`)QPCt8RC;*fKyX~qH!x_3? zA_vNJFlV@-EqB3`ISRfqgEs0`PYU8Mo#8TnZ|m04dO;troZ6Nw6eQ>HBm25D$#>X} zYlNw|9UM9ipRK5Gr5i$!v)H$T>9%sd!^E#Rm~Deon$FpdL^T@cSy>{9aO6X_mRG9P z#i?u~&hiO)YHAyM?0FPbJG*! zmq=Mkg*yu|Qu^I}!y?){`P_zC5Agd%tv1dWlf#&<`<7^*z-{;YfV z7^oen#N-SxUs{tqjvhJS+By2T_6k0a$Ikno-c9|74FZ7iU2g>OC$Py4RQTff6(UIE zP3QAkyzHZa0L{EJB^=AD)Pq6KSE`scD{)sj?<3jLy0_jtA%xS0DE=nmAoLWz&U*ih%JIoGU6b@xCF7`e^?< zKffP5){~vx2gGWI$+N|w>#dCgVb&TF$chPVhqzbjTDAu)Nz@t^rP{tg5i|} zMH;TJ{|B9p|BTA-DD~{4Cwp}Xhdj$}_G(g6L-W*!m75s(#cB7MF-*)4n-c&yU$-{U zx}9fSn_zBPM%h#|0<8>@$bnp?o&(pXX>MXK*m*(T(TFrS;Q#J*CyR*<9QurCgHxhk z>W9OWr7(zJ!X)Yb&$Ysgpt1-j7@0D_v?sqA?Ee2{Up*yUfe5Dt^n{JbFLq%|tV3tB z0__!HsizL;Nutf5pEu{73{Id9|1a3{ckkMkWw*Z(Wh7fqx6$(w{+*I+S-*3z!?15W z)ZPv3eT*}VQiuVY_y|sitb695Zirf4PSu7vx$os}vu>d$X5^Yt_&{jeoSyqWciH7( zC8Ux{{~q7~EL;wch~Lv{K_pP1Y2DLG$`i&0{?M`5Y6_RRGX$Doh>5Gq;IDTky|8(~ zqPSFTdHSkNwXV{P9=0Al^3~4CcI)z+$s-{=JOSf1bCjJnrHi$r)@p8gr(>eYCZ#p; zfsJY2X>r-HN@0v!iPVGK&$vpqdw(n}pN}w$`OaE_?hAj5K}Myts^W^)ygrg)-8RzL z^c%m`G#U@*X$P}?I`Xtfmfov_``l@ggxKg69#GZ6ziBlxS5&J!|5gI?kwXYmwVZn8 zyUuS=;t=LgtD?d>rxr%+8j-kHl^CBH`8A@VmYL-W!L~OZjdmvDiaE=m4yAv8`irS} zTd6a!vh2)8(BsBOCQYqVHzWml62<-cdbtmdJz9u>WP68=*5|yE+x$q0w@t=LcVzU= zwon0~Z5+J)N$gF+vT&5`KOkC4y?+!)#47*)-qsm z455F{sl4(m({bsYt6az8iIl8%j-+n0nL1%OWA%@wy@kMGNmE=^tPe5)$y)JMb=W{H zviaW4Bfcx&Mq)wyuVGcBzAqjR|A)7?jH+Yl)<#JJ2|j$NND?}f>n%?6 zqrmF4mvJH9K2Iv6oJ&=GYO-iLvFkOi?PnF<`wrx@=kf%|RrD2i6x%iuy=kqd6|$V4 z1mDy5FWe{J*l;%S$p^IYzGAR_%zhT}MG2jM=lq+AFhORfsba=5w{SafY-2$vrxk0) zf9vx0cMDI=P0=do#mEYGfNlH%0rVu}$>uRPr!F4SNixP4a-mcM8RRU8okSB#1nKMA z+VhO1-&B**QVhqXS*NFsH<9~d6e)B+RbQfVE-*sfm)-`6#X3NR_u0B@wO+e2l z0O>myWj4p7{?MkJDnV4Cr9P&{HJEGk z=~wj=kpoKIWWn!#~J zw1XpckFY^uq3cN_UG~Qn##@^ZXL9eKRkZmuL;7>Z#&U&uCSlGsI=8+T;OX)2sg(K!n6^X(gurc8)WP;PL!8=9lB?L=dT7|kmPwHRb8cB~o+Hpq z#GZ_#oY!mx#5vV`I#%`(uUMR0CN6BtCbBir?g890+RFo&^lhh!Cr$~|MZXrVetz+? zha||6gnakCxlK%Oppy=RuS*4Mto`gA?z=G)manvVBRzp-+qllpzefpeDN)iGYl~;$ zqm#2}fP~8b&ZnfbHpeAhtL3U@%X{D`xq;31^KKL9pWm-(ZZvhfy(+P9Z3eQJiEl+u zDv|c>7N4FH^}PCPo6fIl@U1tT~l(m?tbF9a~zz zm$;IW%KE@Y*~!^wB9eIj58mf$Ip6P9x7VMT5GGCPJ&ezbgji}>bXupU{;pqmVN#R_ zML&6-o2gjQsg0SMu1#3@6WNgTtS1rBgvkz>y-d5xS?n-D*hD*gWozQPTXAB7r-OTWEVP)<)Xt(vMr%EKZ^u3=J7HCDer zj45^p{vxGRQDA^q6@}G8@#*9r@#V&N9^ld_WG8TDbM`DaK@_K&S}ioxQRbS|T0R>t z$@I(27Cwmk;Z=^rl*&>qXm}U4{vq{^vAc{J-Q#ONY~{kyDdT3) zY&;$rZtsZlOMR)D{wwaeLVq=FJsfY0ZqzQXrqYTD9lhcyO?0Hf0J9`g?Q!k&L19^~ zRZ{9N5XEGXw!woWbY%=ny-8t@I=o52$!}H~^xX+TZTp1pr3MxCHnlRqla~;LkJ6wv zr*&$D7W{tS$A!G~n{aeY-r&WJySD$+U!w&Mvy>(jl#(Tnoy6H%jBA9COu}&%@oUe@ z>DVP~r==8CtQLW`o}Z_fn{KWVpJUHV{j-n%3Kx3I$E!lTV|TEJ6Cp)bs4t-{Ak0K) zwVpE`pIMj$Nfst#u!jr+TA{Q%-y)Pti(chciq9_yw52*$RCJC6Ef>@fT2y$TvyY=U zyV}#xlGCycQp9R2^-#)0Nb?h3O?%{oC+75oOmH^~kEzYE?yylldsRGr& zx7Xe1NnKb!zOn&*J2zb4S-$Sd55UApzVx*5|4zz=n!PaI`$c=%mZYaMTLCwGDm76~ zj6}^D9iE7*ZTIa|sFql$7QgkJf&N04wvGkQUCMjfZip+BhleuHY}&`B(< z7K@Ux$!$dEtYi+^gm2S3VPn!N_df_2Gw7zvW)9rGd`qCvZcIV;o-9E+%V0=!huk|J z16G--^RD5#=GJ@c3B8W+Q7UFPqM@ZlzgCIX&CTocjdx0ygRFR6ijKBbz;NYCUeX<&%7)wlqojy;z{mA* zhhxuS({eWck!q5t2X7`=kJj9g^h5J#{qdX8x>G%!4L{Jje>Sl<%q71UNCgY=PdCnyZf7Lb2{b6@m*suOC0S#=K2P zUx#UuL)YKq5hWxSq|;zW>Bw6F=2p52=W<-Z>_ofW?W|nX3b|v{k~ci&_fP#?{Mi&M z&ZVu4@ZM{IB({W3&jb=mGyX7V{vpnYcXk`S>y{+kbDH0fCoyGFqRBr&tLs6!9Vj42b%RFd;U;=Cs!M*2?gyy{Y)Wn5%$suFk8 zm$M9@pO~%IX49k9`|Ka~L<`nG?1?dXjJghsFJ6q*FPa3EFm_qr-n>til#YdjWgH3G zU#RH78^#F0$7g!YowSj*AMaJXa`=dZeW8svUhs9os<BXdBZZRXREMMvuyLHwh5|SvJ4rWdN5CzXeu_CUA&k3DuYX*%Sbs`6<8p(c zPI!krpA^19xs!{BkL>sxQ^10Cu?n*~NVYSnR@~RqkiuyHiULy%-Zw&CPl+&tb6rx% zPC1>BdBx=aK6xC`4 zokksX(~e3?L6Gjyt2a+OLQM~vR7Ivv!ct++VEX1GBR3JPpP<#CJ+Eodd`Kk3M9Cwh zkm{IJI?U2;MKTeT6i#`^Fh1CI@-<^+&b;>`C{BJj=T6i`rMbHgFXA@0xbUu_A9at1 zma&&t==%_@RtgU@BGmufADB#T<;bmR@s>dp{veFf?2}}gHcUpIBj{$SE~nT1N-j{f z;~vh%&QkuSx>#N>rE_x9#-~x5jPiLJN41GZ4qvy;W+mal__*mMXNaKhJ*n^sA%Y#E z3=YZhCUFLAPQ?=Khv6c|#G;*NDPiF5>Bs>57scK}Q%->ex61)h9w1k3w`)D~q)s)? z3fDOjKNfWNcChs3pIVr%%asrflMIN;>qtL!crE7O%Jro{PCDm`bJG4;gMib@si>y5 zthotsCNDq83;5ua%z>Yjg7cOC(zfwVZ08u6Uh!*qHROX>5u4Ig&*M~Ne_*xPov|xg zv6Itrr2uCWB?Yes=e;J|_nD{_xGoL3%nzh!|W> z)!yt)yM4+0k|cv3_rh^01|Pd`T2?flRp22o2aLuvRs3?d5(>#m{osMV^zvdcS*R(~ z@Od&Ov6@5CsLbbX`IBj(zN^KZLFL?cHQkb|%xZ=C6scgjW1goo#?ie#)Bno~XTQvb zDguNLxrgQLDQs~}=rew@zzbg*xBb~fj~%95ZSX#J71mjmqc_D{mr(vAo( zVL+s2&g9-)85M1bs}@=LXy#dxh%*Kq(jfDD>0(1o z1Ri9_Twu9J;C!lO{hU@9V9uCX{n5QSlPpG6;PA!l_Rl$%P30xlayZ)DJi%W^F0Ogk ziZWi2y%x$LPN?yu?jPlflsv7n<3FGwocSdli?E zu%Lt&)B1BR&5j;&+ryexl1xf+LP18ff%)rx7!gvi_ZCSVh*8Y|$tbm9h*z4ZF%vBp z2r+GB=eI?8tA-nClpFx=3Agd?I=?{qgsOmyg&;@NyzBhV%hNNFK?@%v;M40*C*GTw zaO|0Wyl;29aw$PyIj|;&l*VCZhO3a(B1%9~)(w8*=d}1$`UJEq@bvQZZ-;R$vZ;Qx z?AyBJYV$&N8vl_yAEmyR5%NDL3=Yy5v%5=rNN|d7^bWJwn|wh@3Bwy#jg(`FcTR5&oa(0RO)? z;CH^1&q^Q)8VQ3I%IfMhqK<6mb}}4ug%yccg2mKKBB5X*VcC};PbXD0l*KTo#L&xQ zM3Lg*&HsuJM0if~FG8@e@x@h!UM7bNl0*k=Ih&YJ3?00z`NY*3leK%jdjz0JAj5@? z`3;F|12mCqQc6!juOd9^9_KD=rm+piYcg-{9{VnEtWHE6ML`wF@3kJr`4wSoXLVYm zNDt0bgIOi){io&my~BILme3mbr8{Qqp#0A5$7U=n9fA&x(8#`h?#*1~<&+sWHI(fA zhGp*>M3sE~$6NkO$$N=o4jmvM#>cWVWk}59rKZ5YAOukPd~X7thXVaI7nk@O>PB?f z|2t4&2+FmSL)ArISu2Fh=a*9Dx#DJx+2b}&DvQKaVkc44$DwKuJp92iZ+Gi@AS49?k@$!<8bBKpg2ceTys>U2cwK>*xW{Vka@l)WaXC}^OVM%qr2kuu;Exk0 zZY(tN^u}7}1G*VJ=E#BNpM(o;K!KxZZ4rZ(0=;vrU-kC^D%O%#I%YH{RXbi)wL{5f z9u(}pKd-2R@4Mop*BF-Q6_@_iOT&Rb+Z=#<7EA4%Y}R(u!&<}sF4)&2b$I%*$wg%1 zFB`?l$T{J)tUijzioeH`g$sJ;?}F+T*!Jr;_K`;*^T zvni4x;e?9u^Nmjd{1%f&CGm<4u*mu-LoLZ*Zl;dmF<a81u-s;}1{~D8gXceYei2Y#ClQD{8kgR^HtF0%Kqz95E^#M6zPY z?(I$MEu3^qX}VpKmW{BD(bym;{1pD2D`6KO|5*%oWQ0TNl_ecxy{2ko@tuR)lHq$> zP2C1}5BwiO{|f5DtnzbansY$vgX#cDYIjic@XvZ&QwuG2- zH(gul(U1eRS3e+}DiRWV%X4?vGxH(l20Nlzp)xrlOIXN^5JQGV2dB=KQj0Qp%x@I9 ztP-XksPh6Fctv!_PEtnf@_f_|oFu;YmwTN+K$>JBYmXHCb0M?mv0rLGg3?{zs2XYV zb8JrUDoo-Y0u^9|$0&MuwBLpNxcZ9*bEkyooIh!YU2G5dz(F4>H{`VMGIiCv% zpy(`ie)Qyf(pD?2 znlm2TwY$L|i=`qVLUSt?y3U<|8r~evhSo}<50pe8hCr2RIn59h*8Ka4KD4;#9Q;Nf zYAX-EJWwsTLml{2EBKpSQa&*{HK(Sb(dgSLo*PR{Kwzw;&V(9GfELJ_IYONjB4K*c z^alQB+kUYh_U-D&Mol)*s%p_cWwc2!)$pVsTdvm?Le<4Lk2CDCw-Z%Q4I#?nWe&7~+0f^N=fTsu2o%g}#*X=B%? z`W7eIq2db5qAV9j{b=%;B84aP<$t{bZutZbJK#j4UQ=8B9lE95T31K0j2WH2O_Abn zq_R@eSthVRn$1E&KUx@`MBNS#VElP|Rx#7<+R!ipN>+XvIykf)x@P`li&}rzO#A{@ z-khnPm6MvS4VIiHNS^!?)_-^qekxq3`BOCOpQGE_q&_{P`g5{2{!d?Ne-}FbK7OwL zhI;*b_`#uq8)ttGfWiHZ!=GPZ{x^oozkk#`Azl5?_x=y1X?}2*=+E*0a;^XEQh(wf zL}JjK@6Yl7(Y5|P{r|WW@PAy3y5rw(eZUsXJWh9879$9hj8CnE2RSr&#KgY0n;I?G z&Bq_WNORf`EJXi(_-Wm~_;f9F^TrnT;S=z*zXBR|Y7j^F9-}%tyZ-(v7ccp<-dWrp z7Nmcq=|M0L1$rN7Wq1vAGcVtqh@e_kr+`eyeL3-MrbswCLw zpITb*te(TP74njcsNxgsEi5dsv$H#$pZz;qw$;D?t{4c6bg>A($?)DQ6A)=Z)uCbF<+DE>twv``gLyYx+nWCMt6}*C^l~?UVtgC`LB5J@#VGAcO=TSkL_M*nqqOt`|b+uhbj~YeAv1y}cA_V&KB9)n{X2H*KgL0?F~dL}-GA{3#LjU72y6VrB-BhqSXW8+%~$m9xoZ&6S92_0VOR`W?QK6uqxVpH2d^9Nvql3x7 zCcRfFR`Ul@euME}YrMrOl%ni9m2S1W4Fzw*2(VKrW~C5$u52xLjLCroSgA2K6>V+1 zyfX@Igz`hIsRU#u_12wp4O5E`Z}IVy9YMpiji}S*E~+rmNI$DnNb|&hWrNZ|SG;Y;OfWNvMsn z5dtD&Cp)Ux76}~H4C9`PCKzzL7QKGCbcJ8D%{tIzs>-w%uOT4OWQJ#{cHfW+{mg3#z5bWY= zsw_qys6SGcyVq%?qfSgw{yWhz{-sn~Sz2MufnF<#9*$jRfF>a)Pb6MK(&GG?kqKzb zQpBS*(^;OYrvLJIJK(ZppTJ$DqT-*}*{pfDRgHt!JdsLf6Jp?1Z0`M9x8Hg9QcS$Y z{Fqvfu`@=HBK}>iO<$2L9M|~ylXZz`OX$wQt{LaPJwV_Rye;Yt+aph5XhH0O?#9og@oBc*_V9-TlvD5wL<+?8?=o8$3rJG%E$Zef z(~{+9sKil(_H$|Oi)_Pax7MzFOZGFkHuz>N|K+9QJ zsY!TP)kkrHNxs41JZvgRoRZI>phqf~a)(u60i=IT$#hYdFbqjf8YT*`6D5n|+U)J^ zTw8{PF=-`b6n)@Baaq`@;|Q>*Ivn-dv=iB_22F7Zl?>RQ>w`mTr^bnR&I z2H>JbSZ+k$^b{4LAZeCJN%g_O1v%N`ckXVa(c4Ma)VQb;?aVJMPSq8htht1yNMX?g z$V;OFX7{TWUU#y+Ed-GM_t_zh?U1*?6#0CxSXU1|%_G8g{ezZ`hyxiGNnT9UHdWSy^Xeb7g{9+s9{S zCMT1VlDM1Yjg3V>YZ^`k&ofdq(|#>)F?r$%*PTel>V>HydIWQUtv4hm<f9q z8A<1is!YtJb{G#(1s4#iFsT=3t!r(~Vvf9g?%k%n()Bq?H(PbDJLVZ@pk$r8*iiU=c7gT(nsMjp(QRIdsWT+B{qbocYD9XUD zr&z-UN&b%f9Zh1xO4s>enrunjtbv*=tRsMln}9`Z8xh%ET$iCRD!U6mG+DW{!Afk8 zbs`!56PlZ1x^K5_IBRrv?%L>4*vsZZo5E-63xDu{4*JQN7$4#R-Or*~b8Co%y|4Xw z=2{5Ye1627R3F&adcrbvU2(ZxmZR(WugzWx!-r9^_Kc15npD4f?<)4ay^*rZ&Ar8e(3!_M@Z58!2gK}z2beeyOP`0QX+B1gu_I360(Sd(-;$Nwqj zDWmyV)mu{l9SH*bA?qQRMew~-z8;=#ZJib*Y||YIS@CVYJ0Yc3wu<b7H`T#qkx`N>VvmmIgY1Wlymr?RekNW*?#*ch_hAuT|$T}Nr6u#ulHAZ=~fFpBX<`p zkk9vDbiVhtEhlFj&9J>;iSbApM?R0){gU^236yiAK7Z1sHjj0nDls(u4U!{C6I7on zHeqRgetd3@_Jm*l&90?o0jQHzpWXcEl%&Umo-SSHujps0=|3$gPZseSR67uiP~J<( zZ>H&4OhEwYcu3UK1sv!P7hr+%gG5u4rtm;M2R$sm9ga*e^(xO@9ejjO;AFE(Q#+P?DK&yW0*q4fG-d9gLR*1aw&nl#5lAl^Yrx7v|>`LuIKDXFfJo zzhiVg6GfLM=biJR@mb_gFB~qRjnOrXH>P$Se-*5PzZtP9kw+wu_l0Z6;=elVcu=A3 zEj<6-%%S7%<=u}1n}BvdrmTogyV_cyrV0raH9NeC^us@+$Pe!GdfoD}V0{`Jek3b( zGuv8zLs>WK=yM(N%7*se>k&$yYT*o^Fyl;iUWsSHU#t)?1z!HK= z5O6_3_%1h4sw4t3adGiQrrP#zz5kpZ22l=_=M+H|%2K3J9DJJqNc&zdNWlBwtU#BS z>#a4~*Zg(tn@zK*!jOC&xAiP73x!%9C3-DSqB0-ivrI51)>b^WiP*kI7$zS>SE0%( z%2`3;#s{@6mvU9CI=2yu{Gg`zlXa=|HRMmN0F(&UF6pr&k(ZWkdXx|v-*?%G->9Bh zDJYnATL5P4K~crs^V7Teh)dpj6Ua>A2{B8g3_)IKlXQCkaSGJH1x~(8aM?D%_Zpu0!eGC2FZ*`}irOZq^?3v;M)dHBzLH_fcnfBV zl7T8Hay_%NeN(Q7^Njf$aB$(hKg-vv%qr*5W%CqiCdbBhT}kxRey+^hbERq}iuL)g z919uVM%4H=9fI1+f9<}UPaL3i-gtbR9xi37}SQjAVWruOy$zUZI(DaJ*AnP zZLEE68(neGrBDfImta$J?(l$U>F2EW0)O)O?VN`P{&s=IU7eeSM^m?nY~HxTiU_m{J6hVL(Za>Wtpnw= zY!~YwJew0@R0z9_3=C;B!lB~o>SkVE67%Jcz|fRcg6*%2gaoYS&umFMJ)9Z_kH74J z@OAgJv77b~65m@H1I+MlT$m4^{QUl%I2@jT`s-VLJ=`XuS*2ETjVEXxJ8STCwlmZE zR83pycy)P+j66rpgpYKWlS9GO#n`X!4Eou97A3=!dN@mJEiIVU&~8wcjWsnb2AEn8 z*i@Y?EOwSNS87e9zJnsNB~68ep{70T+oxzut2{S~cZla0S5z2H4w6qXj;YGWVjfl=eS922+SzZ>*m_?6m z1yI1Vxyk#u2YG*eeO-xD^=WY>Q=?cFbnf7I_cJ)SN9f^n1V9zM@e?oUfq_AM_NM~l zr&V25Wp}?3w?l(Y0hSWZW`$||Nt>Q}QjLv`k6W&vL?JjWH=DnQ4yS4H7@&gxqv#z? zk_s;`?^rm-gq8P6dkDSykI{XX_03Nh@hc9GE)&t%a5UA`)z&kmq54R$&r3>dxfdNC zAb9k?m!hAKGmF`IVzQe!z;GZ0cDn)Sb}mj9bm?p=HHq0Em@A95-I-zF z>G11za0rpb!v3Y*T!jnhU1!k<0;re*{g8y3_m4CK47)b9 z)m2rMl_UCFPlA~F?-!j`o`$bu<6`d^urcFBaX7w~c#DtSN|5;8przy;UP0E@u-u+D zqIRcB>(1wDtd9MEzrP2X=Pcf3vxpXGrvo6bC)%%Wp~H-%ChP%h9i`tx+Z2 zKl-X>#K!V(2Ak1mqV-dGdU_#yzOaJwOlc;Hy_Hl}HgayB?b}2NA!lVpdjZ2h^B<;$ zgOQmN@SF>9ARHMSOx9;gkOn(@TVIgepEM6u=4(_|!Nx`o-T)dB%+Jv_!vAl|gMY{0 z|Noag|18GDz@I?}e)4~mBL9b(AOF~E>yxLL@6V{ir8s-s8L?iA@|4B3Ta@^B@$~F= zTOiAcTUC&Ya8b(?tgTO ze{qBsOaQE;ekma1?kbn(rvElgQ5BZ>2BVkva5@@@e#n`|E&Kj?eL8KK>Kz4l=j*Y1 z5xP-B^vcB{Jk%$XsDCQgej|%5J_*gu&2ch`%m#^xiNQl!;8xQTQ|PNTf1nL zCSq#pNEp{HjIe-l-l(#g^mjTnG1($KD=wTe17c>Z*-xEVA#*oa`;Mo<{r%6ZP8-Hm z;kK$Lk8DkH;{&^tY`#?0eFo*R8%wP0%l=Yw_j~mMMx!Cx4bbrD1V)&@1=NWatI9?E!U~K_QM#`<-u=@%1$FzU?ER9oK%k}!` zOonMWFr7a-WOskR6GSs0@d>xJdxR{CK2*3+nmOiNDi*Jd7-I@r%MI3Dbi`7`R$rhv zrTN9w(Yf5ts}8F-7Zd9=)zBmYY2#GW23pJf8=Gsnu)%1`VHfi@owzc}8)Lp$%5tu| zMXGnn1e7_a;RA`i803+g2}@jDf@*IEoxx|ItPM~xE!9j2M80SU#aO)FwN*Y1GM%4& z!G~%bu2J?mDVToPoBQ@FN=EAa$A6|x2W-vy3fM<(4i4v&50{rSfC2X{9q9KEC)I6K zP6`i3zFUE?RRSLmXJ<7v+-g~;d2)qo7b5qpIq<=Fc55os)8T|a8V%y)pd447wZVh+ zF;tpNvw_bhMeKCTxjSvXOYsX@3yZvT99SBS$jOCut8)BcVv>xqD$!djtK0d#365Hd zKTUb?ahmcDwxJ$aSd;CSk<>6ecZxP+KV04ZF85D63#wqtKrs{q6Jlc*7Z<&co3CLX zp1_cfAn3iYQU$1ho7(a~sruXIqSe4%DO(8@Wggqqd|mWrL1>^NTJWxJ%F5KoM%`>0 z6}a&U75#0e%g=jCNa!RwDkU~h-x*oWw_Kcs4j_bw$6BoA;IFdklH{+wymhBsVC7I6 zEEgFHS)%Fw7K;!*uC_j*sd)uhye4U;D(;d@pDd|-3`tGL-Qr&?(2Ma$<~yCGSRcgr zn06(Q0{L%GU=0RNpzp>Uck4%jdEfIA=#uk9l6sjoCl}Y%((&Urn@g)gK=tez^x;{M zAfDcLA?$s;n?W6w#-H+jB7qJLj5LE@(<0wTe|z_Ul62?c22J1$>gT~`|4hQ&PR`gY zl$D*WSIlYQOF7Nc#+oKEd1QTVA?||OoM)AD&s(~94=t(K?7J^Q0-L`u@tvAnqFg** zudi*Ezfhan#FmZKPWcN+EBE4*lD9WIvQ2JI!*nSLTQXUykz`@+Sk&znGE>1XO(NRZeKMr~pDqJ^7) z2b~Wtd?*!VXwlA&D}Lv>b4FJQ1`o*_Nn_8ftX8-IS=AxI-kr3Cv_|$Zs()@eldbS# zr_6ao@ZJrs1Y8LR#;y>VTX=#aAw0wZ-Gg<;+S*0x*^zcC@kn6v-8zBx9iw^MDIvu* zt-wrROn*FOPz+0eYOWbU2ud&_tJrwhP90jI;FK&;(cIOQaKxA03B=1vS6Nck-SENA zsTeNKT`@~K!p=ag=r$qU+QY+!lSGbkXJhyJ#E;OCWgkB+B@zUZjKk*gHsz#cG)$$x zXlrJTLRnL~oa1ySS~v3WIa4&1?a^h5sDQkJa_Xy>*ngoZiFz_O4vf#W*sRzmP7O)h z(*+!RUGusPLk9=H^1>@@)v`OGS2SGpX_bGN3tl$%%sD!5q&B%EPUg1n9!&BNoO~Eg zD8?xrB1q3{8R)7QaN$z_;d@%oOUbyvcV6g1B%a3a8&WBA)pUCQ1nv#~MS+Qpr4QWI zkD!nU+N}3Piiufw#>9|IEPveAOXapdnk_f>@p%MQ<#m{q@OVD_B`gf-dX3SsD?#GL zPfu^%+G2U60p!q=n?hhTl>WuOC_di-{q^4*(EIKLCskK|IvK(y#KC!a0K5P5# z1aHpj)On&wE>qe&VfBS=35d62<=AOK~ zu>O>yy_CD)9#Sjxwe@l3ICfw>Zr#Z3abQXESTE=BGWz9J((kR>19kU@R!bQVN9Fh1 zQCbi*%Qu{cHSp_Vb%^PX>~4ZzqVAT(<};uct1ves;D*<133%p{8zDY+#;jf_#4Lm$ zGc8_Isor*5BfG^dre<3Rp9kjoD;SucI*{Yp3FWwe&oVM{MMb4cfXBnH*+$8QB@$#p zn4|*mMjO>uM%`i4mfjq7Fxi)XG&un8>nQpqiXZ#$xtLc!+xxzgWX^yGWb z4iUAnIrtpo)057^@%F79n7p>$P6f2y4dDd5IJ+^f)b}ynjrM&ACF(hDTA%9)&Qk!c zs#f(bE&!D>#>eTJgFnRreN|j}FlkG_`(%7~by#5#4!zSrz1kT&U5l2p5#ljDOS?Z# zcZEpwT>m=0IVU)3ek55zLFqUId>XQv-lhE-Irg4^6(uzy`Fi#7a5S@BNlmT#mQhV@ z3T#w|u0ooXtHxB4xtfyFmKI3Q%ELC$gsQLi)6CdIYl?%yL;z zkFTyitY7ufCzc^)B&(9;$ot^RbT8CkWkL(zACst4o$imL!E zt+_b)ceGT$r?V&)gNMw@cN=Ej`wJvbnIq7vX(54+ebfre`()sVlEKDt+0RJ*`gI=E ziMB8lmJhkGTXI|&sdPO|jEIPL2|5&&67X`Bmhn1ARNULg2NRpdb5E3U zCGtD(^;8lB>R|!kAfM?L5UWeo(z1k1WOWRgnyS5cbtx%%bBru4BU8*}M?=I@aDOyi zW9sCgW;&$N;%c`&JXhAzY!|rDYBd!sz|hENp2m51fJ%%bA4u|qJo98dE+tW2;;=s* z*2KgMan>8?pFzc+G#)8Ns1$Q?vAsUx^YuFv+WR^7umvUNKR*})czJhi*?;Iae^<%> zQ6m&i#6_xWvKyNv=+l^!M+J9+!WaL^cz>GFaA{|;-bp?LrG%XF_t>rb2yUp=*AszJ z%_@At#M|sgDQsqb?rX1=dfP2)?rK3IwpB#8B_|JMJA)UX1?A=(r}!^vD0lZIqZqdC23drE?j1gN7sXRdo&ZJ$Px7=C}8 z>e`L^ball(8%ut*@4sX6%@P_U@mF+Y1z#_mEB8B z#mQ+_*w<+yspD|vHv4F%Y+nkag@DPWr%(uKhW)lalhHu^AsyOWT~p)x*ieGwebZH3 zT2JAfbVYE|Hi7tH#kSj(2#yM=noA{V58*d-2p6^-p2Ng1+aGs6I#M`9i$6cRIe% zsaMAU>q9!g)7b$BM;%3SbugK^0zIbBm-T(D5fUoz#5Wt#K8xmSy+1${5<33@j9o9W za}w5HAK@@HSacXG==1sXl+n}GJU%HHAfm+djWj*=MML=?>d!)e`$IVc(D>fVbSt)W z>0J}gIk$@bjfW=L(amuJ9>8XP6Xn7MH(E51J zXSv%4DTdrs;X!Afc%Wk;=1;)>@HS|`(J2zG=d?MME& z8uf_KwfN1=S%etm21TdN$m4W?9UHoC9|zp;1HkHqQb~XUKi57tw=b=C5|saDyc6o- zaaJjWXxx>XDYQP2v_zN~rJ}%h_Z>P2Z@y5G>wQ;si=)R6xwD310VFuwwmoI}@=&!| ztiKTxNbE?4avsB6au$G{axaf5~2@ceUGNaXR z#1N!wLHA8?x2Kp8HwdaN+Oix$b7bR)LC&3W;EO|HzA(#8>rL3=#3F;TN8HBnpWF z&qs@!*?CcUTpu{;_WJfhj^7}u+|Ju4K79( z!+;=xJjW|xaPr|Px7D$Akc`Bg+g8csEvd<~ySsn`gzC4VLWt|l9s&(PrF`f^Z~R$B zQA$d~S~cSW|9Y+%SgG?UFK2}WKW_C5XeS{@LrHv0%=Gj+1+Jki`672m%VfG&8!F;v z=K0k}+TQPVaYZ{q>6u!a%gcAWcM*?#R>Mf1R)Z$AS|1f|z*4p4G`)<_ z-Ko?e_lLDE2(<3;YZKH_ zRZ}-JHLuOg|53%kC?Fu%vKbi`7S?fl?sbI#Jv%;6owV0#a+Lb0_R~Y|^yly+d%(56 zZQuR~a~To8kqxs(%rlsimaBUq-)0fJp5gV6ryn8h@AsYEEta^5q-AY*IX;Jn+XAU} z0!Ows76>(&H>17+d?e^00=X_^Tugc2%QqwZWCTB+@tQ*|y2D(+iBAZ$GSjh-`zYyr zl(yvgO>lPuO#-?8U`it?DCDBGaWjU%_p08Z^Y>b3>!tZxM_k2tVmLgSSioU^@^(bv zzK2@Sqn~)0a_jJw1caJ%Z}v$Cs8M~mZ^wzoAM@H6^BEW@Ch>7MgFY>39~X}4?>L^xmbceqxh{dC&vTLfP(>BETBi(0%$Ln@>fQtZFRL?T$V=Fh zDBc1Y)&7Bjz5V@HNVub(!u=6d8cc9Ogkgk=`a(0dPZNDw8NBvx+ zPU4THT|O;*E1jdDi8lxP?xU)D%39@sVQpbU)17T`Q3(Untg`mZ%@y|#rn#yCH&__I z>-2{Qd5ED0sFyEz^=lCDG5A?vaW3@4#moO3HOXhSW!F5d`g%aW^uAAzI)mo|94eQ; zmoAb3x3=?%bY^}X6lv^cE$DmjjdoX2Zv?&f`i#t&rtu zYh`&s(+!2P*87||3E2DH*|8UiOtkodO&ZCTbp6GhMzOK6xK93LyI|KKn5SO$$9kNrAKL}d(lWNLA=4||B+pPbN{mDiaK{JB5P8`^8;?DG!SIA= z3ktcmhu#9n*J(Vyvu>?`(#o2!jSgp6#;pjw3+q_VqA=Zz;a zp{2&Gn0Mp|-+mSDBVc~?a_#5}@4c|!ZIYurH@E|?BLYK7h$b384y|7^%MnhIYc?6V zSZXG#+(io6t(^?^Bvc;zX4RQ)DhS>8=C6BY3Z4!Mwx8jc$?7-R-kWv160`0NzSh%~ zegP&uTE1n&22BS)!UesTz(_Ap>;oupE_4+57TR!LzhCyK;ZEq=9-@GR+I`(1X{6j? zT?Q~AMwgaB`&xrhjxQ%N0VeM2(X<<%ne>q$A&989=h+N&(t6z$q2SoM~dzh}}B-)F;CON*HL5#Z+a!3Q(>ooMW z9G`e+HeC9&Dsp!6bXpeQ5&IU^KHl~@4HLv=#EdsQYCNo7ISn_^v9f+|`mtwlt>4m! zMMc#E91?8nT?G+@APs?gZzPW!4GJNEa&)09I;yJ5rr5T@;F#G&ww^ zKkn6?nn%mhEnm8C)tFWirzry|+;$tuPg|8n{2|6 zCmI*ZZgH4SD;!u8H{f|n9^6|?7|3r?OyfBy81tE6uc&0(o96e1W_`HXL5+|s(|qcQ zD+?sx^}QEA(=Yl_)F-L1TqZ_GXI;TwO$$TXk+O29w{{hpJe?0&cbNh72nY+N#8N%4 zcQ6URjE@)H-3801SNK0x+3e1rB=dti5VdnV*{!bo2CXiG1-=Pb3rx2=BTP@TT{Ynh z^9!3%gl)^Vi;4|k{&C{q6Y~GG_uWxVX6?SrpfZdCDk>sP2Ne|okq$zJaTEao6=?#A zNbkLd@H&7ZBA`f-u1Ig8mrx=#QX{<+dV~O>CnS*Ejo&xtoOSPAcdfh5UFTbOt;0WA z1m5@E@80`qzvuTno3yH_aZS$gy3%vBA4dhEr4$^EUi9)1`T33HPL%n;#|Gsc|Ml)c zvD2akg?zpqSZIfUs*#8lH!?ObfbC^Z)*vZz3;DCkCns3lbeo9N4YO+FMXSKMgh{<6TJXk?9>|vI zYWwT8y^dXfsJ7j8%=^6Omsea?l>7*tU%li6AM;2|_Cg=zY&wg&#YCQ`Z}TI^X^o35 zhfhynR;DLrpSg!@3ylIQKpG)?Qd3|2>ZFPL{j9BS(~?KCgCbsSUF69PE~ne()FWDa zgJ&ej!=;od*SH7(lRkeyYWs!l>6b72Uk~NjwgC?lc*FJi`MEYL3m~vLL{10xqvqCy zOClmr-F{`$YFu+O)jQiMIO;ujq>z*iy43S)*YNzx;~hJ^mWV$^(wvQLtx!Ju%R>Z0 zsLWrzeQzl@H-Pm>`bF&S(aj?JB)?JH75r58h<>TVZV7O`Ui5K{#f|cuZh7%yK-EsY zC$H~y(8mgKo6(Y5pN_Sp@z&ATqr>TMf-9|{H(lTRH@u5K`47!b9~~Ibavf1ucV2aJ zcxLy9iMi38l@wh~2fL+2&1PKr_(XPbu`Mytq1k%p*2;Kh9Nh56=3-jBsz0&e_T>oj zK+lTWPb^cW>xq04Nb09J=jz2z!NF7^6Gjg=j_c@iN=DGH{OamrxOJm=Bak38tah`6 z@F}|r0&cy%y^nNt0pB9;$zuZpgBY;L4-C&r%UKiWqGO{kUS1jNfo^-Q&qS4wyh8rQ z3NZaiD!c`8$cvW5496>mSpUs0wae*m(qGPW4TP)Gb_8s4Cm%mLk(!cNmAIZn?e8DP z^cLB8Xgd0CX1?wk@N{kCRfmdIO-#Oh>mPROn_V+KN4mB|3JgY#)mAbfJzv|mQVWfx zI!^i5KVYY-<9KSLKXzWMs(HjauN?Dk$K@jcTht7JdBq zv`c*9Rp{bm9sQ$fkfugOJ^WEgWS(R8aG3>4w{kWz;l@UR;e(hlD)eP}phM$sYIL6j z%(5-eS64|;xk5+7bNzJ=9<8Ow2rgAywYnuzH#RlioMliaU$YsHn?ICjP%m90UD)w( zmzO=3&i^}3zuBe{8u)M&h5(cyuk;@s)a;MPO;4$%u6OtmHo9%5n`FKI7 zk+(@|6O*3R8gsF=1yL#bP5_sq*!$hWCh@qUwAu_W@8ai3 zK9oljXsa@RdVc=&>G_x#y+z(dUa^~{B{yehjVG@hC8wGP^UPYT`Bdy&+jvVsb1t!&7mgsy1BWdVrmtKekkx@jKyyNMyeM z#*3WF@>=t^y~OOkBUKMCv9PGUD(jyEP}_1RYi(kySz0beR?L$}Bua=E$zxkX|NIxL z%L77MNr4|U)~5p-z(UMVO)tsYHO~?ggAN~EKpWQYZAo?CQ&(r_41GBo^_rIcW~-ly z;^SHp)vycd-+GfSKtyMAKDzmyvx?Fm8|R#p9jST0ZFSC>Xx2~(U^Mb=a&u+2w5*Kd z=%bXR=(B<^?yk$NY;%N9U+~a!G|d|CtQr=;l5RjtU_eO5j{#Y`q^DDV z+S(K>JcUoCM!$bwCN!)1$6f74aUx#63f->V-nr~$Q||y)j#GI0owd)N5rFz;7fRM% zNxw?CVH~{v%sb@ULjr*HHUX^nJgDwZ%jIEL`@#H?GS}OilM8Ry1GSSk{kE4*AJm`r zoDhVptTP32hFtkakZfd8Ha_{NsFQ4*{Kpxf%o@DQ3F6NnK4W<&@}pAZ|LF(x_uY7e zz!Qq(0L0}^>@u$N)AG_E{Q6F~q_mWE%4OylNToloqmL1ot{@ZSz1nZPGCD&>F*m@E zOHqEl!&1L3($Eeq#S-%R06#7AM%U3s+pCir*a&t?$&Arl2R_scYBRfM(HP>~F0vaD07DgHnWePECXF>95 z_VsDeES<#38gFuAW8jWdbZpF|AIb6kWo-x3X=?~Z*_G;+S5{GBB-jVs^lgt8N%iv^ z>9sV+MX(PEvC#3n2Z?VaB^(Xa71^v1#rGQpyeIV@JvkS_@7ez_&VO%qpSE2`Tgtn9 z|19Fk6VMU76~28WT56?rR>i4$7tlT!u^)y%6kNLWlgsbW zxWy@;%gDgJf!6lPh44DR)p6fAL_iBOfW+kBmoM5sHZlU>$pk9V*mT#qe)n!G2W)?LxEP1nSdX0d`hJ{+t@$4VeyFd%L!H$L=B`6hRLHgNFls|-3S_p# zxtVXIyOssp_w8KAA$*|e5D~3};aXk7tME|A0Q<6>oSgc4m85(3?p3Xhrus8_@gS#c zy8SuNpx}uB3)`PlU^a%sKxcj=xdvOV0?>bPQ%pXXtuY+8oxPng@#^*K%ae7u?>#t> z*T^sWxK?nOsdRg&(6s57UO(Rskk4ppJ)sTG%H)ISbQlCSkcxjph#zHD*TjS$4FA%# zgZX|J6}15agflTL9p(4riSHfX57)Zo_gSZ@U;bi!AN3xMSy}=|19;DUedj&yJ9OHB zaYa_P!W#`QI9ic9T{YWCb6o~>BqnWpC6OSCZQ99u`0^O9+Yok@F@4(Las2{?Doo-+_B$kk}mAc8e%+*;>3N8jEq3KBV&ogcZ5su6b}#2KiA}o(*!L` z1nv{C+C2+lXKM~0<^%B$7<^|I6=79{rmi~4#R?0 zuR!sWw9Qm^kkUB%lswDoK}Y80!Q2>8xNDCgvBv-*3}T~ER(|Iue|)*+uJcQF^h}5?Cjmgp`SoXp=OtYSR7YSN~&Vk z+q%OMuG4q8{kFU53zZUQWpbHcKhs~%hVk&EFdOVwYiFxV@d_#|J`()xEPw)7>oOe( zXhnl%{zUh%=;&xcWh5u>&5Hm=0?U}T0I;CdUKtUwm9Cn^{q2gZMjRf+*owtz>NqDB z*bl`*bW2(CURO2?IrjD$`3lD|SyGIFhK9z>%&d~vQglqbkv<$=;z=sW(5!SAaT|BV zT@oP^1cbn_DdVt#=>^>F>;pTxnp0TV6DoFqJ z8Su77%WOT%%WuCtwlai;OvV!{cWfLSAi#ld0XNUoN>+63RaaLJZTR%Xnb&FDsv`&g)K250n1Dt6Abu1A88^zeOpN70*g;1f5I0@=gD!$fq@@h z@dXhoSy{f9`{cYAf4G9jx%0xMB6PiLgE%eyLmpNlWgDjI?yXY#uiCrf+vnmH?`& zJoQ(WNu`kUE};IaQ0&?g0Sg(*o#RV{qdW&`o}?#;rNKISF)pIJYvo%d0vjW$e+;&i z-Q0{)3H|)Ze^s~-F$%p^`Cj$;?!_31cx5u-uyJ>l%j49t&QG&1Ee};!i+O&|c{Vc6 zbJVT%{!+|rKqeSe#BLurr(OuQSTi%cOYbrlG9_ACF=Ws^Uh$CL zM+TtPn<(g|(!T?AhB*hJts^J40182(Zl{{DbYYfPRkZH|}zs&8`)#GYwQ6`SJ zJ=>PLREe_@DJ~9HRWZs+mzCu{r>9|5?rDBq91|gSRiDcna=tpA*Ix`aOtPYPz(y#pL!%=Qt_p~iRJ%pSp$KYKdNeh2AaC## z*V))q-o*Y4W;~Ar^y!mdgRrjkk=x>mO4hqjCe%I7&sjjDIOYW|tMK?+7{Z%kt0g*< zt8?w>6c3SYU}M^Fp6cPw*NnDCoA6FH`)+a3PW#=rkoQtL0P+7RVQ+uO8mzg>!FnWT zo>1W+-IWJfw@tdSplzaiWAo>TuV2xKZNhDol&nb#3!6jw z{E0pGUwu=T^lBYi@=EpV%6tYI8-(#6`lYfaj^!k~xl5g#Y1iGvcsoG~!k_K-H_FR% zCTP|;gSglu>jE`fox^4RBeAbljMUXtCljLU^$a6WGCzUs|-cxtZ` zt8NX4!cy&prfacXknh@}TuMi`qVt?nW%Z|GFJn(#&-M0raPEaz%A}->xVXw@c4=#K z-3{x7t&yWyY`hbG;R?1c%%os2l&3<`14C>f8BJbIQxo35{H*5Y_NdZnLch>N0Ytye z&B2o9t;;uVJ33A*4dgWqpXjc}+jD@YVw^R0Ke~&g3@byQ<{cu|lplr3>Gw+?S*$Tm zpRW+eDIRHxsC!CBsWbCF`MKY5!0YM=o-n{xKi$>T)vu#!I8{nvs_!23gN`^BvOxb6 zq>6|7hpDZ6?q0h~Bs88Au`*PND1tpw^y&^9tC)+KZ<*IJ`*g*yHn&LeY9*33#;0y# zclVD6#gJuNdw5Ht9bc~-&0FVMa*fS2ZS=g&H6vN#s&Uq>$`ZwFanyDwj}R1qy7RVQ zQbJDns;tT+PY!tYmv^(rlNwI61MT)%4WE|Qs8=mO-&*NoWSkb&cQC}Nz_13|AxZmB zto^moecc?H^+n&Op+Xy9!MH)^z3-u{kD7*|9;d?L2-YK*2Rw`?qnqxyf?#myzX_-e0>OEahv)Y;m4mG~g$=WGBTAtprW@ z!Iho8e4FawQf_d{UgSKb&Z2emCzmV0KAcdbeJ}^`GO;T$F)FGz!#-}XVWzxg6C&dA z?cIXDTRja7&81!vtI&$4o1^C)Pm{aay@_MzQWcj2rI^UXZ=K>I0dt+)V=1k~2zh${ z362545jpxuO3A8X(neVJGx;Hpe<41$t32G5eMCS?mun;dh}9v2km7F+{F>L=8CCx4 zf>uhU_7tb9Ign}f^(5`r;aXvkx5dvPofm2z35$z+jkAlSXIa;7w0CZk21A6WXY&oL zEv+mf_`LrZ8?qF7PEGGms=ySZJJP+Dqv-k*HpV)BaLTxoX+I!nOg&giNOF5bEaWN> ztCGj!IVBFsN0$AK6=;2rj(h)hcxP+5P#I5REL3794XS8+w$OV@(de3uqd%^;*>Gs6 zNkt(VM{8?SpB1_9-oxXe{bRnULG49?yvYYYxb-Qg}W-@BWh+Z=_JzI)m>y!|ywwhcvz{_L3pU{?a z+g;YU>uCGX#`oE4eCPZ!GfgEaur2cD{`UeyCykb(Gw33?r0U&8ux7E43kMEP3LvsG7z6-g*_cM8NupYNYa715XMA zTKwAcS;^zrb7jS??F%X@PGF8LEi*vK;p*xN27BD-AP|CNoyMu7_N5Z_)U=9q8jV(@ zG)JU3phf{RkbvSUgW=N(3kyLwr<1B8DM|OPrg7Rkl{!sel9G}@Y1o7?5U*Rv zerWElbP3n8eNT%cHQ+kd+4A;&2y%-PKq-xj>dBZ|jHf%x@}!cR-eH#K@p@rOoqB>Z;WFtFk+7p7xc4122>e z$A+nmt)5VUR516^b`932ktQ&(vzJ475dOA1%R`WNhZZ>aOfyc5z6%zNPMC0;KOKjl z?MlE=+j<3?ok_dx97BjPz@(a)@ic&Mf97rtSDKOunbnNFzCR!V^%R>s4L=Cjcuz_} zuI}n;D<~|V$I|BUpi*7PXR0Aa)Ib2ajD*g}i!YDR6H5{Gu8WO>SrX9HIG!Rv#m$E~ z^ULh?*0VA&ZX?gPI(MxhYWmek{BTKeZgGHxUC8ix8$Y_ns!83<5RO0fEDt5aNaCeV zmZJ&5w|}_6vs_os@oa27inKl};kQ1*GA#TT&_}~un+y0KOqxv5ls}tMv)5P>$TKJlzxAMsHWm~DneTKMbS3Why~FLh zeD485Dh|yK6gdT>C+pot;vrL{bii`2PM6Q8eZN%+lkJ+G*>eerLf1_MRFaML-6jGR zn2OVLX-m0(#T!BJdY_ewrGLJQyvrE6$;JuR3wBpu>i%967=2aGXOOUmb~X{f?B-q4 zjNMro%aXAM;qt0kmt+Hk-u@-)+3XW5MyIuksgcp{5@e5_@R{whPWD`gw@wnQz|R0z7dfmZQI)>lKR;2!CC&;Mr9~qmIe$3wsC2_^W`&7)}40; zVcv>8d6b&Mb`qJ*?pZ}Y}(uCVwJ!m4N5^8mI4I`~_pi;#zYI~^kf)*mn*ezyQ;*j6UK zvPGR!fqPGsH+<4JFaT~84PLhqG~v+Jo6(`@we(@^Qdmczq`4{dFYeaoPanu4Z4JCq*;;V` zO`;AsgkRwP(AQUgJD`VtAam3Qk?_d-dtX8Yl&nQA9tru&MXE8JUGD055zFvDZ?g)b zn5I+~c@u>nr;@z7bs&!{&_6%_RUgnlhVef=zLt96+{)?mID!CItPbh%D`)f%TIOH_ zzXMBcqDdN1l`Maz{OGFlpZWm!>woI{|HS2R|AYB2LJq-^`yW+Vf7sS{I6-7>QPG+v z0+a7(0f0rO@Av85O`gX9%_9?-tXav5R-Nkw63QFjKbL~OG9jLVsLmcD5S|2?!w|q{ z$09{8Ufx^V5GvD-i-{p1$tZMJfS9N#vXRPt00Ejmd-n75iQ@;-sBBc`vko=h3A_8| ztfbG{$H8~oB)NQ(a*X54!$;3BHVl{`T#xL7xjb>gjms_6x^9YL6XhTVj|yZ)FJ z-*0oFivnAl0)hoGC=iyHW%=cg<-K$FE)5`l`{;n(P2e{Oz;pBRj=HxVV94}TiHe9M zpGJ0(RR0Z~!a65vkr{AJ7As8UQy4aWRC%Df1O~rh5!k)GJ%J-}phu-_1ZZ4S>rI}V z?(2{4kF)Chg~bbhAXCqkK{p`7fD5SJnYfm(=uD_X=pT9g4rqUbW^7U9Gzj*ng|4JTWcU`AY0GbJPdWUyL0;{xl`)D`>h2bn0eMmYuTtl% z+-lWqBxx*5houPFJq0$#j`;GqtYp8X0{|3|vgzwUDNl%%gv9N%07DVb(Ipoy1Y^Xv z=@!;*sNrGEKmP5)bl)16nZ8uU9LarQ9B|2j*}-n%fv$P#y)*o)25jW}I(JE$}+8JZ+>`LtE*c-BICCE$qkC*srvJ~AqM{^jfTBkzk*|q~@+n!}cXA3DLZrGA_PIPrgWLI1 z`0+STaYS)iZEc#TB8XDHzP&KrT#OT>Z%MspZljUnnD31Mi-nkU7{KpP^lc6nuiHPS z@HXkiwH38)MN;Pt_i=YJiVL=Pk18!Gxt^}h4O`$>0sS}FiOCI* z=2xHVcbWCB#ANRSA)D?4PV4>@8=<4Eo0G*$`2U0ztK$9xR$MCzd}hOk4@YXe(N$Gd zQ^&80i*tsl1M|lET-ogi(Bm#P9+Tpbd^*OHbcSV_RgU){p8mgQx9c3d>OZfWyxP)2 zTEHV70JB^xb?)4`GuI7^BBg{j!r2MRCT7Qt=-Z&m{>K$wdG+;-**M65L(KrOIV*&q zcR8czI9RpU1jdtI<)Ee2#f7eN1G$=d_rE@;m2A-X0xBfjSBoS8cnpkQL&FGT6~^(K zJprctD)@)Kj;v?akux(gWMpKNcl-O~COkTHAiw?k>w1qLN56Z=$mn2*$RArSk_0~B zL1_VjL2!l+e|h(t(@eAaPyDV+OMFo$DQLpLfXqdaOCa^7_Uc1L4)9zr$v-gbr&1t6 zMUcn<-C%$cQ}kZ-z$JSpLzC5Ltcvwm1<-%?t61h=k~ks4_&EPgAe+!0rDVRty~Mqw zq(s(va_{@+$O0qX2gbVGg%>3x(7CyG8*^77F6F(it>Jt%UMmSnNwtn+6(TAAxk!rB zv@yT|536nh{rJrM*=h~>FW{Io#pD}TuCC)!eU=upNOmN@on>CWF}EXrATisAzaKz- zr*`eyHH^pNM)P|exX*Nm_wX|C=NDmI%F@UkQha>;bgtL%2jH;dl_`5*;X9!Ab~`)! z8qXEXX2lCp-&KOH%GCiRnW_ZAhyZ?PVfTL^<2ESe284kf^f35=jaxjpidF_am-Qn_mi@& zcVn`0U5A%kR2HrSsN3{c&apv^mApgs!LeY&22xhFN$-=B-@d&;-yUN6lM1FEdicEM zk6=zmLVmWbZvYAo*3=Yg(($gBx&!K3^k5jPj#x2>Tjc(5m2KbYuL%nVe}e@EjO?;9 z={HBG&33`f%8EZpDk;@`{J1uWB-2;Is3)fF6;km7#2xVtu zdBzsdGQf#1fY)JkiP|;Hdx6-!`JIUBYBSX;kil!Zdpsf^fn}({FqbdOdd)Pm=jo&C zS804t2&>a-f(7~cHm29!Tn7bECf`n;n5-BwyW$I4mT4zQOJ3K2VQ2}ht*sSuzm|5` zmm;9;@}bGmu0^xw!xJGwkkg))907z7E_>^^F8}!$B{FR&1y<)GkrIE)vh%}i6YN`} zbG_wYzJd2-1q-LM_`!jUJHNd->RRk-9|q!5{Y_^5`u1SMn>U>x4BZFLG$;$c6Lw~T zI!0)2ZG9OsGA+RrLhh}TA#zPtlG{dF3y^7Qn#%$Rj>n-a9kLE;>HV~a&sP*M@JA!@ zxJV&{u`xX!AK>Bl+l3Tp-M>d|2W$zq=2su*Z^eGd(am}>pSIr`EbBNb z?abJNlpV&ZiAmfl0`7aRLX5S;1!1*2qsu#Fx8>r`Pei(v$VtGSyQI|@isy5g30G0- z>3BtY=b#L~oZnXYkSTKpoZ|QfCOI6wgA{0e2k-`$OVc6>{?xUiH_a_A1*7QMGu1Q+lg|1MC$!Rh{^h=+%mjC3ODfx;$b&<4<$jctoOfx}P9j?AUz+0`(n!Tz`Guhlg~{SBx=epM^~O zDiH)A3{csb5Ls>!?ONc*HASZ7=hv3yJ2byKA%Rb-uHnKWDXRtC3L_(WdSXH?t15Ls zb$BdB4eh`|R{04w;P!Be*RZU7C#d)W&QLJhrmd~5(y=t7oU7L6?ZQDk?^r z{EQ?ead2=H1OYSTC{;!vMF=P{fC0C6aPZ&XR)FfaD>wxd%kIQJ7(N%F04;pyLHODB zwQ2!q=NE&j5H5(wp{cGuGM^RKThhSXzo@C90ope-nNFIGP2C_Kw4D5+$}8(&16pMP zW`S`k+P)3Kf9W^NrS z1r)0IF}{{qb|4a!k>kU~(;jd*kyi@tG_i5x(X!hJ*`GqEOn5^kCgULGr=g=msGdsl z?@i~)!b&@iZXEzX!{v}#sOTdDKHDvPm~!u z(>s7qDJ$Kd>f6ezo3%P~`Lmi32q+?zciMDN3xQn8(0U8cO08pqs{H;I*Qo+iWa2ePN;T)Vzd6zVWsR16?M zVYP^1vzI4?@9wSZR6<_t=IX@mhI$m$(TlS9LUeaQ4!a%#V<;?NHj7&vukN!}+0S7~ zyZpa({4ll0SzZ;bd6WM`q2*fPl`B{B^Ux#$ny_jGQp_gRZhC%xPXGfV2vrIk43~E) z%E+<_y6`YV;0n}XkpBzUx;LdIBN3=XOu5CE=ZpeEI7WgKtbBe&h1UdNmuzf~rT%(? z;d<)KnHM#sA)CUDrtsotmd~CwKL3ezXJ7htT>#Z_XKnHH-}D5w^BljrQ@bkIfY)hTevM>>SdWrIk}itWl)O2lJ=Vli`PkRZctr42)eWn$TZ_{Ex3>#VOf3vZ%wUTbb-D& zUAoweYmt!yt-=RogCoPWvOki~i4)X$18fb1GtM-E%^wUYvcwc%DX*?aj?FFotmhQ%9He+c>2M&22BldS z`P=`XLAxBHwYSv?Ce<;bjI9aoBgI$Qi%mKwugAQ>K3Lmpm?}X)8QZq-oBMhC{@X_XJM*t_J9iywwMWO^?3(=iJ6Ly zm^67v$r*#A?H%gL1D?nNzE4F?$5s;THRu(Mn@Psv{4lZ`{lKb^;ATRx6j)e31 z5K0-8_eW(>vxU1{{+3%0C#1x0Rgff7OrR6|NuXgq0�AUA3CsRZ^>~A*`daqRyj* zjSks9LPzdRmvcofxwU;*o&GAJa`WQeZX zOdMqdEcTd9R3Y`(8H3^J!NYlYfp0rogSLlTGRh5-rKVVKE2&62&*~nP+a^UrUW=j1 z-taZD)N4@XBrZUZGO<`_?;SeY9_an<$CgtI9K@_Q?`aV*)(rxwFe?voevy+urT$Js zfX1tE-U+3el|@sju%M4STV~1nj)ja=sM}uPO*nfaWz?+UMN3SFto{q`4%x?biA1Ne zlLtdSzcIShDo2L95P2ZZTBqP^_I&s{Uw!(!=-}byXsX<=Oe85>FR}7)2=9+1Y1+#1 z!En=2eZu34f=|v_{T1(2O?$l*h)5rV`Hz42Ex*9YpFVk8S8m4Ky!-56&dTCwZ^Ev< zXbQY&F|EfGqJ&O!;YYq@nJ-vV~>5(^8f;4 z`Ev{irL=!7a;M($xSc94R!9$*BClsfpd#0sh7&|;igD8^!m_)nQ_NX&#au6#+;%nk0}i({ zF}<^%pv7l5s|b0n^a@uNC`s!%8CLpfg5r)lns$nF-gpgmJGla5L|KL;fJ_5?dB=$M zx#K0a+oL6;>2Cxll!zI);M?JPmK|r##0?&@_4Q@NSY7OoIc?CK#WsJ~Ui6%l;udK@ zQmQ_uBCX__nB*myik!mSDwihA>+=P=St_Pdj{<2MB~Wg9b9%5#N|ac~ua){Tmn^(j zdgaEyCYa`1kHu<{P~$c5viD`IXY83X=)9wW3B50l%JqAFmo4)itSHYTI#bCp!tA#3wxO=- zyhh14-_Hgmq_yOQ1#$+lEzCbIOsq+}?%%OdF3RKZYDMw}R)6>RZT}`UfAc|tT{$b z6>u=z9)10q_A-IPUM@~nT1L+~zQ)4+?U1`6x~yI1j(uLDS8)oaw?$M#*Kg-s!cdi; zYfmNy%#lxs$9U=m`N zlxuV-bF`?X%0eI8$qGwd+h=gHcFxsBftL|y<@CsdyeSaNvPW@}}|+&A!Mm4x+@npo4;@ zj&b^E>aJv3YQo_?Z(&u|@Qty)-}TyJU%vSigydKm%Fy-yc2;mX3$o0V({sqSaWE=$ z>)X4O*q#`8L%R8JxR!H`oUMa~c1UzPN5`*ueVaEQHD<-z=7bv6(dW4Y-V)I6eox$L zj+zAyR}IU>>D<%A-`8I1&9kg%sB>#vOrMKhZl-Zu8goXCJ}CE_PS~$KYIk45O5?%9 zM?EKH5-J?8e5lEsDfkWQ11&2sVs)goPjxSM+SGqd@T#7SoiNdh>6c-Cc({+m>F1;E z4r;2sTjQG)wayJ+@PR3yY;b?SrS3{>x%<()C2qdBE=1LDuR^s}ZauTf8k9JzevdRP z<~mP8cSjz57Ikv=bG|C-8u9v*qKb@@mlrs`_d#y0YIj2lL+uyv@KM{}$*Ybe zt?uu!@HIc%!D0omtHKwu=pQ%q+z}JQ-yR@Rhs~PTmc6cC`gD6JKkak}d^B3_na%1) z_<%c6!m#I?;aGG(@*W|5I9QOiCJv8KL21cb-+w}x6pk{_3*0|mzx}OUZxfMB=r3sl zTQ~F4A?WlNB2=rZOUu~c#^!ulQE_@Rc_S&xCG(ATxTsZK?FD}YS!L%xV-rE!<1b3z zaYQb~{UM1V8Cfk!9N!t+8u)6*8ACES7y`S*GfG{`M59J`fw5f^XOGd0FrIKx*_=A!2e`<_9m{OV{;oBOZW=oh<+4N^WOjhy+lzmMtEe@8nObq7WU=>i^ zkpTL-shXKNICPX>1uw`HVqsx2zna_5fEyh-2uWF4>ik=dUh0lbIhYmoJKBF1{_!;M Fe*j}^zw`hA From 670d03d5728547dcb124fcc411b22d72d2abae87 Mon Sep 17 00:00:00 2001 From: Codex Review Integration Date: Wed, 19 Aug 2026 14:29:01 -0300 Subject: [PATCH 10/10] test(vendors): update the deactivation e2e to the confirmation flow (SH-254) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The browser smoke encoded the behaviour this ticket changes: it asserted the Deactivate button was disabled when work orders were linked. It now asserts the opposite — the work orders are listed as links, "Deactivate anyway" is enabled, and confirming sends confirmOpenWorkOrders=true. Also fixes the mock's DELETE route, whose anchored pattern stopped matching once the request carried a query string. The raced-409 case is unchanged and still preserves the vendor. --- e2e/vendors/vendors.spec.ts | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/e2e/vendors/vendors.spec.ts b/e2e/vendors/vendors.spec.ts index ad1a89e8..181711d6 100644 --- a/e2e/vendors/vendors.spec.ts +++ b/e2e/vendors/vendors.spec.ts @@ -66,6 +66,7 @@ interface MockState { createdBody?: Record; updatedBody?: Record; deletedId?: string; + deleteConfirmedOpenWorkOrders?: boolean; } async function fulfillJson(route: Route, body: unknown, status = 200) { @@ -259,8 +260,9 @@ async function mockVendorApi( }, }), ); - await page.route(/\/api\/vendors\/\d+$/, async (route) => { - const id = route.request().url().split("/").pop() ?? ""; + await page.route(/\/api\/vendors\/\d+(\?.*)?$/, async (route) => { + const requestUrl = new URL(route.request().url()); + const id = requestUrl.pathname.split("/").pop() ?? ""; if (route.request().method() === "PUT") { state.updatedBody = route.request().postDataJSON(); const vendor = vendorRecords.find((item) => String(item.Id) === id); @@ -273,6 +275,8 @@ async function mockVendorApi( return; } if (route.request().method() === "DELETE") { + state.deleteConfirmedOpenWorkOrders = + requestUrl.searchParams.get("confirmOpenWorkOrders") === "true"; if (options.deleteConflict) { await fulfillJson( route, @@ -517,20 +521,28 @@ test.describe("Vendor directory prototype parity", () => { await expect(page.getByRole("button", { name: "Close drawer" })).toHaveCount(0); }); - test("blocks deactivation for linked work orders and preserves the vendor on a raced 409", async ({ + test("confirms deactivation past linked work orders and preserves the vendor on a raced 409", async ({ page, }) => { - const blockedState = await mockVendorApi(page, { deactivationBlocked: true }); + const confirmState = await mockVendorApi(page, { deactivationBlocked: true }); await page.goto("/vendors"); await page.getByRole("button", { name: "Edit vendor Gateway Plumbing" }).click(); await page.getByRole("switch", { name: "Active status" }).click(); - const blockedDialog = page.getByRole("dialog", { name: "Deactivate Vendor" }); - await expect(blockedDialog).toContainText("WO-501 — Emergency boiler repair"); - await expect(blockedDialog.getByRole("button", { name: /^Deactivate$/ })).toBeDisabled(); - expect(blockedState.deletedId).toBeUndefined(); + const dialog = page.getByRole("dialog", { name: "Deactivate this vendor?" }); + await expect(dialog).toContainText("It still has 1 open work order"); + await expect( + dialog.getByRole("link", { name: /WO-501 — Emergency boiler repair/ }), + ).toHaveAttribute("href", "/workorders/501"); + + // SH-254: the open work orders inform the decision, they no longer block it. + const confirm = dialog.getByRole("button", { name: "Deactivate anyway" }); + await expect(confirm).toBeEnabled(); + await confirm.click(); + + await expect.poll(() => confirmState.deletedId).toBe("1"); + expect(confirmState.deleteConfirmedOpenWorkOrders).toBe(true); - await blockedDialog.getByRole("button", { name: "Cancel" }).click(); await page.unrouteAll({ behavior: "wait" }); const racedState = await mockVendorApi(page, { deleteConflict: true }); @@ -538,13 +550,13 @@ test.describe("Vendor directory prototype parity", () => { await page.getByRole("button", { name: "Edit vendor Gateway Plumbing" }).click(); await page.getByRole("switch", { name: "Active status" }).click(); await page - .getByRole("dialog", { name: "Deactivate Vendor" }) + .getByRole("dialog", { name: "Deactivate this vendor?" }) .getByRole("button", { name: /^Deactivate$/, }) .click(); - await expect(page.getByRole("dialog", { name: "Deactivate Vendor" })).toContainText( + await expect(page.getByRole("dialog", { name: "Deactivate this vendor?" })).toContainText( /open work orders|conflict/i, ); expect(racedState.deletedId).toBeUndefined();