import type { AdminSession, AdminUser, AuthUser, BillingStatus, Organization, PeriodOverview, PeriodType, RecurringBilling, TicketMeta, TicketPeriod, UserRole, WorkType } from "./types"; type CreateSessionPayload = { ticketNumber: string; organizationId: string; activity: string; workType: WorkType; startedAt: string; endedAt: string; durationSeconds: number; }; type DeleteSessionResult = { deleted: { id: string; ticket_id: string; started_at: string; userTicketEmpty: boolean; ticketDeleted: boolean; }; }; export class ApiError extends Error { constructor( message: string, public status: number ) { super(message); this.name = "ApiError"; } } async function request(url: string, options: RequestInit = {}): Promise { const response = await fetch(url, { credentials: "same-origin", headers: { "Content-Type": "application/json", ...options.headers }, ...options }); if (!response.ok) { const body = await response.json().catch(() => ({})); throw new ApiError(body.error ?? "Anfrage fehlgeschlagen", response.status); } return response.json() as Promise; } export function login(payload: { username: string; password: string }) { return request<{ user: AuthUser }>("/api/auth/login", { method: "POST", body: JSON.stringify(payload) }); } export function logout() { return request<{ ok: boolean }>("/api/auth/logout", { method: "POST" }); } export function getCurrentUser() { return request<{ user: AuthUser }>("/api/auth/me"); } export function updateCurrentUser(payload: { username: string; displayName: string; currentPassword?: string; newPassword?: string; }) { return request<{ user: AuthUser }>("/api/auth/me", { method: "PATCH", body: JSON.stringify(payload) }); } export function getAdminUsers() { return request<{ users: AdminUser[] }>("/api/admin/users"); } export function createAdminUser(payload: { username: string; displayName: string; password: string; role: UserRole; active: boolean; }) { return request<{ user: AdminUser }>("/api/admin/users", { method: "POST", body: JSON.stringify(payload) }); } export function updateAdminUser( userId: string, payload: { username: string; displayName: string; password?: string; role: UserRole; active: boolean; } ) { return request<{ user: AdminUser }>(`/api/admin/users/${userId}`, { method: "PATCH", body: JSON.stringify(payload) }); } export function getAdminSessions() { return request<{ sessions: AdminSession[] }>("/api/admin/sessions"); } export function getOrganizations(search = "") { const params = new URLSearchParams(); if (search.trim()) { params.set("search", search.trim()); } return request<{ organizations: Organization[] }>(`/api/organizations${params.size ? `?${params}` : ""}`); } export function getZammadSettings() { return request<{ settings: { baseUrl: string; hasApiKey: boolean } }>("/api/admin/zammad/settings"); } export function saveZammadSettings(payload: { baseUrl: string; apiKey?: string }) { return request<{ settings: { baseUrl: string; hasApiKey: boolean } }>("/api/admin/zammad/settings", { method: "PUT", body: JSON.stringify(payload) }); } export function syncZammadOrganizations(payload: { baseUrl?: string; apiKey?: string }) { return request<{ synced: number; skipped: number; removed: number; unlinkedTickets: number; unlinkedSessions: number }>("/api/admin/zammad/organizations/sync", { method: "POST", body: JSON.stringify(payload) }); } export function getRecurringBillings() { return request<{ recurringBillings: RecurringBilling[] }>("/api/recurring-billings"); } export function createRecurringBilling(payload: { ticketNumber?: string | null; organizationId: string; activity: string; workType: WorkType; recurrenceType: "weekly" | "every_n_weeks"; intervalValue: number; validFrom: string; validUntil?: string | null; slots: Array<{ weekday: number | null; startTime: string; durationMinutes: number; }>; }) { return request<{ recurringBillings: RecurringBilling[] }>("/api/recurring-billings", { method: "POST", body: JSON.stringify(payload) }); } export function updateRecurringBilling( billingId: string, payload: | { active: boolean } | { ticketNumber?: string | null; organizationId: string; activity: string; workType: WorkType; recurrenceType: "weekly" | "every_n_weeks"; intervalValue: number; validFrom: string; validUntil?: string | null; slots: Array<{ id?: string; weekday: number | null; startTime: string; durationMinutes: number; }>; } ) { return request<{ recurringBillings: RecurringBilling[] }>(`/api/recurring-billings/${billingId}`, { method: "PATCH", body: JSON.stringify(payload) }); } export function deleteRecurringBilling(billingId: string) { return request<{ recurringBillings: RecurringBilling[] }>(`/api/recurring-billings/${billingId}`, { method: "DELETE" }); } export function createAdminSession(payload: { userId: string; ticketNumber: string; organizationId: string; activity: string; workType: WorkType; startedAt: string; endedAt: string; }) { return request<{ session: AdminSession }>("/api/admin/sessions", { method: "POST", body: JSON.stringify(payload) }); } export function reassignAdminSession(sessionId: string, userId: string) { return request<{ session: AdminSession }>(`/api/admin/sessions/${sessionId}/owner`, { method: "PATCH", body: JSON.stringify({ userId }) }); } export function createSession(payload: CreateSessionPayload) { return request("/api/sessions", { method: "POST", body: JSON.stringify(payload) }); } export function lookupTicket(ticketNumber: string) { return request<{ ticket: TicketMeta | null }>(`/api/tickets/lookup?ticketNumber=${encodeURIComponent(ticketNumber)}`); } export function updateTicket( ticketId: string, payload: { ticketNumber: string; organizationId: string; workType: WorkType; } ) { return request<{ ticket: TicketMeta }>(`/api/tickets/${ticketId}`, { method: "PATCH", body: JSON.stringify(payload) }); } export function updateSessionDetails( sessionId: string, payload: { organizationId: string; activity: string; workType: WorkType; startedAt: string; endedAt: string; } ) { return request(`/api/sessions/${sessionId}/details`, { method: "PATCH", body: JSON.stringify(payload) }); } export function updateTicketDayBilling(ticketId: string, day: string, billedMinutes: number | null) { return request(`/api/tickets/${ticketId}/day-billings/${day}`, { method: "PATCH", body: JSON.stringify({ billedMinutes }) }); } export function getMonthOverview(month: string) { return getPeriodOverview("month", month); } export function getTicketMonth(month: string, ticketId: string) { return getTicketPeriod("month", month, ticketId); } function periodPath(type: PeriodType) { return type === "month" ? "months" : "days"; } export function getPeriodOverview(type: PeriodType, period: string) { return request(`/api/periods/${periodPath(type)}/${period}/overview`); } export function getTicketPeriod(type: PeriodType, period: string, ticketId: string) { return request(`/api/periods/${periodPath(type)}/${period}/tickets/${ticketId}`); } export function updateSessionBilling(sessionId: string, billingStatus: BillingStatus) { return request(`/api/sessions/${sessionId}/billing`, { method: "PATCH", body: JSON.stringify({ billingStatus }) }); } export function deleteSession(sessionId: string) { return request(`/api/sessions/${sessionId}`, { method: "DELETE" }); } export function closeTicketMonth(month: string, ticketId: string) { return closeTicketPeriod("month", month, ticketId); } export function reopenTicketMonth(month: string, ticketId: string) { return reopenTicketPeriod("month", month, ticketId); } export function closeMonth(month: string) { return closePeriod("month", month); } export function reopenMonth(month: string) { return reopenPeriod("month", month); } export function closeTicketPeriod(type: PeriodType, period: string, ticketId: string) { return request(`/api/periods/${periodPath(type)}/${period}/tickets/${ticketId}/close`, { method: "POST" }); } export function reopenTicketPeriod(type: PeriodType, period: string, ticketId: string) { return request(`/api/periods/${periodPath(type)}/${period}/tickets/${ticketId}/reopen`, { method: "POST" }); } export function closePeriod(type: PeriodType, period: string) { return request(`/api/periods/${periodPath(type)}/${period}/close`, { method: "POST" }); } export function reopenPeriod(type: PeriodType, period: string) { return request(`/api/periods/${periodPath(type)}/${period}/reopen`, { method: "POST" }); }