// ───────────────────────────────────────────────────────────────────────────── // src/lib/api/adminUserManagementApi.ts // User Management API for Admin Dashboard // Based on the official API documentation - completely independent from adminApi // ───────────────────────────────────────────────────────────────────────────── import { getApiBaseUrl } from './config'; const BASE_URL = getApiBaseUrl(); const TOKEN_KEY = 'authToken'; // ─── Custom Error Class ─────────────────────────────────────────────────────── export class UserManagementApiError extends Error { readonly status: number; readonly path: string; constructor( status: number, message: string, path: string, ) { super(`[${status}] ${path} → ${message}`); this.status = status; this.path = path; this.name = 'UserManagementApiError'; } } // ─── Shared helper ──────────────────────────────────────────────────────────── function getHeaders(): HeadersInit { const token = localStorage.getItem(TOKEN_KEY) ?? ''; return { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}), }; } async function request(path: string, options?: RequestInit): Promise { const url = `${BASE_URL}${path}`; // Guard: expired token const token = localStorage.getItem(TOKEN_KEY); if (token) { try { const payload = JSON.parse(atob(token.split('.')[1])); if (payload?.exp && payload.exp * 1000 < Date.now()) { localStorage.removeItem(TOKEN_KEY); throw new UserManagementApiError(401, 'Session expired — please log in again', path); } } catch (e) { if (e instanceof UserManagementApiError) throw e; localStorage.removeItem(TOKEN_KEY); } } try { const res = await fetch(url, { ...options, headers: { ...getHeaders(), ...options?.headers }, }); // Handle 204 No Content if (res.status === 204) { return null as T; } if (!res.ok) { const message = await res.text().catch(() => res.statusText); throw new UserManagementApiError(res.status, message, path); } const text = await res.text(); if (!text.trim()) { return null as T; } try { return JSON.parse(text) as T; } catch { return text as unknown as T; } } catch (error) { if (error instanceof UserManagementApiError) throw error; throw new Error(`Network error while calling ${path}`); } } // ─── Response Types ─────────────────────────────────────────────────────────── export interface UserManagementStatsDto { totalUsers: number; totalSheikhs: number; totalStudents: number; blockedUsers: number; } export interface StudentDtoAdmin { id: number; name: string; email: string; registrationDate: string; streak: number; totalSessions: number; status: 'ACTIVE' | 'BLOCKED' | 'INACTIVE'; } export interface SheikhDtoAdmin { id: number; name: string; email: string; registrationDate: string; numberOfSessions: number; totalRevenue: number; averageRating: number; status: 'ACTIVE' | 'BLOCKED' | 'PENDING' | 'APPROVED' | 'REJECTED'; } export type UserDtoAdmin = StudentDtoAdmin | SheikhDtoAdmin; export interface DetailedStudentView { id: number; name: string; email: string; registrationDate: string; streak: number; sessionCount: number; country: string; numberOfCompletedSessions: number; totalSpentTime: number; } export interface DetailedSheikhView { id: number; name: string; email: string; country: string; experienceYears: number; bio: string; specialization: string; hourlyRate: number; status: 'PENDING' | 'APPROVED' | 'REJECTED' | 'ACTIVE' | 'BLOCKED'; registrationDate: string; averageRating: number; numberOfCompletedSessions: number; numberOfStudents: number; numberofReviews: number; sessionCount: number; totalEarnings: number; } export interface SessionHistoryDto { sessionId: number; sheikhName: string; studentName: string; date: string; status: 'COMPLETED' | 'CANCELLED' | 'PENDING' | 'SCHEDULED'; price: number; durationInMinutes: number; rate: number; } export interface SheikhProfileResponse { sheikhProfile: DetailedSheikhView; sessionHistories: SessionHistoryDto[]; } export interface StudentProfileResponse { studentProfile: DetailedStudentView; sessionHistories: SessionHistoryDto[]; } // ─── API Functions ──────────────────────────────────────────────────────────── /** * GET /api/admin/user-management/stats * Retrieves comprehensive statistics about all users in the platform. */ export async function fetchUserManagementStats(): Promise { return request('/api/admin/user-management/stats'); } /** * GET /api/admin/user-management/students * Retrieves a list of all students with basic information. */ export async function fetchAllStudents(): Promise { return request('/api/admin/user-management/students'); } /** * GET /api/admin/user-management/sheikhs * Retrieves a list of all sheikhs with basic information. */ export async function fetchAllSheikhs(): Promise { return request('/api/admin/user-management/sheikhs'); } /** * GET /api/admin/user-management/users * Retrieves a combined list of all users (students and sheikhs). */ export async function fetchAllUsers(): Promise { return request('/api/admin/user-management/users'); } /** * POST /api/admin/user-management/block-user * Blocks a user account, preventing them from accessing the platform. * @param userId - ID of the user to block */ export async function blockUser(userId: number): Promise { return request(`/api/admin/user-management/block-user?userId=${userId}`, { method: 'POST', }); } /** * POST /api/admin/user-management/unblock-user * Unblocks a previously blocked user account. * @param userId - ID of the user to unblock */ export async function unblockUser(userId: number): Promise { return request(`/api/admin/user-management/unblock-user?userId=${userId}`, { method: 'POST', }); } /** * GET /api/admin/user-management/sheikh-profile * Retrieves detailed profile information for a specific sheikh including session history. * @param userId - Sheikh's ID */ export async function fetchSheikhProfile(userId: number): Promise { return request(`/api/admin/user-management/sheikh-profile?userId=${userId}`); } /** * GET /api/admin/user-management/student-profile * Retrieves detailed profile information for a specific student including session history. * @param userId - Student's ID */ export async function fetchStudentProfile(userId: number): Promise { return request(`/api/admin/user-management/student-profile?userId=${userId}`); } // ─── Convenience: load all user management data ─────────────────────────────── export interface UserManagementData { stats: UserManagementStatsDto; students: StudentDtoAdmin[]; sheikhs: SheikhDtoAdmin[]; users: UserDtoAdmin[]; } export async function fetchUserManagementData(): Promise<{ data: Partial; errors: Record; }> { const [statsResult, studentsResult, sheikhsResult, usersResult] = await Promise.allSettled([ fetchUserManagementStats(), fetchAllStudents(), fetchAllSheikhs(), fetchAllUsers(), ]); const data: Partial = {}; const errors: Record = {}; if (statsResult.status === 'fulfilled') { data.stats = statsResult.value; } else { errors.stats = statsResult.reason?.message ?? 'Failed to load statistics'; } if (studentsResult.status === 'fulfilled') { data.students = studentsResult.value; } else { errors.students = studentsResult.reason?.message ?? 'Failed to load students'; } if (sheikhsResult.status === 'fulfilled') { data.sheikhs = sheikhsResult.value; } else { errors.sheikhs = sheikhsResult.reason?.message ?? 'Failed to load sheikhs'; } if (usersResult.status === 'fulfilled') { data.users = usersResult.value; } else { errors.users = usersResult.reason?.message ?? 'Failed to load users'; } return { data, errors }; } // ─── Default export ─────────────────────────────────────────────────────────── const adminUserManagementApi = { fetchUserManagementStats, fetchAllStudents, fetchAllSheikhs, fetchAllUsers, blockUser, unblockUser, fetchSheikhProfile, fetchStudentProfile, fetchUserManagementData, UserManagementApiError, }; export default adminUserManagementApi;