Spaces:
Paused
Paused
| import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; | |
| import { useTranslation } from 'react-i18next'; | |
| import { graphqlRequest } from '@/gql/graphql'; | |
| import { toast } from 'sonner'; | |
| import i18n from '@/lib/i18n'; | |
| import { useErrorHandler } from '@/hooks/use-error-handler'; | |
| import { Project, ProjectConnection, CreateProjectInput, UpdateProjectInput, projectConnectionSchema, projectSchema } from './schema'; | |
| // GraphQL queries and mutations | |
| const PROJECTS_QUERY = ` | |
| query GetProjects($first: Int, $after: Cursor, $orderBy: ProjectOrder, $where: ProjectWhereInput) { | |
| projects(first: $first, after: $after, orderBy: $orderBy, where: $where) { | |
| edges { | |
| node { | |
| id | |
| createdAt | |
| updatedAt | |
| name | |
| description | |
| status | |
| } | |
| cursor | |
| } | |
| pageInfo { | |
| hasNextPage | |
| hasPreviousPage | |
| startCursor | |
| endCursor | |
| } | |
| totalCount | |
| } | |
| } | |
| `; | |
| const CREATE_PROJECT_MUTATION = ` | |
| mutation CreateProject($input: CreateProjectInput!) { | |
| createProject(input: $input) { | |
| id | |
| name | |
| description | |
| status | |
| createdAt | |
| updatedAt | |
| } | |
| } | |
| `; | |
| const UPDATE_PROJECT_MUTATION = ` | |
| mutation UpdateProject($id: ID!, $input: UpdateProjectInput!) { | |
| updateProject(id: $id, input: $input) { | |
| id | |
| name | |
| description | |
| status | |
| createdAt | |
| updatedAt | |
| } | |
| } | |
| `; | |
| const UPDATE_PROJECT_STATUS_MUTATION = ` | |
| mutation UpdateProjectStatus($id: ID!, $status: ProjectStatus!) { | |
| updateProjectStatus(id: $id, status: $status) { | |
| id | |
| name | |
| description | |
| status | |
| createdAt | |
| updatedAt | |
| } | |
| } | |
| `; | |
| const MY_PROJECTS_QUERY = ` | |
| query MyProjects { | |
| myProjects { | |
| id | |
| name | |
| description | |
| status | |
| createdAt | |
| updatedAt | |
| } | |
| } | |
| `; | |
| // Query hooks | |
| export function useProjects( | |
| variables: { | |
| first?: number; | |
| after?: string; | |
| orderBy?: { field: 'CREATED_AT'; direction: 'ASC' | 'DESC' }; | |
| where?: any; | |
| } = {} | |
| ) { | |
| const { handleError } = useErrorHandler(); | |
| const { t } = useTranslation(); | |
| const queryVariables = { | |
| ...variables, | |
| orderBy: variables.orderBy || { field: 'CREATED_AT', direction: 'DESC' }, | |
| }; | |
| return useQuery({ | |
| queryKey: ['projects', queryVariables], | |
| queryFn: async () => { | |
| try { | |
| const data = await graphqlRequest<{ projects: ProjectConnection }>(PROJECTS_QUERY, queryVariables); | |
| return projectConnectionSchema.parse(data?.projects); | |
| } catch (error) { | |
| handleError(error, t('projects.errors.loadProjectsFailed')); | |
| throw error; | |
| } | |
| }, | |
| }); | |
| } | |
| export function useProject(id: string) { | |
| const { handleError } = useErrorHandler(); | |
| const { t } = useTranslation(); | |
| return useQuery({ | |
| queryKey: ['project', id], | |
| queryFn: async () => { | |
| try { | |
| const data = await graphqlRequest<{ projects: ProjectConnection }>(PROJECTS_QUERY, { where: { id } }); | |
| const project = data.projects.edges[0]?.node; | |
| if (!project) { | |
| throw new Error('Project not found'); | |
| } | |
| return projectSchema.parse(project); | |
| } catch (error) { | |
| handleError(error, t('projects.errors.loadProjectDetailFailed')); | |
| throw error; | |
| } | |
| }, | |
| enabled: !!id, | |
| }); | |
| } | |
| export function useMyProjects() { | |
| const { handleError } = useErrorHandler(); | |
| const { t } = useTranslation(); | |
| return useQuery({ | |
| queryKey: ['myProjects'], | |
| queryFn: async () => { | |
| try { | |
| const data = await graphqlRequest<{ myProjects: Project[] }>(MY_PROJECTS_QUERY); | |
| if (!data || !data.myProjects) { | |
| return []; | |
| } | |
| // myProjects 直接返回项目数组,不是 connection 格式 | |
| const projects = data.myProjects.map((project) => projectSchema.parse(project)); | |
| return projects; | |
| } catch (error) { | |
| handleError(error, t('projects.errors.loadMyProjectsFailed')); | |
| return []; | |
| } | |
| }, | |
| }); | |
| } | |
| // Mutation hooks | |
| export function useCreateProject() { | |
| const queryClient = useQueryClient(); | |
| const { handleError } = useErrorHandler(); | |
| return useMutation({ | |
| mutationFn: async (input: CreateProjectInput) => { | |
| try { | |
| const data = await graphqlRequest<{ createProject: Project }>(CREATE_PROJECT_MUTATION, { input }); | |
| return projectSchema.parse(data.createProject); | |
| } catch (error) { | |
| handleError(error, i18n.t('projects.errors.createProjectFailed')); | |
| throw error; | |
| } | |
| }, | |
| onSuccess: () => { | |
| queryClient.invalidateQueries({ queryKey: ['projects'] }); | |
| queryClient.invalidateQueries({ queryKey: ['myProjects'] }); | |
| toast.success(i18n.t('common.success.projectCreated')); | |
| }, | |
| }); | |
| } | |
| export function useUpdateProject() { | |
| const queryClient = useQueryClient(); | |
| const { handleError } = useErrorHandler(); | |
| return useMutation({ | |
| mutationFn: async ({ id, input }: { id: string; input: UpdateProjectInput }) => { | |
| try { | |
| const data = await graphqlRequest<{ updateProject: Project }>(UPDATE_PROJECT_MUTATION, { id, input }); | |
| return projectSchema.parse(data.updateProject); | |
| } catch (error) { | |
| handleError(error, i18n.t('projects.errors.updateProjectFailed')); | |
| throw error; | |
| } | |
| }, | |
| onSuccess: () => { | |
| queryClient.invalidateQueries({ queryKey: ['projects'] }); | |
| queryClient.invalidateQueries({ queryKey: ['project'] }); | |
| queryClient.invalidateQueries({ queryKey: ['myProjects'] }); | |
| toast.success(i18n.t('common.success.projectUpdated')); | |
| }, | |
| }); | |
| } | |
| export function useArchiveProject() { | |
| const queryClient = useQueryClient(); | |
| const { handleError } = useErrorHandler(); | |
| return useMutation({ | |
| mutationFn: async (id: string) => { | |
| try { | |
| const data = await graphqlRequest<{ updateProjectStatus: Project }>(UPDATE_PROJECT_STATUS_MUTATION, { | |
| id, | |
| status: 'archived', | |
| }); | |
| return projectSchema.parse(data.updateProjectStatus); | |
| } catch (error) { | |
| handleError(error, i18n.t('projects.errors.archiveProjectFailed')); | |
| throw error; | |
| } | |
| }, | |
| onSuccess: () => { | |
| queryClient.invalidateQueries({ queryKey: ['projects'] }); | |
| queryClient.invalidateQueries({ queryKey: ['project'] }); | |
| queryClient.invalidateQueries({ queryKey: ['myProjects'] }); | |
| toast.success(i18n.t('common.success.projectArchived')); | |
| }, | |
| }); | |
| } | |
| export function useActivateProject() { | |
| const queryClient = useQueryClient(); | |
| const { handleError } = useErrorHandler(); | |
| return useMutation({ | |
| mutationFn: async (id: string) => { | |
| try { | |
| const data = await graphqlRequest<{ updateProjectStatus: Project }>(UPDATE_PROJECT_STATUS_MUTATION, { | |
| id, | |
| status: 'active', | |
| }); | |
| return projectSchema.parse(data.updateProjectStatus); | |
| } catch (error) { | |
| handleError(error, i18n.t('projects.errors.activateProjectFailed')); | |
| throw error; | |
| } | |
| }, | |
| onSuccess: () => { | |
| queryClient.invalidateQueries({ queryKey: ['projects'] }); | |
| queryClient.invalidateQueries({ queryKey: ['project'] }); | |
| queryClient.invalidateQueries({ queryKey: ['myProjects'] }); | |
| toast.success(i18n.t('common.success.projectActivated')); | |
| }, | |
| }); | |
| } | |