feat(documents): useDocumentFolders hook + mutations
Wraps the folder tree fetch in TanStack with a 30s staleTime, and provides create / rename / move / delete / move-document mutations that invalidate the relevant query keys. buildFolderPaths flattens the tree into ' / '-separated path strings for picker dropdowns. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
84
src/hooks/use-document-folders.ts
Normal file
84
src/hooks/use-document-folders.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
'use client';
|
||||
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
|
||||
import { apiFetch } from '@/lib/api/client';
|
||||
import type { DocumentFolder } from '@/lib/db/schema/documents';
|
||||
|
||||
export interface FolderNode extends DocumentFolder {
|
||||
children: FolderNode[];
|
||||
}
|
||||
|
||||
const FOLDERS_KEY = ['document-folders'] as const;
|
||||
|
||||
export function useDocumentFolders() {
|
||||
return useQuery<FolderNode[]>({
|
||||
queryKey: FOLDERS_KEY,
|
||||
queryFn: () => apiFetch<{ data: FolderNode[] }>('/api/v1/document-folders').then((r) => r.data),
|
||||
staleTime: 30_000,
|
||||
});
|
||||
}
|
||||
|
||||
export function useCreateFolder() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (input: { name: string; parentId: string | null }) =>
|
||||
apiFetch('/api/v1/document-folders', { method: 'POST', body: input }),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: FOLDERS_KEY }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useRenameFolder() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, name }: { id: string; name: string }) =>
|
||||
apiFetch(`/api/v1/document-folders/${id}`, { method: 'PATCH', body: { name } }),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: FOLDERS_KEY }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useMoveFolder() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, parentId }: { id: string; parentId: string | null }) =>
|
||||
apiFetch(`/api/v1/document-folders/${id}`, { method: 'PATCH', body: { parentId } }),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: FOLDERS_KEY }),
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteFolder() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => apiFetch(`/api/v1/document-folders/${id}`, { method: 'DELETE' }),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: FOLDERS_KEY });
|
||||
qc.invalidateQueries({ queryKey: ['documents'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useMoveDocument() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ docId, folderId }: { docId: string; folderId: string | null }) =>
|
||||
apiFetch(`/api/v1/documents/${docId}/folder`, {
|
||||
method: 'PATCH',
|
||||
body: { folderId },
|
||||
}),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['documents'] }),
|
||||
});
|
||||
}
|
||||
|
||||
/** Walk the tree → produce flat path strings like "Deals 2026 / Q1". */
|
||||
export function buildFolderPaths(tree: FolderNode[]): Array<{ id: string; path: string }> {
|
||||
const out: Array<{ id: string; path: string }> = [];
|
||||
function walk(nodes: FolderNode[], prefix: string) {
|
||||
for (const n of nodes) {
|
||||
const path = prefix ? `${prefix} / ${n.name}` : n.name;
|
||||
out.push({ id: n.id, path });
|
||||
walk(n.children, path);
|
||||
}
|
||||
}
|
||||
walk(tree, '');
|
||||
return out;
|
||||
}
|
||||
Reference in New Issue
Block a user