/** * Admin API Service * Handles all admin-related API operations */ import { apiDelete, apiGet, apiPost } from '@/lib/api-client'; import { httpClient } from '@/lib/http-client'; import { z } from 'zod'; // ============================================================================ // Dashboard & Statistics // ============================================================================ export const dashboardStatsSchema = z.object({ totalOrganizations: z.number(), verifiedOrganizations: z.number(), activeConnections: z.number(), newThisMonth: z.number(), pendingVerifications: z.number(), pendingTranslations: z.number(), systemAlerts: z.number(), }); export type DashboardStats = z.infer; export const organizationStatsSchema = z.object({ total: z.number(), verified: z.number(), pending: z.number(), unverified: z.number(), newThisMonth: z.number(), bySector: z.record(z.string(), z.number()), bySubtype: z.record(z.string(), z.number()), verificationRate: z.number(), }); export type OrganizationStats = z.infer; export const userActivityStatsSchema = z.object({ totalUsers: z.number(), activeUsers: z.number(), inactiveUsers: z.number(), newThisMonth: z.number(), byRole: z.record(z.string(), z.number()), lastLoginStats: z.record(z.string(), z.number()), }); export type UserActivityStats = z.infer; export const matchingStatsSchema = z.object({ totalMatches: z.number(), activeMatches: z.number(), successfulMatches: z.number(), matchRate: z.number(), }); export type MatchingStats = z.infer; export const systemHealthSchema = z.object({ status: z.enum(['healthy', 'degraded', 'down']), database: z.string(), cache: z.string(), externalServices: z.record(z.string(), z.string()), uptime: z.number(), responseTime: z.number(), errorRate: z.number(), activeConnections: z.number(), }); export type SystemHealth = z.infer; const activityItemSchema = z.object({ id: z.string(), type: z.string(), description: z.string(), timestamp: z.string(), }); export const recentActivitySchema = z.array(activityItemSchema); export type RecentActivity = z.infer; /** * Get dashboard statistics */ export async function getDashboardStats(): Promise { return apiGet('/admin/dashboard/stats'); } /** * Get organization statistics */ export async function getOrganizationStats(): Promise { return apiGet('/admin/analytics/organizations'); } /** * Get user activity statistics */ export async function getUserActivityStats(): Promise { return apiGet('/admin/analytics/users'); } /** * Get matching statistics */ export async function getMatchingStats(): Promise { return apiGet('/admin/analytics/matching'); } /** * Get system health */ export async function getSystemHealth(): Promise { return apiGet('/admin/system/health'); } export type MaintenanceSetting = { enabled: boolean; message: string; allowedIPs?: string[] }; /** * Get maintenance settings */ export async function getMaintenance(): Promise { const resp: any = await apiGet('/admin/settings/maintenance'); // Normalize server-side snake_case to camelCase return { enabled: resp.enabled, message: resp.message, allowedIPs: resp.allowed_ips || resp.allowedIPs || [], }; } /** * Set maintenance settings */ export async function setMaintenance(request: MaintenanceSetting): Promise<{ message: string }> { const payload: any = { enabled: request.enabled, message: request.message, allowed_ips: request.allowedIPs || [], }; return httpClient.put('/admin/settings/maintenance', payload); } /** * Get recent activity feed for admin dashboard */ export async function getRecentActivity(): Promise { return apiGet('/admin/dashboard/activity'); } // ============================================================================ // User Management // ============================================================================ export const userSchema = z.object({ id: z.string(), email: z.string(), name: z.string(), role: z.string(), permissions: z.string().optional(), lastLoginAt: z.string().nullable().optional(), isActive: z.boolean(), createdAt: z.string(), updatedAt: z.string(), }); export type User = z.infer; export const userListResponseSchema = z.object({ users: z.array(userSchema), total: z.number(), limit: z.number(), offset: z.number(), }); export type UserListResponse = z.infer; export const createUserRequestSchema = z.object({ email: z.string().email(), name: z.string().min(1), password: z.string().min(8), role: z.string(), permissions: z.array(z.string()).optional(), }); export type CreateUserRequest = z.infer; export const updateUserRequestSchema = z.object({ name: z.string().optional(), email: z.string().email().optional(), role: z.string().optional(), permissions: z.array(z.string()).optional(), isActive: z.boolean().optional(), }); export type UpdateUserRequest = z.infer; export const userStatsSchema = z.object({ total: z.number(), active: z.number(), inactive: z.number(), new_this_month: z.number(), by_role: z.record(z.string(), z.number()), }); export type UserStats = z.infer; /** * List users with filters */ export async function listUsers(params?: { role?: string; isActive?: boolean; search?: string; limit?: number; offset?: number; }): Promise { const queryParams = new URLSearchParams(); if (params?.role) queryParams.append('role', params.role); if (params?.isActive !== undefined) queryParams.append('isActive', String(params.isActive)); if (params?.search) queryParams.append('search', params.search); if (params?.limit) queryParams.append('limit', String(params.limit)); if (params?.offset) queryParams.append('offset', String(params.offset)); const query = queryParams.toString(); return apiGet(`/admin/users${query ? `?${query}` : ''}`); } /** * Get user by ID */ export async function getUser(id: string): Promise { return apiGet(`/admin/users/${id}`); } /** * Create user */ export async function createUser(request: CreateUserRequest): Promise { return apiPost('/admin/users', request); } /** * Update user */ export async function updateUser(id: string, request: UpdateUserRequest): Promise { return httpClient.put(`/admin/users/${id}`, request); } /** * Update user role */ export async function updateUserRole(id: string, role: string): Promise<{ message: string }> { return apiPost<{ message: string }>(`/admin/users/${id}/role`, { role }); } /** * Update user permissions */ export async function updateUserPermissions( id: string, permissions: string[] ): Promise<{ message: string }> { return apiPost<{ message: string }>(`/admin/users/${id}/permissions`, { permissions }); } /** * Deactivate user */ export async function deactivateUser(id: string): Promise<{ message: string }> { return apiDelete<{ message: string }>(`/admin/users/${id}`); } /** * Get user activity log */ export async function getUserActivity( id: string, params?: { limit?: number; offset?: number } ): Promise<{ activities: unknown[]; total: number; limit: number; offset: number; }> { const queryParams = new URLSearchParams(); if (params?.limit) queryParams.append('limit', String(params.limit)); if (params?.offset) queryParams.append('offset', String(params.offset)); const query = queryParams.toString(); return apiGet(`/admin/users/${id}/activity${query ? `?${query}` : ''}`); } /** * Get user statistics */ export async function getUserStats(): Promise { return apiGet('/admin/users/stats'); } // ============================================================================ // Organization Verification // ============================================================================ export const verificationQueueItemSchema = z.object({ id: z.string(), organizationId: z.string(), dataType: z.string(), dataId: z.string(), status: z.enum(['unverified', 'pending', 'verified', 'rejected']), verifiedBy: z.string().optional(), verifiedAt: z.string().nullable().optional(), notes: z.string().optional(), }); export type VerificationQueueItem = z.infer; export const verificationQueueResponseSchema = z.object({ queue: z.array(verificationQueueItemSchema), }); export type VerificationQueueResponse = z.infer; export const verificationStatsSchema = z.object({ total: z.number(), pending: z.number(), verified: z.number(), rejected: z.number(), unverified: z.number(), average_time_days: z.number().optional(), }); export type VerificationStats = z.infer; /** * Get verification queue */ export async function getVerificationQueue(params?: { status?: string; dataType?: string; organizationId?: string; }): Promise { const queryParams = new URLSearchParams(); if (params?.status) queryParams.append('status', params.status); if (params?.dataType) queryParams.append('dataType', params.dataType); if (params?.organizationId) queryParams.append('organizationId', params.organizationId); const query = queryParams.toString(); return apiGet( `/admin/organizations/verification-queue${query ? `?${query}` : ''}` ); } /** * Verify organization */ export async function verifyOrganization(id: string, notes?: string): Promise<{ message: string }> { return apiPost<{ message: string }>(`/admin/organizations/${id}/verify`, { notes: notes || '' }); } /** * Reject verification */ export async function rejectVerification( id: string, reason: string, notes?: string ): Promise<{ message: string }> { return apiPost<{ message: string }>(`/admin/organizations/${id}/reject`, { reason, notes: notes || '', }); } /** * Bulk verify organizations */ export async function bulkVerifyOrganizations( organizationIds: string[] ): Promise<{ message: string }> { return apiPost<{ message: string }>('/admin/organizations/bulk-verify', { organizationIds }); } /** * Get organization statistics */ export async function getOrganizationStatsAdmin(): Promise { return apiGet('/admin/organizations/stats'); } // ============================================================================ // Localization Management // ============================================================================ export const translationKeySchema = z.object({ key: z.string(), source: z.string(), value: z.string(), status: z.enum(['missing', 'translated']), }); export type TranslationKey = z.infer; export const translationKeysResponseSchema = z.object({ locale: z.string(), keys: z.array(translationKeySchema), total: z.number(), translated: z.number(), missing: z.number(), }); export type TranslationKeysResponse = z.infer; export const updateUITranslationRequestSchema = z.object({ value: z.string(), }); export const bulkUpdateUITranslationsRequestSchema = z.object({ updates: z.array( z.object({ locale: z.string(), key: z.string(), value: z.string(), }) ), }); export const autoTranslateRequestSchema = z.object({ sourceLocale: z.string(), targetLocale: z.string(), }); export const autoTranslateResponseSchema = z.object({ message: z.string(), translated: z.number(), results: z.record(z.string(), z.string()).optional(), }); /** * Update UI translation */ export async function updateUITranslation( locale: string, key: string, value: string ): Promise<{ message: string }> { return apiPost<{ message: string }>(`/admin/i18n/ui/${locale}/${key}`, { value }); } /** * Bulk update UI translations */ export async function bulkUpdateUITranslations( updates: Array<{ locale: string; key: string; value: string }> ): Promise<{ message: string }> { return apiPost<{ message: string }>('/admin/i18n/ui/bulk-update', { updates }); } /** * Auto-translate missing keys */ export async function autoTranslateMissing( sourceLocale: string, targetLocale: string ): Promise<{ message: string; translated: number; results?: Record; }> { return apiPost('/admin/i18n/ui/auto-translate', { sourceLocale, targetLocale }); } /** * Get translation keys for a locale */ export async function getTranslationKeys(locale: string): Promise { return apiGet(`/admin/i18n/ui/${locale}/keys`); } /** * Update data translation */ export async function updateDataTranslation( entityType: string, entityID: string, field: string, locale: string, value: string ): Promise<{ message: string }> { return apiPost<{ message: string }>( `/admin/i18n/data/${entityType}/${entityID}/${field}/${locale}`, { value } ); } /** * Bulk translate data */ export async function bulkTranslateData( entityType: string, entityIDs: string[], targetLocale: string, fields?: string[] ): Promise<{ message: string; translated: number; results: Record>; }> { return apiPost<{ message: string; translated: number; results: Record>; }>('/admin/i18n/data/bulk-translate', { entityType, entityIDs, targetLocale, fields, }); } /** * Get missing translations */ export async function getMissingTranslations(entityType: string, locale: string): Promise { return apiGet(`/admin/i18n/data/${entityType}/missing?locale=${locale}`); } // ============================================================================ // Content Management // ============================================================================ export const staticPageSchema = z.object({ id: z.string(), slug: z.string(), title: z.string(), content: z.string().optional(), metaDescription: z.string().optional(), seoKeywords: z.array(z.string()).optional(), status: z.enum(['draft', 'published', 'archived']), visibility: z.enum(['public', 'private', 'admin']), template: z.string().optional(), publishedAt: z.string().nullable().optional(), createdBy: z.string().optional(), updatedBy: z.string().optional(), createdAt: z.string(), updatedAt: z.string(), }); export type StaticPage = z.infer; export const staticPagesResponseSchema = z.object({ pages: z.array(staticPageSchema), }); export type StaticPagesResponse = z.infer; export const createPageRequestSchema = z.object({ slug: z.string(), title: z.string(), content: z.string().optional(), metaDescription: z.string().optional(), seoKeywords: z.array(z.string()).optional(), status: z.string().optional(), visibility: z.string().optional(), template: z.string().optional(), }); export type CreatePageRequest = z.infer; export const updatePageRequestSchema = z.object({ title: z.string().optional(), content: z.string().optional(), metaDescription: z.string().optional(), status: z.string().optional(), visibility: z.string().optional(), }); export type UpdatePageRequest = z.infer; export const announcementSchema = z.object({ id: z.string(), title: z.string(), content: z.string(), priority: z.enum(['low', 'normal', 'high', 'urgent']), displayType: z.enum(['banner', 'modal', 'notification']), targetAudience: z.enum(['all', 'organizations', 'users', 'specific']), targetGroups: z.array(z.string()).optional(), startDate: z.string().nullable().optional(), endDate: z.string().nullable().optional(), isActive: z.boolean(), views: z.number(), clicks: z.number(), dismissals: z.number(), createdBy: z.string().optional(), updatedBy: z.string().optional(), createdAt: z.string(), updatedAt: z.string(), }); export type Announcement = z.infer; export const announcementsResponseSchema = z.object({ announcements: z.array(announcementSchema), }); export type AnnouncementsResponse = z.infer; export const createAnnouncementRequestSchema = z.object({ title: z.string(), content: z.string(), priority: z.string().optional(), displayType: z.string().optional(), targetAudience: z.string().optional(), startDate: z.string().optional(), endDate: z.string().optional(), isActive: z.boolean().optional(), }); export type CreateAnnouncementRequest = z.infer; export const mediaAssetSchema = z.object({ id: z.string(), filename: z.string(), originalName: z.string(), url: z.string(), type: z.enum(['image', 'video', 'document', 'audio']), mimeType: z.string().optional(), size: z.number().optional(), width: z.number().nullable().optional(), height: z.number().nullable().optional(), duration: z.number().nullable().optional(), altText: z.string().optional(), tags: z.array(z.string()).optional(), uploadedBy: z.string().optional(), createdAt: z.string(), updatedAt: z.string(), }); export type MediaAsset = z.infer; export const mediaAssetsResponseSchema = z.object({ assets: z.array(mediaAssetSchema), }); export type MediaAssetsResponse = z.infer; /** * List static pages */ export async function listPages(): Promise { return apiGet('/admin/content/pages'); } /** * Get page by ID */ export async function getPage(id: string): Promise { return apiGet(`/admin/content/pages/${id}`); } /** * Create page */ export async function createPage(request: CreatePageRequest): Promise { return apiPost('/admin/content/pages', request); } /** * Update page */ export async function updatePage(id: string, request: UpdatePageRequest): Promise { return httpClient.put(`/admin/content/pages/${id}`, request); } /** * Delete page */ export async function deletePage(id: string): Promise<{ message: string }> { return apiDelete<{ message: string }>(`/admin/content/pages/${id}`); } /** * Publish page */ export async function publishPage(id: string): Promise { return apiPost(`/admin/content/pages/${id}/publish`, {}); } /** * List announcements */ export async function listAnnouncements(params?: { isActive?: boolean; priority?: string; }): Promise { const queryParams = new URLSearchParams(); if (params?.isActive !== undefined) queryParams.append('isActive', String(params.isActive)); if (params?.priority) queryParams.append('priority', params.priority); const query = queryParams.toString(); return apiGet(`/admin/content/announcements${query ? `?${query}` : ''}`); } /** * Get announcement by ID */ export async function getAnnouncement(id: string): Promise { return apiGet(`/admin/content/announcements/${id}`); } /** * Create announcement */ export async function createAnnouncement( request: CreateAnnouncementRequest ): Promise { return apiPost('/admin/content/announcements', request); } /** * Update announcement */ export async function updateAnnouncement( id: string, request: Partial ): Promise { return httpClient.put(`/admin/content/announcements/${id}`, request); } /** * Delete announcement */ export async function deleteAnnouncement(id: string): Promise<{ message: string }> { return apiDelete<{ message: string }>(`/admin/content/announcements/${id}`); } /** * List media assets */ export async function listMediaAssets(params?: { type?: string; tags?: string; }): Promise { const queryParams = new URLSearchParams(); if (params?.type) queryParams.append('type', params.type); if (params?.tags) queryParams.append('tags', params.tags); const query = queryParams.toString(); return apiGet(`/admin/content/media${query ? `?${query}` : ''}`); } /** * Get media asset by ID */ export async function getMediaAsset(id: string): Promise { return apiGet(`/admin/content/media/${id}`); } /** * Create media asset */ export async function createMediaAsset(request: { filename: string; originalName: string; url: string; type: string; mimeType?: string; size?: number; width?: number; height?: number; duration?: number; altText?: string; tags?: string[]; }): Promise { return apiPost('/admin/content/media', request); } /** * Update media asset */ export async function updateMediaAsset( id: string, request: { altText?: string; tags?: string[] } ): Promise { return httpClient.put(`/admin/content/media/${id}`, request); } /** * Delete media asset */ export async function deleteMediaAsset(id: string): Promise<{ message: string }> { return apiDelete<{ message: string }>(`/admin/content/media/${id}`); }