import { API_BASE_URL } from '@/src/config/env'; export type Tenant = { id: number; name: string; short?: string | null; [key: string]: unknown; }; export type TaskStatus = 'Offen' | 'In Bearbeitung' | 'Abgeschlossen'; export type Task = { id: number; name: string; description?: string | null; categorie?: string | null; userId?: string | null; user_id?: string | null; profile?: string | null; archived?: boolean; [key: string]: unknown; }; export type TenantProfile = { id?: number | string; user_id?: string; full_name?: string; fullName?: string; email?: string; [key: string]: unknown; }; export type StaffTimeSpan = { id: string | null; eventIds: string[]; state: string; started_at: string; stopped_at: string | null; duration_minutes: number; user_id: string | null; type: string; description: string; }; export type MeResponse = { user: { id: string; email: string; must_change_password?: boolean; [key: string]: unknown; }; tenants: Tenant[]; activeTenant: number | string | null; profile: Record | null; permissions: string[]; }; type RequestOptions = { method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'; token?: string | null; body?: unknown; }; function buildUrl(path: string): string { if (path.startsWith('http://') || path.startsWith('https://')) { return path; } const normalizedPath = path.startsWith('/') ? path : `/${path}`; return `${API_BASE_URL}${normalizedPath}`; } async function parseJson(response: Response): Promise { const text = await response.text(); if (!text) return null; try { return JSON.parse(text); } catch { return null; } } export async function apiRequest(path: string, options: RequestOptions = {}): Promise { const response = await fetch(buildUrl(path), { method: options.method || 'GET', headers: { Accept: 'application/json', 'Content-Type': 'application/json', ...(options.token ? { Authorization: `Bearer ${options.token}` } : {}), }, body: options.body ? JSON.stringify(options.body) : undefined, }); const payload = await parseJson(response); if (!response.ok) { const message = (payload as { message?: string; error?: string } | null)?.message || (payload as { message?: string; error?: string } | null)?.error || `Request failed (${response.status}) for ${path}`; throw new Error(message); } return payload as T; } export async function checkBackendHealth(): Promise<{ status: string; [key: string]: unknown }> { return apiRequest<{ status: string; [key: string]: unknown }>('/health'); } export async function loginWithEmailPassword(email: string, password: string): Promise { const payload = await apiRequest<{ token?: string }>('/auth/login', { method: 'POST', body: { email, password }, }); if (!payload?.token) { throw new Error('Login did not return a token.'); } return payload.token; } export async function fetchMe(token: string): Promise { return apiRequest('/api/me', { token, }); } export async function switchTenantRequest(tenantId: number, token: string): Promise { const payload = await apiRequest<{ token?: string }>('/api/tenant/switch', { method: 'POST', token, body: { tenant_id: String(tenantId) }, }); if (!payload?.token) { throw new Error('Tenant switch did not return a token.'); } return payload.token; } export async function fetchTasks(token: string): Promise { const tasks = await apiRequest('/api/resource/tasks', { token }); return (tasks || []).filter((task) => !task.archived); } export async function createTask( token: string, payload: { name: string; description?: string | null; categorie?: TaskStatus; userId?: string | null; } ): Promise { return apiRequest('/api/resource/tasks', { method: 'POST', token, body: payload, }); } export async function updateTask(token: string, taskId: number, payload: Partial): Promise { return apiRequest(`/api/resource/tasks/${taskId}`, { method: 'PUT', token, body: payload, }); } export async function fetchTenantProfiles(token: string): Promise { const response = await apiRequest<{ data?: TenantProfile[] }>('/api/tenant/profiles', { token }); return response?.data || []; } export async function fetchStaffTimeSpans( token: string, targetUserId?: string ): Promise { const query = targetUserId ? `?targetUserId=${encodeURIComponent(targetUserId)}` : ''; const spans = await apiRequest(`/api/staff/time/spans${query}`, { token }); return (spans || []) .map((span) => { const started = span.startedAt ? new Date(span.startedAt) : null; const ended = span.endedAt ? new Date(span.endedAt) : new Date(); const durationMinutes = started && ended ? Math.max(0, Math.floor((ended.getTime() - started.getTime()) / 60000)) : 0; return { id: span.sourceEventIds?.[0] ?? null, eventIds: span.sourceEventIds || [], state: span.status || 'draft', started_at: span.startedAt, stopped_at: span.endedAt || null, duration_minutes: durationMinutes, user_id: targetUserId || null, type: span.type || 'work', description: span.payload?.description || '', } as StaffTimeSpan; }) .sort((a, b) => new Date(b.started_at).getTime() - new Date(a.started_at).getTime()); } export async function createStaffTimeEvent( token: string, payload: { eventtype: string; eventtime: string; user_id: string; description?: string; } ): Promise { await apiRequest('/api/staff/time/event', { method: 'POST', token, body: { eventtype: payload.eventtype, eventtime: payload.eventtime, user_id: payload.user_id, payload: payload.description ? { description: payload.description } : undefined, }, }); } export async function submitStaffTime(token: string, eventIds: string[]): Promise { await apiRequest('/api/staff/time/submit', { method: 'POST', token, body: { eventIds }, }); } export async function approveStaffTime( token: string, eventIds: string[], employeeUserId: string ): Promise { await apiRequest('/api/staff/time/approve', { method: 'POST', token, body: { eventIds, employeeUserId }, }); } export async function rejectStaffTime( token: string, eventIds: string[], employeeUserId: string, reason: string ): Promise { await apiRequest('/api/staff/time/reject', { method: 'POST', token, body: { eventIds, employeeUserId, reason }, }); }