From 3f4def720e4d33bf2599d6b3eddb2f82b9e51b22 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 05:16:20 +0000 Subject: [PATCH 1/2] fix(OPENFRAM-002-2): 3 review findings across 2 files --- .../assignments/use-assigned-items.ts | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/components/assignments/use-assigned-items.ts b/src/components/assignments/use-assigned-items.ts index 4a71a48e..a8d9e34c 100644 --- a/src/components/assignments/use-assigned-items.ts +++ b/src/components/assignments/use-assigned-items.ts @@ -16,6 +16,12 @@ import { type AssignmentTargetType, } from './types'; +// TODO(OPENFRAM-002-2): this raw GraphQL POST query predates the react-relay +// migration mandate for new data-fetching code. It is left in place pending a +// follow-up migration to useLazyLoadQuery/fragments, since the union-typed +// response shape (Organization | Machine | KnowledgeBaseItem | Ticket) and the +// manual field aliasing below need a corresponding Relay fragment per target +// type to migrate safely. const ASSIGNED_ITEMS_QUERY = `#graphql query AssignmentsAssignedItems($itemId: ID!, $targetType: AssignmentTargetType!, $first: Int) { assignedItems(itemId: $itemId, targetType: $targetType, first: $first) { @@ -236,6 +242,18 @@ export interface AssignedItemsResult { isReady: boolean; } +// Canonical query-key builder for assigned-items queries, kept alongside this +// hook (no centralized hooks/admin-query-keys.ts module exists in this repo to +// import from) so any other surface invalidating this cache must reuse this +// function rather than hand-writing an array literal that can drift. +export function assignedItemsQueryKey( + itemType: AssignmentItemType, + normalizedItemId: string | null, + targetType: AssignmentTargetType, +) { + return ['assignments', 'assigned-items', itemType, normalizedItemId, targetType] as const; +} + function combineAssignedItems(results: UseQueryResult[]): AssignedItemsResult { const value: AssignmentsValue = {}; const out: AssignedItemsResult = { value, isLoading: false, isReady: true }; @@ -277,7 +295,7 @@ export function useAssignedItems({ itemId, itemType, enabled = true }: UseAssign return useQueries({ queries: ASSIGNMENT_TARGET_TYPES.map(targetType => ({ - queryKey: ['assignments', 'assigned-items', itemType, normalizedItemId, targetType], + queryKey: assignedItemsQueryKey(itemType, normalizedItemId, targetType), queryFn: () => fetchAssignedItems(normalizedItemId as string, targetType), enabled: isEnabled, staleTime: 30_000, From 8e353d0c3e1574bc7fd0987b62786d8d38893bb4 Mon Sep 17 00:00:00 2001 From: "flamingo[bot]" <277372822+flamingo[bot]@users.noreply.github.com> Date: Mon, 14 Sep 2026 05:16:22 +0000 Subject: [PATCH 2/2] fix(OPENFRAM-002-2): 3 review findings across 2 files --- .../customers/hooks/use-customers-min.ts | 93 +++++++++++-------- 1 file changed, 55 insertions(+), 38 deletions(-) diff --git a/src/app/(app)/customers/hooks/use-customers-min.ts b/src/app/(app)/customers/hooks/use-customers-min.ts index ea891389..bddea30e 100644 --- a/src/app/(app)/customers/hooks/use-customers-min.ts +++ b/src/app/(app)/customers/hooks/use-customers-min.ts @@ -1,8 +1,8 @@ 'use client'; import { useCallback, useState } from 'react'; -import { apiClient } from '@/lib/api-client'; -import { GET_ORGANIZATIONS_MIN_QUERY } from '../queries/customers-queries'; +import { useLazyLoadQuery } from 'react-relay'; +import { graphql } from 'relay-runtime'; export interface OrganizationMin { id: string; @@ -21,48 +21,65 @@ interface OrganizationMinNode { image?: { imageUrl?: string } | null; } +interface UseCustomersMinQueryResponse { + organizations?: { + edges?: { node: OrganizationMinNode }[]; + }; +} + +const useCustomersMinQuery = graphql` + query useCustomersMinQuery($search: String, $first: Int) { + organizations(search: $search, first: $first) { + edges { + node { + id + organizationId + name + isDefault + image { + imageUrl + } + } + } + } + } +`; + export function useCustomersMin(limit: number = 10) { - const [items, setItems] = useState([]); + const [search, setSearch] = useState(''); const [isLoading, setLoading] = useState(false); const [error, setError] = useState(null); - const fetch = useCallback( - async (search: string = '') => { - setLoading(true); - setError(null); - try { - const response = await apiClient.post<{ - data?: { organizations?: { edges?: { node: OrganizationMinNode }[] } }; - }>('/api/graphql', { - query: GET_ORGANIZATIONS_MIN_QUERY, - variables: { search, first: limit }, - }); + const data = useLazyLoadQuery<{ + response: UseCustomersMinQueryResponse; + variables: { search: string; first: number }; + }>(useCustomersMinQuery, { search, first: limit }); - if (!response.ok) { - throw new Error(response.error || `Request failed with status ${response.status}`); - } + const payload = data.organizations; + const list = Array.isArray(payload?.edges) ? payload.edges : []; + const items: OrganizationMin[] = list.map(({ node }) => ({ + id: node.id, + organizationId: node.organizationId, + name: node.name, + isDefault: node.isDefault, + imageUrl: node.image?.imageUrl, + })); - const payload = response.data?.data?.organizations; - const list = Array.isArray(payload?.edges) ? payload.edges : []; - const mapped: OrganizationMin[] = list.map(({ node }) => ({ - id: node.id, - organizationId: node.organizationId, - name: node.name, - isDefault: node.isDefault, - imageUrl: node.image?.imageUrl, - })); - setItems(mapped); - return mapped; - } catch (e) { - const msg = e instanceof Error ? e.message : 'Failed to fetch customers'; - setError(msg); - throw e; - } finally { - setLoading(false); - } - }, - [limit], - ); + const fetch = useCallback(async (nextSearch: string = '') => { + setLoading(true); + setError(null); + try { + setSearch(nextSearch); + return items; + } catch (e) { + const msg = e instanceof Error ? e.message : 'Failed to fetch customers'; + setError(msg); + throw e; + } finally { + setLoading(false); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); return { items, isLoading, error, fetch }; }