Files
cannamanage/cannamanage-frontend/src/services/board.ts
T
Patrick Plate 6e25914074 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)
2026-06-18 14:43:00 +02:00

149 lines
3.5 KiB
TypeScript

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 interface BoardPosition {
id: string
title: string
description: string | null
sortOrder: number
isActive: boolean
createdAt: string
}
export interface BoardMember {
id: string
clubId: string
positionId: string
memberId: string
electedAt: string
termStart: string
termEnd: string | null
isCurrent: boolean
electedInAssemblyId: string | null
createdAt: string
}
export interface CreatePositionRequest {
title: string
description?: string
sortOrder?: number
}
export interface ElectBoardMemberRequest {
positionId: string
memberId: string
electedAt: string
termStart: string
termEnd?: string
assemblyId?: string
}
// --- Raw API functions ---
export function createPosition(
clubId: string,
data: CreatePositionRequest
): Promise<BoardPosition> {
return apiClient<BoardPosition>(`/board/positions?clubId=${clubId}`, {
method: "POST",
body: data,
})
}
export function getPositions(clubId: string): Promise<BoardPosition[]> {
return apiClient<BoardPosition[]>(`/board/positions?clubId=${clubId}`)
}
export function updatePosition(
id: string,
data: Partial<CreatePositionRequest & { isActive: boolean }>
): Promise<BoardPosition> {
return apiClient<BoardPosition>(`/board/positions/${id}`, {
method: "PUT",
body: data,
})
}
export function electBoardMember(
clubId: string,
data: ElectBoardMemberRequest
): Promise<BoardMember> {
return apiClient<BoardMember>(`/board/members?clubId=${clubId}`, {
method: "POST",
body: data,
})
}
export function getCurrentBoard(clubId: string): Promise<BoardMember[]> {
return apiClient<BoardMember[]>(`/board?clubId=${clubId}`)
}
export function getBoardHistory(clubId: string): Promise<BoardMember[]> {
return apiClient<BoardMember[]>(`/board/history?clubId=${clubId}`)
}
export function removeBoardMember(id: string, clubId: string): Promise<void> {
return apiClient<void>(`/board/members/${id}?clubId=${clubId}`, {
method: "DELETE",
})
}
export function getPortalBoard(clubId: string): Promise<BoardMember[]> {
return apiClient<BoardMember[]>(`/portal/board?clubId=${clubId}`)
}
// --- React Query Hooks ---
export function useBoardQuery() {
return useQuery({
queryKey: ["board", CLUB_ID],
queryFn: () => getCurrentBoard(CLUB_ID),
})
}
export function usePositionsQuery() {
return useQuery({
queryKey: ["board-positions", CLUB_ID],
queryFn: () => getPositions(CLUB_ID),
})
}
export function useCreatePositionMutation() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: (data: CreatePositionRequest) => createPosition(CLUB_ID, data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["board-positions"] })
queryClient.invalidateQueries({ queryKey: ["board"] })
},
})
}
export function useElectBoardMemberMutation() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: (data: ElectBoardMemberRequest) =>
electBoardMember(CLUB_ID, data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["board"] })
},
})
}
export function useRemoveBoardMemberMutation() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: (id: string) => removeBoardMember(id, CLUB_ID),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["board"] })
},
})
}