import type { components } from './schema'; export type AuthSession = components['schemas']['AuthSession']; export type ReportingPeriod = components['schemas']['ReportingPeriod']; export type AnalyticsRun = components['schemas']['AnalyticsRun']; export type SessionDetailPage = components['schemas']['SessionDetailPage']; export type SessionDetail = components['schemas']['SessionDetail']; export type Terminal = components['schemas']['Terminal']; export type TerminalList = components['schemas']['TerminalList']; export type SessionStatus = components['schemas']['SessionStatus']; export type DetailView = components['schemas']['DetailView']; export type FailureStage = components['schemas']['FailureStage']; export type Problem = components['schemas']['Problem']; const apiBase = (import.meta.env.PUBLIC_API_BASE_URL || '/api/v1').replace(/\/$/, ''); export class ApiError extends Error { constructor( public status: number, public problem?: Problem, public retryAfter?: number, ) { super(problem?.detail ?? 'در ارتباط با سرویس مشکلی پیش آمد.'); } } async function request(path: string, init: RequestInit = {}): Promise { const response = await fetch(`${apiBase}${path}`, { ...init, credentials: 'include', headers: { Accept: 'application/json', ...(init.body ? { 'Content-Type': 'application/json' } : {}), ...init.headers, }, }); if (!response.ok) { const type = response.headers.get('content-type'); const problem = type?.includes('application/problem+json') ? ((await response.json()) as Problem) : undefined; throw new ApiError(response.status, problem, Number(response.headers.get('retry-after')) || undefined); } return response.status === 204 ? (undefined as T) : ((await response.json()) as T); } export const api = { login(username: string, password: string) { return request('/auth/sessions', { method: 'POST', body: JSON.stringify({ username, password }), }); }, session() { return request('/auth/session'); }, logout(csrfToken: string) { return request('/auth/session', { method: 'DELETE', headers: { 'X-CSRF-Token': csrfToken }, }); }, periods() { return request('/analytics/periods'); }, terminals() { return request('/analytics/terminals'); }, startRun(periodId: string, csrfToken: string, terminalId?: string) { return request('/analytics/runs', { method: 'POST', headers: { 'X-CSRF-Token': csrfToken, 'Idempotency-Key': crypto.randomUUID(), }, body: JSON.stringify({ periodId, ...(terminalId ? { terminalId } : {}) }), }); }, run(runId: string) { return request(`/analytics/runs/${encodeURIComponent(runId)}`); }, retry(runId: string, sectionId: string, csrfToken: string) { return request( `/analytics/runs/${encodeURIComponent(runId)}/sections/${encodeURIComponent(sectionId)}/retry-attempts`, { method: 'POST', headers: { 'X-CSRF-Token': csrfToken, 'Idempotency-Key': crypto.randomUUID(), }, }, ); }, details( periodId: string, view: DetailView, options: { cursor?: string; failureStage?: FailureStage; terminalId?: string; status?: SessionStatus; minAmountRial?: number; maxAmountRial?: number; limit?: number; } = {}, ) { const query = new URLSearchParams({ periodId, view, limit: '25' }); if (options.cursor) query.set('cursor', options.cursor); if (options.failureStage) query.set('failureStage', options.failureStage); if (options.terminalId) query.set('terminalId', options.terminalId); if (options.status) query.set('status', options.status); if (options.minAmountRial !== undefined) query.set('minAmountRial', String(options.minAmountRial)); if (options.maxAmountRial !== undefined) query.set('maxAmountRial', String(options.maxAmountRial)); if (options.limit) query.set('limit', String(options.limit)); return request(`/analytics/session-details?${query}`); }, sessionDetail(sessionId: string) { return request(`/analytics/sessions/${encodeURIComponent(sessionId)}`); }, exportUrl( periodId: string, view: DetailView, options: { failureStage?: FailureStage; terminalId?: string; status?: SessionStatus; minAmountRial?: number; maxAmountRial?: number; } = {}, ) { const query = new URLSearchParams({ periodId, view }); if (options.failureStage) query.set('failureStage', options.failureStage); if (options.terminalId) query.set('terminalId', options.terminalId); if (options.status) query.set('status', options.status); if (options.minAmountRial !== undefined) query.set('minAmountRial', String(options.minAmountRial)); if (options.maxAmountRial !== undefined) query.set('maxAmountRial', String(options.maxAmountRial)); return `${apiBase}/analytics/session-details.csv?${query}`; }, };