feat: complete Phase 3 — advanced features for Casera web app
Adds sharing (residence share codes, join, user management, .casera file export/import), subscription status with feature comparison, notification preferences with bell icon, profile settings (edit info, change password, theme picker, delete account), onboarding wizard with create/join paths, enhanced dashboard with stats cards, Recharts completion chart, recent activity feed, and task report PDF download. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
"use client";
|
||||
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import * as tasksApi from '@/lib/api/tasks';
|
||||
import type { CreateTaskRequest, UpdateTaskRequest, CreateCompletionRequest } from '@/lib/api/tasks';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Query hooks
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export function useTasks() {
|
||||
return useQuery({
|
||||
queryKey: ['tasks'],
|
||||
queryFn: () => tasksApi.listTasks(),
|
||||
});
|
||||
}
|
||||
|
||||
export function useTasksByResidence(residenceId: number) {
|
||||
return useQuery({
|
||||
queryKey: ['tasks', 'by-residence', residenceId],
|
||||
queryFn: () => tasksApi.getTasksByResidence(residenceId),
|
||||
enabled: !!residenceId,
|
||||
});
|
||||
}
|
||||
|
||||
export function useTask(id: number) {
|
||||
return useQuery({
|
||||
queryKey: ['tasks', id],
|
||||
queryFn: () => tasksApi.getTask(id),
|
||||
enabled: !!id,
|
||||
});
|
||||
}
|
||||
|
||||
export function useKanbanBoard(residenceId: number) {
|
||||
return useQuery({
|
||||
queryKey: ['tasks', 'kanban', residenceId],
|
||||
queryFn: () => tasksApi.getTasksByResidence(residenceId),
|
||||
enabled: !!residenceId,
|
||||
});
|
||||
}
|
||||
|
||||
export function useTaskCompletions(taskId: number) {
|
||||
return useQuery({
|
||||
queryKey: ['tasks', taskId, 'completions'],
|
||||
queryFn: () => tasksApi.getTaskCompletions(taskId),
|
||||
enabled: !!taskId,
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Mutation hooks
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export function useCreateTask() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: CreateTaskRequest) => tasksApi.createTask(data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['tasks'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['residences'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateTask(id: number) {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (data: UpdateTaskRequest) => tasksApi.updateTask(id, data),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['tasks'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['tasks', id] });
|
||||
queryClient.invalidateQueries({ queryKey: ['residences'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteTask() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => tasksApi.deleteTask(id),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['tasks'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['residences'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useMarkInProgress() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => tasksApi.markInProgress(id),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['tasks'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useCancelTask() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => tasksApi.cancelTask(id),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['tasks'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useArchiveTask() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: number) => tasksApi.archiveTask(id),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['tasks'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateCompletion() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
data,
|
||||
images,
|
||||
}: {
|
||||
data: CreateCompletionRequest;
|
||||
images: File[];
|
||||
}) => {
|
||||
if (images.length > 0) {
|
||||
return tasksApi.createCompletionWithImages(
|
||||
{
|
||||
task_id: data.task_id,
|
||||
notes: data.notes,
|
||||
actual_cost: data.actual_cost,
|
||||
completed_at: data.completed_at,
|
||||
},
|
||||
images,
|
||||
);
|
||||
}
|
||||
return tasksApi.createCompletion(data);
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['tasks'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['residences'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user