import { useQuery } from '@tanstack/react-query'; import { graphqlRequest } from '@/gql/graphql'; import { useTranslation } from 'react-i18next'; import { useSelectedProjectId } from '@/stores/projectStore'; import { useErrorHandler } from '@/hooks/use-error-handler'; import { useRequestPermissions } from '../../../hooks/useRequestPermissions'; import { Request, RequestConnection, RequestExecutionConnection, requestConnectionSchema, requestExecutionConnectionSchema, requestSchema, } from './schema'; // Dynamic GraphQL query builder function buildRequestsQuery(permissions: { canViewApiKeys: boolean; canViewChannels: boolean }) { const apiKeyFields = permissions.canViewApiKeys ? ` apiKey { id name }` : ''; const channelFields = permissions.canViewChannels ? ` channel { id name }` : ''; return ` query GetRequests( $first: Int $after: Cursor $last: Int $before: Cursor $orderBy: RequestOrder $where: RequestWhereInput ) { requests(first: $first, after: $after, last: $last, before: $before, orderBy: $orderBy, where: $where) { edges { node { id createdAt updatedAt${apiKeyFields}${channelFields} source modelID stream status clientIP metricsLatencyMs metricsFirstTokenLatencyMs executions(first: 10, orderBy: { field: CREATED_AT, direction: DESC }) { edges { node { modelID status channel { id name } } cursor } pageInfo { hasNextPage hasPreviousPage startCursor endCursor } totalCount } usageLogs(first: 1) { edges { node { id promptTokens completionTokens totalTokens promptCachedTokens promptWriteCachedTokens totalCost } } } } cursor } pageInfo { hasNextPage hasPreviousPage startCursor endCursor } totalCount } } `; } function buildRequestDetailQuery(permissions: { canViewApiKeys: boolean; canViewChannels: boolean }) { const apiKeyFields = permissions.canViewApiKeys ? ` apiKey { id name }` : ''; const requestChannelFields = permissions.canViewChannels ? ` channel { id name }` : ''; return ` query GetRequestDetail($id: ID!) { node(id: $id) { ... on Request { id createdAt updatedAt${apiKeyFields}${requestChannelFields} source modelID stream clientIP projectID dataStorageID requestHeaders requestBody responseBody responseChunks status format usageLogs(first: 1) { edges { node { id promptTokens completionTokens totalTokens promptCachedTokens promptWriteCachedTokens totalCost } } } } } } `; } function buildRequestExecutionsQuery(permissions: { canViewChannels: boolean }) { const channelFields = permissions.canViewChannels ? ` channel { id name type baseURL }` : ''; return ` query GetRequestExecutions( $requestID: ID! $first: Int $after: Cursor $orderBy: RequestExecutionOrder $where: RequestExecutionWhereInput ) { node(id: $requestID) { ... on Request { executions(first: $first, after: $after, orderBy: $orderBy, where: $where) { edges { node { id createdAt updatedAt requestID${channelFields} modelID projectID dataStorageID requestHeaders requestBody responseBody responseChunks errorMessage status format stream metricsFirstTokenLatencyMs } cursor } pageInfo { hasNextPage hasPreviousPage startCursor endCursor } totalCount } } } } `; } // Query hooks export function useRequests(variables?: { first?: number; after?: string; last?: number; before?: string; orderBy?: { field: 'CREATED_AT'; direction: 'ASC' | 'DESC' }; where?: { status?: string; source?: string; channelID?: string; channelIDIn?: string[]; statusIn?: string[]; sourceIn?: string[]; projectID?: string; [key: string]: any; }; }) { const { handleError } = useErrorHandler(); const { t } = useTranslation(); const permissions = useRequestPermissions(); const selectedProjectId = useSelectedProjectId(); return useQuery({ queryKey: ['requests', variables, permissions, selectedProjectId], queryFn: async () => { try { const query = buildRequestsQuery(permissions); const headers = selectedProjectId ? { 'X-Project-ID': selectedProjectId } : undefined; // Add project filter if project is selected const finalVariables = { ...variables, where: { ...variables?.where, ...(selectedProjectId && { projectID: selectedProjectId }), }, }; const data = await graphqlRequest<{ requests: RequestConnection }>(query, finalVariables, headers); return requestConnectionSchema.parse(data?.requests); } catch (error) { handleError(error, t('requests.errors.loadRequestsFailed')); throw error; } }, enabled: true, // Requests can be queried without project selection for admin users }); } export function useRequest(id: string) { const { handleError } = useErrorHandler(); const { t } = useTranslation(); const permissions = useRequestPermissions(); const selectedProjectId = useSelectedProjectId(); return useQuery({ queryKey: ['request', id, permissions, selectedProjectId], queryFn: async () => { try { const query = buildRequestDetailQuery(permissions); const headers = selectedProjectId ? { 'X-Project-ID': selectedProjectId } : undefined; const data = await graphqlRequest<{ node: Request }>(query, { id }, headers); if (!data.node) { throw new Error('Request not found'); } return requestSchema.parse(data.node); } catch (error) { handleError(error, t('requests.errors.loadRequestDetailFailed')); throw error; } }, enabled: !!id, }); } export function useRequestExecutions( requestID: string, variables?: { first?: number; after?: string; orderBy?: { field: 'CREATED_AT'; direction: 'ASC' | 'DESC' }; where?: Record; } ) { const permissions = useRequestPermissions(); const selectedProjectId = useSelectedProjectId(); return useQuery({ queryKey: ['request-executions', requestID, variables, permissions, selectedProjectId], queryFn: async () => { const query = buildRequestExecutionsQuery(permissions); const headers = selectedProjectId ? { 'X-Project-ID': selectedProjectId } : undefined; const finalVariables = { requestID, ...variables, }; const data = await graphqlRequest<{ node: { executions: RequestExecutionConnection } }>(query, finalVariables, headers); return requestExecutionConnectionSchema.parse(data?.node?.executions); }, enabled: !!requestID, }); }