const BASE = '/admin-api/learning'; export interface PaginatedResponse { items: T[]; total: number; } interface AnomalyData { deltaOutliers: unknown[]; futureEvents: unknown[]; } async function getApi(path: string, params?: Record): Promise { let url = `${BASE}${path}`; if (params) { const qs = Object.entries(params) .filter(([, v]) => v !== undefined && v !== '') .map(([k, v]) => `${k}=${encodeURIComponent(v)}`) .join('&'); if (qs) url += `?${qs}`; } const res = await fetch(url, { headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${localStorage.getItem('admin_access_token') || ''}` } }); if (!res.ok) throw new Error(`HTTP ${res.status}`); const json = await res.json(); return json.data ?? json; } async function postApi(path: string, body?: any): Promise { const res = await fetch(`${BASE}${path}`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${localStorage.getItem('admin_access_token') || ''}` }, body: body ? JSON.stringify(body) : undefined, }); if (!res.ok) throw new Error(`HTTP ${res.status}`); const json = await res.json(); return json.data ?? json; } export interface DashboardData { overview: { totalEvents: number; todayEvents: number; failedEvents: number; duplicateEvents: number; warningEvents: number; totalActiveSeconds: number }; sessions: { active: number; interrupted: number; completed: number; total: number }; users: { activeToday: number; totalWithEvents: number }; materials: { totalRead: number; totalMarkedRead: number }; recentAnomalies: Array<{ eventId: string; userId: string; materialId: string; eventType: string; errorCode?: string; occurredAt: string }>; } export const learningAdminAPI = { getDashboard: (params?: { knowledgeBaseId?: string; startDate?: string; endDate?: string }) => getApi('/dashboard', params), getKnowledgeBases: () => getApi>('/knowledge-bases'), getReadingEvents: (params?: Record) => getApi>('/reading-events', params), getReadingEventDetail: (id: string) => getApi(`/reading-events/${id}`), getFailedEvents: (params?: Record) => getApi>('/reading-events/failed', params), getSessions: (params?: Record) => getApi>('/sessions', params), getSessionDetail: (id: string) => getApi(`/sessions/${id}`), getProgress: (params?: Record) => getApi>('/progress', params), getDailyActivities: (params?: Record) => getApi>('/daily-activities', params), getRecords: (params?: Record) => getApi>('/records', params), getUserTimeline: (userId: string) => getApi('/user-timeline', { userId }), getUserDiagnose: (userId: string) => getApi('/user-diagnose', { userId }), getMaterialDiagnose: (materialId: string) => getApi('/material-diagnose', { materialId }), getAnomalies: () => getApi('/anomalies'), getTemporaryMaterials: (params?: Record) => getApi>('/temporary-materials', params), recalculate: () => postApi('/recalculate'), replayEvent: (eventId: string) => postApi<{ success: boolean; message: string }>(`/reading-events/${eventId}/replay`), replayEventsBatch: (params: { userId?: string; materialId?: string; startDate?: string; endDate?: string; eventType?: string }) => postApi<{ replayed: number; failed: number }>('/reading-events/replay-batch', params), exportData: (type: string) => getApi('/export', { type }), };