2026-05-18 16:02:43 +03:30
|
|
|
const baseUrl = import.meta.env.VITE_API_URL ?? 'http://localhost:3000';
|
|
|
|
|
|
|
|
|
|
export class ApiError extends Error {
|
|
|
|
|
constructor(
|
|
|
|
|
readonly status: number,
|
|
|
|
|
message: string,
|
|
|
|
|
) {
|
|
|
|
|
super(message);
|
|
|
|
|
this.name = 'ApiError';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type ApiOptions = RequestInit & {
|
|
|
|
|
token?: string;
|
|
|
|
|
};
|
|
|
|
|
|
2026-05-18 20:18:28 +03:30
|
|
|
export function getApiToken(): string | null {
|
|
|
|
|
return localStorage.getItem('montime_access_token');
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-18 16:02:43 +03:30
|
|
|
export async function api<T>(path: string, options: ApiOptions = {}): Promise<T> {
|
2026-05-18 20:18:28 +03:30
|
|
|
const token = options.token ?? getApiToken() ?? undefined;
|
2026-05-18 16:02:43 +03:30
|
|
|
const headers: Record<string, string> = {
|
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
|
...(options.headers as Record<string, string> | undefined),
|
|
|
|
|
};
|
|
|
|
|
|
2026-05-18 20:18:28 +03:30
|
|
|
if (token) {
|
|
|
|
|
headers.Authorization = `Bearer ${token}`;
|
2026-05-18 16:02:43 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { token: _token, ...fetchOptions } = options;
|
|
|
|
|
|
|
|
|
|
const res = await fetch(`${baseUrl}${path}`, {
|
|
|
|
|
...fetchOptions,
|
|
|
|
|
headers,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (!res.ok) {
|
|
|
|
|
const body = (await res.json().catch(() => null)) as
|
|
|
|
|
| { message?: string | string[] }
|
|
|
|
|
| null;
|
2026-05-25 20:22:51 +03:30
|
|
|
let msg: string;
|
|
|
|
|
if (Array.isArray(body?.message)) {
|
|
|
|
|
const lines = body.message.map(String);
|
|
|
|
|
const timeErrors = lines.filter((l) =>
|
|
|
|
|
/startTime|endTime/.test(l) && /regular expression|HH:mm/i.test(l),
|
|
|
|
|
);
|
|
|
|
|
msg =
|
|
|
|
|
timeErrors.length > 0
|
|
|
|
|
? 'فرمت ساعت نامعتبر است. لطفاً دوباره بازه را اضافه و ذخیره کنید.'
|
|
|
|
|
: lines.join(' · ');
|
|
|
|
|
} else {
|
|
|
|
|
msg = body?.message ?? res.statusText;
|
|
|
|
|
}
|
2026-05-18 16:02:43 +03:30
|
|
|
throw new ApiError(res.status, msg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (res.status === 204) {
|
|
|
|
|
return undefined as T;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return res.json() as Promise<T>;
|
|
|
|
|
}
|