Files
bizmatch-app/web/src/api.ts
2026-07-23 17:48:44 -05:00

76 lines
2.0 KiB
TypeScript

export interface Staff {
id: string;
name: string;
active: boolean;
}
export type BusinessStatus = 'ACTIVE' | 'SOLD' | 'INACTIVE';
export interface BusinessListItem {
id: string;
name: string;
status: BusinessStatus;
}
export interface BusinessList {
businesses: BusinessListItem[];
counts: Record<BusinessStatus, number>;
}
export interface Business {
id: string;
name: string;
nas_path: string;
status: BusinessStatus;
}
export interface BusinessFile {
name: string;
size: number;
mtime: string;
}
export interface ScanResult {
scanned: number;
inserted: number;
updated: number;
missing: number;
}
export class ApiError extends Error {
constructor(readonly status: number, message: string) {
super(message);
}
}
async function request<T>(path: string, init?: RequestInit): Promise<T> {
const res = await fetch(path, { credentials: 'same-origin', ...init });
if (!res.ok) {
const body = (await res.json().catch(() => null)) as { error?: string } | null;
throw new ApiError(res.status, body?.error ?? `Request failed (${res.status})`);
}
return (await res.json()) as T;
}
function post<T>(path: string, body?: unknown): Promise<T> {
return request<T>(path, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify(body ?? {}),
});
}
export const api = {
me: () => request<Staff>('/api/me'),
staff: () => request<Staff[]>('/api/staff'),
login: (staffId: string) => post<{ ok: boolean; staff: Staff }>('/api/login', { staff_id: staffId }),
logout: () => post<{ ok: boolean }>('/api/logout'),
businesses: (status: BusinessStatus, search: string) =>
request<BusinessList>(
`/api/businesses?status=${encodeURIComponent(status)}&search=${encodeURIComponent(search)}`,
),
business: (id: string) => request<Business>(`/api/businesses/${id}`),
businessFiles: (id: string) => request<BusinessFile[]>(`/api/businesses/${id}/files`),
scan: () => post<ScanResult>('/api/businesses/scan'),
};