2026-03-03 20:22:52 +01:00
|
|
|
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
|
|
|
import { useTenant } from '@/contexts/tenant-context';
|
|
|
|
|
import { apiClient } from '@/lib/api';
|
|
|
|
|
|
|
|
|
|
export interface ShiftListDto {
|
|
|
|
|
items: ShiftListItemDto[];
|
|
|
|
|
total: number;
|
|
|
|
|
page: number;
|
|
|
|
|
pageSize: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface ShiftListItemDto {
|
|
|
|
|
id: string;
|
|
|
|
|
title: string;
|
|
|
|
|
startTime: string;
|
|
|
|
|
endTime: string;
|
|
|
|
|
capacity: number;
|
|
|
|
|
currentSignups: number;
|
2026-03-09 15:47:57 +01:00
|
|
|
isSignedUp: boolean;
|
2026-03-03 20:22:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface ShiftDetailDto {
|
|
|
|
|
id: string;
|
|
|
|
|
title: string;
|
|
|
|
|
description?: string;
|
|
|
|
|
location?: string;
|
|
|
|
|
startTime: string;
|
|
|
|
|
endTime: string;
|
|
|
|
|
capacity: number;
|
|
|
|
|
signups: ShiftSignupDto[];
|
|
|
|
|
clubId: string;
|
|
|
|
|
createdById: string;
|
|
|
|
|
createdAt: string;
|
|
|
|
|
updatedAt: string;
|
2026-03-09 15:47:57 +01:00
|
|
|
isSignedUp: boolean;
|
2026-03-03 20:22:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface ShiftSignupDto {
|
|
|
|
|
id: string;
|
|
|
|
|
memberId: string;
|
2026-03-09 14:46:35 +01:00
|
|
|
externalUserId?: string;
|
2026-03-03 20:22:52 +01:00
|
|
|
signedUpAt: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface CreateShiftRequest {
|
|
|
|
|
title: string;
|
|
|
|
|
description?: string;
|
|
|
|
|
location?: string;
|
|
|
|
|
startTime: string;
|
|
|
|
|
endTime: string;
|
|
|
|
|
capacity: number;
|
|
|
|
|
clubId: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function useShifts(filters?: { startDate?: string; endDate?: string; page?: number }) {
|
|
|
|
|
const { activeClubId } = useTenant();
|
|
|
|
|
|
|
|
|
|
return useQuery<ShiftListDto>({
|
|
|
|
|
queryKey: ['shifts', activeClubId, filters],
|
|
|
|
|
queryFn: async () => {
|
|
|
|
|
const params = new URLSearchParams();
|
|
|
|
|
if (filters?.startDate) params.append('startDate', filters.startDate);
|
|
|
|
|
if (filters?.endDate) params.append('endDate', filters.endDate);
|
|
|
|
|
if (filters?.page) params.append('page', filters.page.toString());
|
|
|
|
|
|
|
|
|
|
const res = await apiClient(`/api/shifts?${params}`);
|
|
|
|
|
if (!res.ok) throw new Error('Failed to fetch shifts');
|
|
|
|
|
return res.json();
|
|
|
|
|
},
|
|
|
|
|
enabled: !!activeClubId,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function useShift(id: string) {
|
|
|
|
|
const { activeClubId } = useTenant();
|
|
|
|
|
|
|
|
|
|
return useQuery<ShiftDetailDto>({
|
|
|
|
|
queryKey: ['shifts', activeClubId, id],
|
|
|
|
|
queryFn: async () => {
|
|
|
|
|
const res = await apiClient(`/api/shifts/${id}`);
|
|
|
|
|
if (!res.ok) throw new Error('Failed to fetch shift');
|
|
|
|
|
return res.json();
|
|
|
|
|
},
|
|
|
|
|
enabled: !!activeClubId && !!id,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function useCreateShift() {
|
|
|
|
|
const queryClient = useQueryClient();
|
|
|
|
|
const { activeClubId } = useTenant();
|
|
|
|
|
|
|
|
|
|
return useMutation({
|
|
|
|
|
mutationFn: async (data: CreateShiftRequest) => {
|
|
|
|
|
const res = await apiClient('/api/shifts', {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
body: JSON.stringify(data),
|
|
|
|
|
});
|
|
|
|
|
if (!res.ok) throw new Error('Failed to create shift');
|
|
|
|
|
return res.json();
|
|
|
|
|
},
|
|
|
|
|
onSuccess: () => {
|
|
|
|
|
queryClient.invalidateQueries({ queryKey: ['shifts', activeClubId] });
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function useSignUpShift() {
|
|
|
|
|
const queryClient = useQueryClient();
|
|
|
|
|
const { activeClubId } = useTenant();
|
|
|
|
|
|
|
|
|
|
return useMutation({
|
|
|
|
|
mutationFn: async (shiftId: string) => {
|
|
|
|
|
const res = await apiClient(`/api/shifts/${shiftId}/signup`, {
|
|
|
|
|
method: 'POST',
|
|
|
|
|
});
|
|
|
|
|
if (!res.ok) throw new Error('Failed to sign up');
|
|
|
|
|
},
|
|
|
|
|
onSuccess: () => {
|
|
|
|
|
queryClient.invalidateQueries({ queryKey: ['shifts', activeClubId] });
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function useCancelSignUp() {
|
|
|
|
|
const queryClient = useQueryClient();
|
|
|
|
|
const { activeClubId } = useTenant();
|
|
|
|
|
|
|
|
|
|
return useMutation({
|
|
|
|
|
mutationFn: async (shiftId: string) => {
|
|
|
|
|
const res = await apiClient(`/api/shifts/${shiftId}/signup`, {
|
|
|
|
|
method: 'DELETE',
|
|
|
|
|
});
|
|
|
|
|
if (!res.ok) throw new Error('Failed to cancel sign-up');
|
|
|
|
|
},
|
|
|
|
|
onSuccess: () => {
|
|
|
|
|
queryClient.invalidateQueries({ queryKey: ['shifts', activeClubId] });
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
}
|