- Updated security configuration to include new API route for file uploads. - Added new endpoints in AdminApiController for user statistics, toggling user status, updating user roles, and managing user details. - Implemented doctor statistics and management endpoints, including toggling doctor status and creating new doctors. - Enhanced user listing with filtering options for roles and status. - Introduced DoctorFormPage component for adding new doctors with specialties selection. - Integrated react-leaflet for mapping functionalities and added necessary dependencies. - Updated package.json and package-lock.json to include new dependencies.
84 lines
2.1 KiB
TypeScript
84 lines
2.1 KiB
TypeScript
import { useAuthStore } from '../stores/authStore';
|
|
|
|
const BASE_URL = '';
|
|
|
|
function getToken(): string | null {
|
|
try {
|
|
const raw = localStorage.getItem('clinicpro-auth');
|
|
if (!raw) return null;
|
|
const parsed = JSON.parse(raw);
|
|
return parsed?.state?.token ?? null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export class ApiError extends Error {
|
|
constructor(
|
|
public status: number,
|
|
public code: string,
|
|
message: string,
|
|
) {
|
|
super(message);
|
|
}
|
|
}
|
|
|
|
async function request<T>(
|
|
path: string,
|
|
options: RequestInit = {},
|
|
): Promise<T> {
|
|
const token = getToken();
|
|
const headers: Record<string, string> = {
|
|
'Content-Type': 'application/json',
|
|
...(options.headers as Record<string, string>),
|
|
};
|
|
if (token) headers['Authorization'] = `Bearer ${token}`;
|
|
|
|
const res = await fetch(`${BASE_URL}${path}`, { ...options, headers });
|
|
|
|
if (!res.ok) {
|
|
if (res.status === 401) {
|
|
useAuthStore.getState().logout();
|
|
window.location.replace('/admin/login');
|
|
throw new ApiError(401, 'ERR_UNAUTHORIZED', 'نشست منقضی شده است');
|
|
}
|
|
const body = await res.json().catch(() => ({}));
|
|
const firstErr = body?.errors?.[0];
|
|
throw new ApiError(
|
|
res.status,
|
|
firstErr?.code ?? 'ERR_UNKNOWN',
|
|
firstErr?.message ?? 'خطای ناشناخته',
|
|
);
|
|
}
|
|
|
|
return res.json() as Promise<T>;
|
|
}
|
|
|
|
export const api = {
|
|
get: <T>(path: string) => request<T>(path),
|
|
post: <T>(path: string, body: unknown) =>
|
|
request<T>(path, { method: 'POST', body: JSON.stringify(body) }),
|
|
patch: <T>(path: string, body: unknown) =>
|
|
request<T>(path, { method: 'PATCH', body: JSON.stringify(body) }),
|
|
put: <T>(path: string, body: unknown) =>
|
|
request<T>(path, { method: 'PUT', body: JSON.stringify(body) }),
|
|
delete: <T>(path: string) => request<T>(path, { method: 'DELETE' }),
|
|
};
|
|
|
|
export interface ApiResponse<T> {
|
|
success: boolean;
|
|
data: T;
|
|
errors: { code: string; message: string; field?: string }[];
|
|
}
|
|
|
|
export interface PaginatedResponse<T> {
|
|
success: boolean;
|
|
data: T[];
|
|
meta: {
|
|
totalRecords: number;
|
|
totalPages: number;
|
|
currentPage: number;
|
|
};
|
|
errors: [];
|
|
}
|