Files
dashboard/frontend/src/services/members.ts

156 lines
4.9 KiB
TypeScript

import { api } from './api';
import {
MemberListItem,
MemberWithProfile,
MemberFilters,
MemberStats,
CreateMemberProfileData,
UpdateMemberProfileData,
Befoerderung,
Untersuchung,
Fahrgenehmigung,
Ausbildung,
} from '../types/member.types';
// ----------------------------------------------------------------
// Response envelope shapes
// ----------------------------------------------------------------
interface ApiListResponse<T> {
success: boolean;
data: T[];
meta: { total: number; page: number };
}
interface ApiItemResponse<T> {
success: boolean;
data: T;
}
// ----------------------------------------------------------------
// Service
// ----------------------------------------------------------------
/**
* Builds a URLSearchParams object from the filter object so query
* strings like ?status[]=aktiv&status[]=jugend are sent correctly.
*/
function buildParams(filters?: MemberFilters): URLSearchParams {
const params = new URLSearchParams();
if (!filters) return params;
if (filters.search) params.append('search', filters.search);
if (filters.page) params.append('page', String(filters.page));
if (filters.pageSize !== undefined) params.append('pageSize', String(filters.pageSize));
filters.status?.forEach((s) => params.append('status[]', s));
filters.dienstgrad?.forEach((d) => params.append('dienstgrad[]', d));
if (filters.sortBy) params.append('sortBy', filters.sortBy);
if (filters.sortDir) params.append('sortDir', filters.sortDir);
return params;
}
export const membersService = {
/**
* Fetches a paginated, optionally filtered list of members.
*/
async getMembers(
filters?: MemberFilters
): Promise<{ items: MemberListItem[]; total: number; page: number }> {
const params = buildParams(filters);
const response = await api.get<ApiListResponse<MemberListItem>>(
`/api/members?${params.toString()}`
);
if (response.data?.data === undefined || response.data?.data === null) {
throw new Error('Invalid API response');
}
return {
items: response.data.data,
total: response.data.meta?.total ?? 0,
page: response.data.meta?.page ?? 1,
};
},
/**
* Fetches a single member with their full profile and rank history.
*/
async getMember(userId: string): Promise<MemberWithProfile> {
const response = await api.get<ApiItemResponse<MemberWithProfile>>(
`/api/members/${userId}`
);
if (response.data?.data === undefined || response.data?.data === null) {
throw new Error('Invalid API response');
}
return response.data.data;
},
/**
* Creates a new member profile for an existing auth user.
* Restricted to Kommandant/Admin (enforced server-side).
*/
async createMemberProfile(
userId: string,
data: CreateMemberProfileData
): Promise<MemberWithProfile> {
const response = await api.post<ApiItemResponse<MemberWithProfile>>(
`/api/members/${userId}/profile`,
data
);
if (response.data?.data === undefined || response.data?.data === null) {
throw new Error('Invalid API response');
}
return response.data.data;
},
/**
* Partially updates a member profile.
* Kommandant/Admin: full update.
* Own profile: limited fields only (enforced server-side).
*/
async updateMember(
userId: string,
data: UpdateMemberProfileData
): Promise<MemberWithProfile> {
const response = await api.patch<ApiItemResponse<MemberWithProfile>>(
`/api/members/${userId}`,
data
);
if (response.data?.data === undefined || response.data?.data === null) {
throw new Error('Invalid API response');
}
return response.data.data;
},
/**
* Fetches aggregate counts for the dashboard KPI widget.
*/
async getMemberStats(): Promise<MemberStats> {
const response = await api.get<ApiItemResponse<MemberStats>>('/api/members/stats');
if (response.data?.data === undefined || response.data?.data === null) {
throw new Error('Invalid API response');
}
return response.data.data;
},
async getBefoerderungen(userId: string): Promise<Befoerderung[]> {
const response = await api.get<ApiItemResponse<Befoerderung[]>>(`/api/members/${userId}/befoerderungen`);
return response.data?.data ?? [];
},
async getUntersuchungen(userId: string): Promise<Untersuchung[]> {
const response = await api.get<ApiItemResponse<Untersuchung[]>>(`/api/members/${userId}/untersuchungen`);
return response.data?.data ?? [];
},
async getFahrgenehmigungen(userId: string): Promise<Fahrgenehmigung[]> {
const response = await api.get<ApiItemResponse<Fahrgenehmigung[]>>(`/api/members/${userId}/fahrgenehmigungen`);
return response.data?.data ?? [];
},
async getAusbildungen(userId: string): Promise<Ausbildung[]> {
const response = await api.get<ApiItemResponse<Ausbildung[]>>(`/api/members/${userId}/ausbildungen`);
return response.data?.data ?? [];
},
};