337 lines
10 KiB
TypeScript
337 lines
10 KiB
TypeScript
import { apiFetch } from './client';
|
|
import { API_ENDPOINTS } from './config';
|
|
|
|
export interface SessionInfo {
|
|
session_id: string;
|
|
user_id: string;
|
|
device_name: string;
|
|
device_type: string;
|
|
browser: string;
|
|
operating_system: string;
|
|
ip_address: string;
|
|
expires_at: string;
|
|
is_active: boolean;
|
|
}
|
|
|
|
export interface AuditLogEntry {
|
|
audit_id: string;
|
|
request_id: string;
|
|
user_id: string | null;
|
|
entity_type: string;
|
|
entity_id: string;
|
|
action: string;
|
|
old_value: Record<string, unknown> | null;
|
|
new_value: Record<string, unknown> | null;
|
|
ip_address: string;
|
|
user_agent: string | null;
|
|
created_at: string;
|
|
user?: {
|
|
name: string;
|
|
avatarInitials: string;
|
|
} | null;
|
|
}
|
|
|
|
export interface DashboardRevenuePoint {
|
|
date: string;
|
|
revenue: number;
|
|
orders: number;
|
|
}
|
|
|
|
export interface DashboardMonthlyPoint {
|
|
label: string;
|
|
revenue: number;
|
|
orders: number;
|
|
}
|
|
|
|
export interface DashboardTopProduct {
|
|
name: string;
|
|
total_sold: number;
|
|
total_revenue: number;
|
|
}
|
|
|
|
export interface DashboardRecentOrder {
|
|
order_no: string;
|
|
amount: number;
|
|
status: string;
|
|
payment_status: string;
|
|
created_at: string | null;
|
|
customer_name: string;
|
|
customer_email: string | null;
|
|
}
|
|
|
|
export interface DashboardStats {
|
|
total_users: number;
|
|
total_orders: number;
|
|
total_products: number;
|
|
total_brands: number;
|
|
total_device_models: number;
|
|
total_device_series: number;
|
|
total_categories: number;
|
|
revenue_all_time: number;
|
|
revenue_last_30_days: number;
|
|
revenue_last_7_days: number;
|
|
orders_by_status: Record<string, number>;
|
|
revenue_chart_daily: DashboardRevenuePoint[];
|
|
revenue_chart_monthly: DashboardMonthlyPoint[];
|
|
top_products: DashboardTopProduct[];
|
|
recent_orders: DashboardRecentOrder[];
|
|
low_stock_variants: number;
|
|
out_of_stock_variants: number;
|
|
total_stock_units: number;
|
|
}
|
|
|
|
export const adminService = {
|
|
async listSessions(skip = 0, limit = 100): Promise<{ sessions: SessionInfo[] }> {
|
|
return apiFetch<{ sessions: SessionInfo[] }>(
|
|
`${API_ENDPOINTS.ADMIN_LIST_SESSIONS}?skip=${skip}&limit=${limit}`
|
|
);
|
|
},
|
|
|
|
async revokeSession(sessionId: string): Promise<{ detail: string }> {
|
|
return apiFetch<{ detail: string }>(
|
|
`${API_ENDPOINTS.ADMIN_REVOKE_SESSION}?session_id=${sessionId}`,
|
|
{ method: 'POST' }
|
|
);
|
|
},
|
|
|
|
async getFailedLogins(limit = 10): Promise<AuditLogEntry[]> {
|
|
return apiFetch<AuditLogEntry[]>(
|
|
`${API_ENDPOINTS.ADMIN_FAILED_LOGINS}?limit=${limit}`
|
|
);
|
|
},
|
|
|
|
async getAuditLogs(limit = 100): Promise<AuditLogEntry[]> {
|
|
return apiFetch<AuditLogEntry[]>(
|
|
`${API_ENDPOINTS.ADMIN_AUDIT_LOGS}?limit=${limit}`
|
|
);
|
|
},
|
|
|
|
async requestKillSwitch(approverId: string, mfaCode: string): Promise<{ detail: string }> {
|
|
return apiFetch<{ detail: string }>(
|
|
`${API_ENDPOINTS.ADMIN_KILL_SWITCH}?approver_id=${approverId}&mfa_code=${mfaCode}`,
|
|
{ method: 'POST' }
|
|
);
|
|
},
|
|
|
|
async cancelKillSwitch(adminId: string): Promise<{ detail: string }> {
|
|
return apiFetch<{ detail: string }>(
|
|
`${API_ENDPOINTS.ADMIN_CANCEL_KILL_SWITCH}?admin_id=${adminId}`,
|
|
{ method: 'POST' }
|
|
);
|
|
},
|
|
|
|
async selfDestruct(adminId: string, mfaCode: string): Promise<{ detail: string }> {
|
|
return apiFetch<{ detail: string }>(
|
|
`${API_ENDPOINTS.ADMIN_SELF_DESTRUCT}?admin_id=${adminId}&mfa_code=${mfaCode}`,
|
|
{ method: 'POST' }
|
|
);
|
|
},
|
|
|
|
async getDashboardStats(): Promise<DashboardStats> {
|
|
return apiFetch<DashboardStats>(API_ENDPOINTS.DASHBOARD_STATS);
|
|
},
|
|
|
|
async listServiceJobs(): Promise<any[]> {
|
|
return apiFetch<any[]>('/api/v1/service/jobs');
|
|
},
|
|
|
|
async fetchServiceCatalog(): Promise<any[]> {
|
|
return apiFetch<any[]>('/api/v1/service/catalog');
|
|
},
|
|
|
|
async createServiceBooking(payload: any): Promise<any> {
|
|
return apiFetch<any>('/api/v1/service/booking/create', {
|
|
method: 'POST',
|
|
body: JSON.stringify(payload)
|
|
});
|
|
},
|
|
|
|
async getServiceJobDetailsAdmin(jobId: string): Promise<any> {
|
|
return apiFetch<any>(`/api/v1/service/jobs/${jobId}`);
|
|
},
|
|
|
|
async submitDeviceIntake(jobId: string, payload: any): Promise<any> {
|
|
return apiFetch<any>(`/api/v1/service/jobs/${jobId}/intake`, {
|
|
method: 'POST',
|
|
body: JSON.stringify(payload)
|
|
});
|
|
},
|
|
|
|
async submitDiagnosticInspection(jobId: string, payload: any): Promise<any> {
|
|
return apiFetch<any>(`/api/v1/service/jobs/${jobId}/inspect`, {
|
|
method: 'POST',
|
|
body: JSON.stringify(payload)
|
|
});
|
|
},
|
|
|
|
async createOrReviseQuote(jobId: string, payload: any): Promise<any> {
|
|
return apiFetch<any>(`/api/v1/service/jobs/${jobId}/quotes`, {
|
|
method: 'POST',
|
|
body: JSON.stringify(payload)
|
|
});
|
|
},
|
|
|
|
async rescheduleAppointmentAdmin(jobId: string, payload: any): Promise<any> {
|
|
return apiFetch<any>(`/api/v1/service/jobs/${jobId}/reschedule`, {
|
|
method: 'POST',
|
|
body: JSON.stringify(payload)
|
|
});
|
|
},
|
|
|
|
async fetchDeviceTypes(): Promise<any[]> {
|
|
return apiFetch<any[]>('/api/v1/catalog/device-types');
|
|
},
|
|
|
|
async fetchBrands(): Promise<any[]> {
|
|
return apiFetch<any[]>('/api/v1/catalog/brands/all');
|
|
},
|
|
|
|
async fetchDeviceSeries(): Promise<any[]> {
|
|
return apiFetch<any[]>('/api/v1/catalog/device-series/all');
|
|
},
|
|
|
|
async fetchDeviceModels(): Promise<any[]> {
|
|
return apiFetch<any[]>('/api/v1/catalog/device-models/all');
|
|
},
|
|
|
|
async updateServiceJobStatus(jobId: string, status: string): Promise<any> {
|
|
return apiFetch<any>(`/api/v1/service/jobs/${jobId}/status?status=${encodeURIComponent(status)}`, {
|
|
method: 'POST'
|
|
});
|
|
},
|
|
|
|
async updateServiceJobLogistics(jobId: string, payload: { courier_name?: string; awb_number?: string; pickup_status?: string }): Promise<any> {
|
|
return apiFetch<any>(`/api/v1/service/jobs/${jobId}/logistics`, {
|
|
method: 'POST',
|
|
body: JSON.stringify(payload)
|
|
});
|
|
},
|
|
|
|
async fetchServiceTypes(): Promise<any[]> {
|
|
return apiFetch<any[]>('/api/v1/catalog/service-types/all');
|
|
},
|
|
|
|
async createServiceType(payload: { name: string; description?: string; icon_url?: string }): Promise<any> {
|
|
return apiFetch<any>('/api/v1/catalog/service-types/create', {
|
|
method: 'POST',
|
|
body: JSON.stringify(payload)
|
|
});
|
|
},
|
|
|
|
async fetchRepairServices(): Promise<any[]> {
|
|
return apiFetch<any[]>('/api/v1/catalog/repair-services/all');
|
|
},
|
|
|
|
async createRepairService(payload: { model_id: string; service_type_id: string; description?: string }): Promise<any> {
|
|
return apiFetch<any>('/api/v1/catalog/repair-services/create', {
|
|
method: 'POST',
|
|
body: JSON.stringify(payload)
|
|
});
|
|
},
|
|
|
|
async fetchRepairVariants(): Promise<any[]> {
|
|
return apiFetch<any[]>('/api/v1/catalog/repair-variants/all');
|
|
},
|
|
|
|
async createRepairVariant(payload: {
|
|
repair_service_id: string;
|
|
name: string;
|
|
price: number;
|
|
cost?: number;
|
|
duration_minutes?: number;
|
|
warranty_days?: number;
|
|
parts?: { part_id: string; quantity: number }[];
|
|
}): Promise<any> {
|
|
return apiFetch<any>('/api/v1/catalog/repair-variants/create', {
|
|
method: 'POST',
|
|
body: JSON.stringify(payload)
|
|
});
|
|
},
|
|
|
|
async attachJobMedia(jobId: string, category: string, fileIds: string[]): Promise<any> {
|
|
return apiFetch<any>(`/api/v1/service/jobs/${jobId}/media`, {
|
|
method: 'POST',
|
|
body: JSON.stringify({ category, file_ids: fileIds })
|
|
});
|
|
},
|
|
|
|
async uploadMediaFile(file: File): Promise<{ file_id: string; webp_path?: string; raw_path?: string }> {
|
|
const formData = new FormData();
|
|
formData.append('file', file);
|
|
return apiFetch<{ file_id: string; webp_path?: string; raw_path?: string }>('/api/v1/files/upload', {
|
|
method: 'POST',
|
|
body: formData
|
|
});
|
|
},
|
|
|
|
async uploadStorefrontImage(file: File): Promise<{ image_url: string; thumbnail_url: string; medium_url: string; large_url: string }> {
|
|
const formData = new FormData();
|
|
formData.append('file', file);
|
|
return apiFetch<{ image_url: string; thumbnail_url: string; medium_url: string; large_url: string }>('/api/v1/admin/storefront/upload-image', {
|
|
method: 'POST',
|
|
body: formData,
|
|
});
|
|
},
|
|
|
|
// ─── Storefront CMS ────────────────────────────────────────────────────────
|
|
async getCmsFooterInfo(): Promise<any> {
|
|
return apiFetch<any>('/api/v1/admin/storefront/cms/footer-info');
|
|
},
|
|
async updateCmsFooterInfo(payload: Record<string, unknown>): Promise<any> {
|
|
return apiFetch<any>('/api/v1/admin/storefront/cms/footer-info', {
|
|
method: 'PUT', body: JSON.stringify(payload),
|
|
});
|
|
},
|
|
|
|
async getCmsSettings(): Promise<any> {
|
|
return apiFetch<any>('/api/v1/admin/storefront/cms/settings');
|
|
},
|
|
async updateCmsSettings(payload: Record<string, unknown>): Promise<any> {
|
|
return apiFetch<any>('/api/v1/admin/storefront/cms/settings', {
|
|
method: 'PUT', body: JSON.stringify(payload),
|
|
});
|
|
},
|
|
|
|
async getCmsMegaMenu(): Promise<any> {
|
|
return apiFetch<any>('/api/v1/admin/storefront/cms/mega-menu');
|
|
},
|
|
async updateCmsMegaMenu(payload: { nav_key: string; groups?: unknown[]; promo?: unknown; featured_category_ids?: string[] }): Promise<any> {
|
|
return apiFetch<any>('/api/v1/admin/storefront/cms/mega-menu', {
|
|
method: 'PUT', body: JSON.stringify(payload),
|
|
});
|
|
},
|
|
|
|
async getCmsCatalogFilters(): Promise<any> {
|
|
return apiFetch<any>('/api/v1/admin/storefront/cms/catalog-filters');
|
|
},
|
|
async updateCmsCatalogFilters(payload: { highlights?: unknown[]; price_ranges?: unknown[] }): Promise<any> {
|
|
return apiFetch<any>('/api/v1/admin/storefront/cms/catalog-filters', {
|
|
method: 'PUT', body: JSON.stringify(payload),
|
|
});
|
|
},
|
|
|
|
async updateCmsCategory(categoryId: string, payload: Record<string, unknown>): Promise<any> {
|
|
return apiFetch<any>(`/api/v1/admin/storefront/cms/categories/${categoryId}`, {
|
|
method: 'PUT', body: JSON.stringify(payload),
|
|
});
|
|
},
|
|
|
|
async getCmsReviews(approved?: boolean): Promise<any[]> {
|
|
const q = approved !== undefined ? `?approved=${approved}` : '';
|
|
return apiFetch<any[]>(`/api/v1/admin/storefront/cms/reviews${q}`);
|
|
},
|
|
async approveReview(reviewId: string, adminReply?: string): Promise<any> {
|
|
return apiFetch<any>(`/api/v1/admin/storefront/cms/reviews/${reviewId}/approve`, {
|
|
method: 'PUT', body: JSON.stringify({ admin_reply: adminReply ?? null }),
|
|
});
|
|
},
|
|
async rejectReview(reviewId: string): Promise<any> {
|
|
return apiFetch<any>(`/api/v1/admin/storefront/cms/reviews/${reviewId}/reject`, { method: 'PUT' });
|
|
},
|
|
|
|
async fetchCustomers(): Promise<any[]> {
|
|
return apiFetch<any[]>('/api/v1/admin/customers');
|
|
},
|
|
};
|
|
|
|
|