Initial commit

This commit is contained in:
Yaojia Wang
2025-11-03 00:04:07 +01:00
parent 34b701de48
commit 097300e8ec
37 changed files with 3473 additions and 109 deletions

27
lib/hooks/use-kanban.ts Normal file
View File

@@ -0,0 +1,27 @@
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { projectsApi } from '@/lib/api/projects';
import type { KanbanBoard } from '@/types/kanban';
import { api } from '@/lib/api/client';
export function useKanbanBoard(projectId: string) {
return useQuery<KanbanBoard>({
queryKey: ['projects', projectId, 'kanban'],
queryFn: () => projectsApi.getKanban(projectId),
enabled: !!projectId,
staleTime: 2 * 60 * 1000, // 2 minutes
});
}
export function useUpdateTaskStatus() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ taskId, newStatus }: { taskId: string; newStatus: string }) =>
api.patch(`/tasks/${taskId}/status`, { status: newStatus }),
onSuccess: (_, { taskId }) => {
// Invalidate kanban board queries to refetch
queryClient.invalidateQueries({ queryKey: ['kanban'] });
queryClient.invalidateQueries({ queryKey: ['tasks', taskId] });
},
});
}

79
lib/hooks/use-projects.ts Normal file
View File

@@ -0,0 +1,79 @@
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { projectsApi } from '@/lib/api/projects';
import type { Project, CreateProjectDto, UpdateProjectDto } from '@/types/project';
export function useProjects(page = 1, pageSize = 20) {
return useQuery<Project[]>({
queryKey: ['projects', page, pageSize],
queryFn: () => projectsApi.getAll(page, pageSize),
staleTime: 5 * 60 * 1000, // 5 minutes
});
}
export function useProject(id: string) {
return useQuery<Project>({
queryKey: ['projects', id],
queryFn: () => projectsApi.getById(id),
enabled: !!id,
});
}
export function useCreateProject() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (data: CreateProjectDto) => projectsApi.create(data),
onSuccess: (newProject) => {
// Invalidate and refetch projects list
queryClient.invalidateQueries({ queryKey: ['projects'] });
// Optimistically update cache
queryClient.setQueryData<Project[]>(['projects'], (old) =>
old ? [...old, newProject] : [newProject]
);
},
});
}
export function useUpdateProject(id: string) {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (data: UpdateProjectDto) => projectsApi.update(id, data),
onMutate: async (updatedData) => {
// Optimistic update
await queryClient.cancelQueries({ queryKey: ['projects', id] });
const previousProject = queryClient.getQueryData<Project>(['projects', id]);
queryClient.setQueryData<Project>(['projects', id], (old) => ({
...old!,
...updatedData,
}));
return { previousProject };
},
onError: (err, variables, context) => {
// Rollback on error
if (context?.previousProject) {
queryClient.setQueryData(['projects', id], context.previousProject);
}
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['projects', id] });
queryClient.invalidateQueries({ queryKey: ['projects'] });
},
});
}
export function useDeleteProject() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (id: string) => projectsApi.delete(id),
onSuccess: (_, id) => {
queryClient.invalidateQueries({ queryKey: ['projects'] });
queryClient.removeQueries({ queryKey: ['projects', id] });
},
});
}