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 { ThreadConnection, ThreadDetail, threadConnectionSchema, threadDetailSchema } from './schema'; type ThreadOrderField = 'CREATED_AT' | 'UPDATED_AT'; type OrderDirection = 'ASC' | 'DESC'; type ThreadOrder = { field: ThreadOrderField; direction: OrderDirection; }; type ThreadWhereInput = { projectID?: string; threadID?: string; threadIDContains?: string; [key: string]: unknown; }; function buildThreadsQuery() { return ` query GetThreads( $first: Int $after: Cursor $orderBy: ThreadOrder $where: ThreadWhereInput ) { threads(first: $first, after: $after, orderBy: $orderBy, where: $where) { edges { node { id threadID createdAt updatedAt project { id name } tracesSummary: traces(first: 1) { totalCount } firstUserQuery } cursor } pageInfo { hasNextPage hasPreviousPage startCursor endCursor } totalCount } } `; } function buildThreadDetailQuery() { return ` query GetThreadDetail( $id: ID! $tracesFirst: Int $tracesAfter: Cursor $traceOrderBy: TraceOrder ) { node(id: $id) { ... on Thread { id threadID createdAt updatedAt usageMetadata { totalInputTokens totalOutputTokens totalTokens totalCost totalCachedTokens totalCachedWriteTokens } project { id name } tracesSummary: traces(first: 1) { totalCount } tracesConnection: traces(first: $tracesFirst, after: $tracesAfter, orderBy: $traceOrderBy) { edges { node { id traceID createdAt updatedAt project { id name } thread { id threadID } requests(where: { status: completed }) { totalCount } firstUserQuery firstText } cursor } pageInfo { hasNextPage hasPreviousPage startCursor endCursor } totalCount } } } } `; } export function useThreads(variables?: { first?: number; after?: string; orderBy?: ThreadOrder; where?: ThreadWhereInput }) { const { t } = useTranslation(); const { handleError } = useErrorHandler(); const selectedProjectId = useSelectedProjectId(); return useQuery({ queryKey: ['threads', variables, selectedProjectId], queryFn: async () => { try { const query = buildThreadsQuery(); const headers = selectedProjectId ? { 'X-Project-ID': selectedProjectId } : undefined; const finalVariables = { ...variables, where: { ...variables?.where, ...(selectedProjectId && { projectID: selectedProjectId }), }, }; const data = await graphqlRequest<{ threads: ThreadConnection }>(query, finalVariables, headers); return threadConnectionSchema.parse(data?.threads); } catch (error) { handleError(error, t('threads.errors.fetchList')); throw error; } }, enabled: true, }); } export function useThreadDetail({ id, tracesFirst, tracesAfter, traceOrderBy, }: { id: string; tracesFirst?: number; tracesAfter?: string; traceOrderBy?: { field: 'CREATED_AT'; direction: OrderDirection; }; }) { const { t } = useTranslation(); const { handleError } = useErrorHandler(); const selectedProjectId = useSelectedProjectId(); return useQuery({ queryKey: ['thread-detail', id, tracesFirst, tracesAfter, traceOrderBy, selectedProjectId], queryFn: async () => { try { const query = buildThreadDetailQuery(); const headers = selectedProjectId ? { 'X-Project-ID': selectedProjectId } : undefined; const variables = { id, tracesFirst, tracesAfter, traceOrderBy, }; const data = await graphqlRequest<{ node?: ThreadDetail | null }>(query, variables, headers); if (!data?.node) { throw new Error(t('threads.errors.notFound')); } return threadDetailSchema.parse(data.node); } catch (error) { handleError(error, t('threads.errors.fetchDetail')); throw error; } }, enabled: !!id, }); }