mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-07 16:19:01 +00:00
fix(vendors): scope facets to directory status
This commit is contained in:
parent
db3ae2ca3f
commit
5eed5ff4a1
6 changed files with 126 additions and 9 deletions
2
src/app/(protected)/vendors/index.tsx
vendored
2
src/app/(protected)/vendors/index.tsx
vendored
|
|
@ -105,7 +105,7 @@ export default function VendorsListPage() {
|
|||
|
||||
const debouncedSearch = useDebounce(search, 300);
|
||||
const deleteVendor = useDeleteVendor();
|
||||
const { data: facets } = useVendorFacets();
|
||||
const { data: facets } = useVendorFacets(isActive);
|
||||
const { data: trades = [] } = useDropdownOptionsByCategory("Trade");
|
||||
const tradeOptions = useMemo(() => trades.map((trade) => trade.value), [trades]);
|
||||
|
||||
|
|
|
|||
18
src/domain/vendors/api/vendors-api.ts
vendored
18
src/domain/vendors/api/vendors-api.ts
vendored
|
|
@ -28,6 +28,18 @@ function pushRepeated(
|
|||
});
|
||||
}
|
||||
|
||||
export interface VendorFacetsParams {
|
||||
isActive?: boolean;
|
||||
}
|
||||
|
||||
function buildFacetsParams(params?: VendorFacetsParams): Record<string, string> {
|
||||
const searchParams: Record<string, string> = {};
|
||||
if (params?.isActive !== undefined) {
|
||||
searchParams.isActive = String(params.isActive);
|
||||
}
|
||||
return searchParams;
|
||||
}
|
||||
|
||||
function buildListParams(params: VendorsListParams): Record<string, string | number> {
|
||||
const searchParams: Record<string, string | number> = {
|
||||
search: params.search ?? "",
|
||||
|
|
@ -61,8 +73,10 @@ export const vendorsApi = {
|
|||
};
|
||||
},
|
||||
|
||||
getFacets: async (): Promise<VendorFacets> => {
|
||||
const data = await apiGet<unknown>(API_PATHS.vendor.facets);
|
||||
getFacets: async (params?: VendorFacetsParams): Promise<VendorFacets> => {
|
||||
const data = await apiGet<unknown>(API_PATHS.vendor.facets, {
|
||||
searchParams: buildFacetsParams(params),
|
||||
});
|
||||
return mapVendorFacets(handleApiResponse(data));
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
import { useQuery, type UseQueryResult } from "@tanstack/react-query";
|
||||
import { vendorsApi } from "@/domain/vendors/api/vendors-api";
|
||||
import { vendorsApi, type VendorFacetsParams } from "@/domain/vendors/api/vendors-api";
|
||||
import type { VendorFacets } from "@/domain/vendors/types/vendor";
|
||||
import { queryKeys } from "@/infra/query-key/query-key";
|
||||
|
||||
export function useVendorFacets(): UseQueryResult<VendorFacets, Error> {
|
||||
export function useVendorFacets(isActive?: boolean): UseQueryResult<VendorFacets, Error> {
|
||||
const params: VendorFacetsParams | undefined = isActive !== undefined ? { isActive } : undefined;
|
||||
return useQuery({
|
||||
queryKey: queryKeys.vendors.facets(),
|
||||
queryFn: () => vendorsApi.getFacets(),
|
||||
queryKey: queryKeys.vendors.facets(isActive),
|
||||
queryFn: () => vendorsApi.getFacets(params),
|
||||
staleTime: 60_000,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ export const queryKeys = {
|
|||
list: (params?: Record<string, unknown>) =>
|
||||
[...queryKeys.vendors.all, "list", params ?? {}] as const,
|
||||
detail: (id: string | number) => [...queryKeys.vendors.all, "detail", id] as const,
|
||||
facets: () => [...queryKeys.vendors.all, "facets"] as const,
|
||||
facets: (isActive?: boolean) => [...queryKeys.vendors.all, "facets", isActive ?? null] as const,
|
||||
portalToken: (vendorId: string | number) =>
|
||||
[...queryKeys.vendors.all, "portalToken", vendorId] as const,
|
||||
},
|
||||
|
|
|
|||
18
src/test/domain/vendors/api/vendors-api.test.ts
vendored
18
src/test/domain/vendors/api/vendors-api.test.ts
vendored
|
|
@ -88,7 +88,7 @@ describe("vendorsApi.getList", () => {
|
|||
|
||||
const facets = await vendorsApi.getFacets();
|
||||
|
||||
expect(apiGet).toHaveBeenCalledWith(API_PATHS.vendor.facets);
|
||||
expect(apiGet).toHaveBeenCalledWith(API_PATHS.vendor.facets, { searchParams: {} });
|
||||
expect(facets.companies[0]).toMatchObject({
|
||||
name: "Gateway Plumbing",
|
||||
companyPhone: "314-555-0100",
|
||||
|
|
@ -98,6 +98,22 @@ describe("vendorsApi.getList", () => {
|
|||
expect(facets.jobBuckets[0]).toEqual({ id: "50-99", label: "50–99" });
|
||||
});
|
||||
|
||||
it("serializes the requested facet scope as an isActive query parameter", async () => {
|
||||
apiGet.mockResolvedValue({
|
||||
data: { companies: [], trades: [], locations: [], jobBuckets: [] },
|
||||
});
|
||||
|
||||
await vendorsApi.getFacets({ isActive: true });
|
||||
await vendorsApi.getFacets({ isActive: false });
|
||||
|
||||
expect(apiGet).toHaveBeenNthCalledWith(1, API_PATHS.vendor.facets, {
|
||||
searchParams: { isActive: "true" },
|
||||
});
|
||||
expect(apiGet).toHaveBeenNthCalledWith(2, API_PATHS.vendor.facets, {
|
||||
searchParams: { isActive: "false" },
|
||||
});
|
||||
});
|
||||
|
||||
it("sends all editable prototype fields when creating and updating", async () => {
|
||||
const values = {
|
||||
companyName: "Gateway Plumbing",
|
||||
|
|
|
|||
86
src/test/domain/vendors/use-cases/use-vendor-facets.test.tsx
vendored
Normal file
86
src/test/domain/vendors/use-cases/use-vendor-facets.test.tsx
vendored
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { renderHook, waitFor } from "@testing-library/react";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
const getFacets = vi.fn();
|
||||
|
||||
vi.mock("@/domain/vendors/api/vendors-api", () => ({
|
||||
vendorsApi: { getFacets: (...args: unknown[]) => getFacets(...args) },
|
||||
}));
|
||||
|
||||
import { useVendorFacets } from "@/domain/vendors/use-cases/use-vendor-facets";
|
||||
import { queryKeys } from "@/infra/query-key/query-key";
|
||||
|
||||
const emptyFacets = { companies: [], trades: [], locations: [], jobBuckets: [] };
|
||||
const activeFacets = { ...emptyFacets, companies: [{ name: "Active Co" }] };
|
||||
const inactiveFacets = { ...emptyFacets, companies: [{ name: "Inactive Co" }] };
|
||||
|
||||
function createClient(): QueryClient {
|
||||
return new QueryClient({
|
||||
defaultOptions: { queries: { retry: false } },
|
||||
});
|
||||
}
|
||||
|
||||
function makeWrapper(client: QueryClient) {
|
||||
return function Wrapper({ children }: { children: ReactNode }) {
|
||||
return <QueryClientProvider client={client}>{children}</QueryClientProvider>;
|
||||
};
|
||||
}
|
||||
|
||||
describe("useVendorFacets", () => {
|
||||
beforeEach(() => {
|
||||
getFacets.mockReset();
|
||||
});
|
||||
|
||||
it("switches facet requests true/false as the tab scope changes", async () => {
|
||||
getFacets.mockResolvedValue(activeFacets);
|
||||
const { rerender } = renderHook(({ isActive }) => useVendorFacets(isActive), {
|
||||
initialProps: { isActive: true as boolean | undefined },
|
||||
wrapper: makeWrapper(createClient()),
|
||||
});
|
||||
|
||||
await waitFor(() => expect(getFacets).toHaveBeenCalledWith({ isActive: true }));
|
||||
|
||||
getFacets.mockResolvedValue(inactiveFacets);
|
||||
rerender({ isActive: false });
|
||||
await waitFor(() => expect(getFacets).toHaveBeenCalledWith({ isActive: false }));
|
||||
|
||||
getFacets.mockResolvedValue(activeFacets);
|
||||
rerender({ isActive: undefined });
|
||||
await waitFor(() => expect(getFacets).toHaveBeenCalledWith(undefined));
|
||||
});
|
||||
|
||||
it("scopes the cache so active, inactive, and unscoped keys cannot collide", () => {
|
||||
const active = queryKeys.vendors.facets(true);
|
||||
const inactive = queryKeys.vendors.facets(false);
|
||||
const unscoped = queryKeys.vendors.facets();
|
||||
|
||||
expect(active).not.toEqual(inactive);
|
||||
expect(active).not.toEqual(unscoped);
|
||||
expect(inactive).not.toEqual(unscoped);
|
||||
expect(active[active.length - 1]).toBe(true);
|
||||
expect(inactive[inactive.length - 1]).toBe(false);
|
||||
expect(unscoped[unscoped.length - 1]).toBeNull();
|
||||
});
|
||||
|
||||
it("reuses the cached facet entry when returning to a previous scope", async () => {
|
||||
getFacets.mockResolvedValue(activeFacets);
|
||||
const client = createClient();
|
||||
const { result, rerender } = renderHook(({ isActive }) => useVendorFacets(isActive), {
|
||||
initialProps: { isActive: true as boolean | undefined },
|
||||
wrapper: makeWrapper(client),
|
||||
});
|
||||
|
||||
await waitFor(() => expect(getFacets).toHaveBeenCalledTimes(1));
|
||||
|
||||
getFacets.mockResolvedValue(inactiveFacets);
|
||||
rerender({ isActive: false });
|
||||
await waitFor(() => expect(getFacets).toHaveBeenCalledTimes(2));
|
||||
|
||||
getFacets.mockClear();
|
||||
rerender({ isActive: true });
|
||||
await waitFor(() => expect(result.current.data).toEqual(activeFacets));
|
||||
expect(getFacets).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue