feat: wire Documents + Board page buttons, add mock-mode dual operation
Sprint 12 Phase 1: Golden Test Standard - Documents: React Query, upload/download/delete wired, category colors+icons, table min-widths, data-testid - Board: React Query, create position/elect/remove wired, confirmation dialogs, data-testid - Both pages: mock-mode fallback (works without backend)
This commit is contained in:
@@ -1,5 +1,13 @@
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
|
||||
|
||||
import { apiClient } from "@/lib/api-client"
|
||||
|
||||
// --- Constants ---
|
||||
|
||||
const CLUB_ID = "00000000-0000-0000-0000-000000000001"
|
||||
|
||||
// --- Types ---
|
||||
|
||||
export type DocumentCategory =
|
||||
| "SATZUNG"
|
||||
| "PROTOKOLL"
|
||||
@@ -28,6 +36,16 @@ export interface StorageUsage {
|
||||
bytesUsed: number
|
||||
}
|
||||
|
||||
export interface UploadDocumentRequest {
|
||||
title: string
|
||||
category: DocumentCategory
|
||||
accessLevel: DocumentAccessLevel
|
||||
description: string | null
|
||||
file: File
|
||||
}
|
||||
|
||||
// --- Raw API functions ---
|
||||
|
||||
export async function uploadDocument(
|
||||
clubId: string,
|
||||
title: string,
|
||||
@@ -90,14 +108,53 @@ export function getPortalDocuments(clubId: string): Promise<ClubDocument[]> {
|
||||
return apiClient<ClubDocument[]>(`/portal/documents?clubId=${clubId}`)
|
||||
}
|
||||
|
||||
// Helper: format file size
|
||||
// --- React Query Hooks ---
|
||||
|
||||
export function useDocumentsQuery(category?: DocumentCategory) {
|
||||
return useQuery({
|
||||
queryKey: ["documents", CLUB_ID, category],
|
||||
queryFn: () => listDocuments(CLUB_ID, category),
|
||||
})
|
||||
}
|
||||
|
||||
export function useUploadDocumentMutation() {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (data: UploadDocumentRequest) =>
|
||||
uploadDocument(
|
||||
CLUB_ID,
|
||||
data.title,
|
||||
data.category,
|
||||
data.accessLevel,
|
||||
data.description,
|
||||
data.file
|
||||
),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["documents"] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function useDeleteDocumentMutation() {
|
||||
const queryClient = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => deleteDocument(id, CLUB_ID),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["documents"] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// --- Helper: format file size ---
|
||||
|
||||
export function formatFileSize(bytes: number): string {
|
||||
if (bytes < 1024) return `${bytes} B`
|
||||
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`
|
||||
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`
|
||||
}
|
||||
|
||||
// Category labels
|
||||
// --- Category labels ---
|
||||
|
||||
export const categoryLabels: Record<DocumentCategory, string> = {
|
||||
SATZUNG: "Satzung",
|
||||
PROTOKOLL: "Protokoll",
|
||||
|
||||
Reference in New Issue
Block a user