import { AccessAction, AccessRecord, ApiResponse, BlacklistCreate, BlacklistItem, ChangePasswordRequest, CurrentUser, ExcelImportResult, InsideVisitor, LoginRequest, StatsSummary, PublicPass, VisitRequestCreate, VisitRequestView, Zone, } from './types'; const BASE = '/api'; /** Endpoints whose 401 must NOT trigger a redirect (login probe / public pages). */ const NO_REDIRECT_ON_401 = ['/auth/me', '/auth/login', '/auth/logout', '/public/']; async function request(path: string, init?: RequestInit): Promise { const res = await fetch(`${BASE}${path}`, { credentials: 'include', ...init, }); // Session expired / not authenticated on a protected call → send to login. if ( res.status === 401 && !NO_REDIRECT_ON_401.some((p) => path.startsWith(p)) && typeof window !== 'undefined' && !['/login', '/kiosk'].includes(window.location.pathname) && !window.location.pathname.startsWith('/pass/') ) { window.location.assign('/login'); } let body: ApiResponse | null = null; try { body = (await res.json()) as ApiResponse; } catch { // non-JSON (shouldn't happen with our envelope) } if (!res.ok || !body) { throw new Error(body?.message || `HTTP ${res.status}`); } return body.data as T; } function jsonInit(method: string, payload: unknown): RequestInit { return { method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }; } // ===== Auth ===== export const login = (req: LoginRequest) => request('/auth/login', jsonInit('POST', req)); export const logout = () => request('/auth/logout', { method: 'POST' }); export const getCurrentUser = () => request('/auth/me'); export const changePassword = (req: ChangePasswordRequest) => request('/auth/change-password', jsonInit('POST', req)); // ===== Zones ===== export const listZones = () => request('/zones'); // ===== Visit requests ===== export const listVisitRequests = () => request('/visit-requests'); export const listPendingRequests = () => request('/visit-requests/pending'); export const getVisitRequest = (id: number) => request(`/visit-requests/${id}`); export const createVisitRequest = (req: VisitRequestCreate) => request('/visit-requests', jsonInit('POST', req)); export const cancelVisitRequest = (id: number) => request(`/visit-requests/${id}/cancel`, { method: 'POST' }); export const uploadVisitRequests = (file: File) => { const form = new FormData(); form.append('file', file); return request('/visit-requests/upload', { method: 'POST', body: form, }); }; // ===== Approvals ===== export const approveRequest = (id: number, comment?: string) => request(`/approvals/${id}/approve`, jsonInit('POST', { comment })); export const rejectRequest = (id: number, comment?: string) => request(`/approvals/${id}/reject`, jsonInit('POST', { comment })); // ===== Access console (check-in / out, currently inside) ===== export const searchApprovedForCheckIn = (q: string) => request(`/access/search?q=${encodeURIComponent(q)}`); export const checkIn = (payload: { qrToken?: string; visitRequestId?: number; gateId?: string }) => request('/access/check-in', jsonInit('POST', payload)); export const checkOut = (payload: { qrToken?: string; visitRequestId?: number; gateId?: string }) => request('/access/check-out', jsonInit('POST', payload)); export const listInside = () => request('/access/inside'); export const listTodayAccess = () => request('/access/today'); // ===== Passes / badge ===== export const getPass = (id: number) => request(`/passes/${id}`); export const passQrUrl = (id: number) => `/api/passes/${id}/qr.png`; // ===== Public pass + self-service kiosk (no login, token-based) ===== export const getPublicPass = (token: string) => request(`/public/passes/${token}`); export const publicPassQrUrl = (token: string) => `/api/public/passes/${token}/qr.png`; export const publicCheckIn = (token: string) => request(`/public/passes/${token}/check-in`, { method: 'POST' }); export const publicCheckOut = (token: string) => request(`/public/passes/${token}/check-out`, { method: 'POST' }); // ===== Stats ===== export const getStatsSummary = () => request('/stats/summary'); // ===== Blacklist (ADMIN) ===== export const listBlacklist = () => request('/blacklist'); export const addBlacklist = (req: BlacklistCreate) => request('/blacklist', jsonInit('POST', req)); export const deleteBlacklist = (id: number) => request(`/blacklist/${id}`, { method: 'DELETE' }); // ===== Reports ===== export const reportDownloadUrl = (from: string, to: string) => `/api/reports/visits.xlsx?from=${from}&to=${to}`;