// lib/api.ts // Browser-safe thin client that calls our own /api/pb proxy. // This module is "use client" friendly — it never imports PocketBase // directly and never touches process.env.PB_TOKEN. import type { PBList, ListParams } from "./types"; export interface ClientListParams extends ListParams { collection: string; } export interface ClientGetParams { collection: string; id: string; expand?: string; } /** Type-safe client fetcher for paginated lists. */ export async function pbList(params: ClientListParams): Promise> { const qs = new URLSearchParams(); qs.set("collection", params.collection); if (params.page) qs.set("page", String(params.page)); if (params.perPage) qs.set("perPage", String(params.perPage)); if (params.sort) qs.set("sort", params.sort); if (params.filter) qs.set("filter", params.filter); if (params.expand) qs.set("expand", params.expand); if (params.fields) qs.set("fields", params.fields); const res = await fetch(`/api/pb/records/list?${qs.toString()}`, { cache: "no-store" }); if (!res.ok) { const text = await res.text(); throw new Error(`PB list failed (${res.status}): ${text || res.statusText}`); } return res.json(); } export async function pbGet(params: ClientGetParams): Promise { const qs = new URLSearchParams(); qs.set("collection", params.collection); if (params.expand) qs.set("expand", params.expand); const res = await fetch(`/api/pb/records/${params.id}?${qs.toString()}`, { cache: "no-store" }); if (!res.ok) { const text = await res.text(); throw new Error(`PB get failed (${res.status}): ${text || res.statusText}`); } return res.json(); } /** * Single-purpose toggle for `is_client` on a director or company row. * Server enforces a hard allowlist; this client only sends * { collection, id, is_client }, nothing else. * * Used by the directors/companies table rows and the detail-page toggle. */ export async function pbToggleClient(params: { collection: "ysterbaard_directors" | "ysterbaard_companies"; id: string; is_client: boolean; }): Promise<{ ok: true; id: string; is_client: boolean; updated: string }> { const res = await fetch("/api/pb/records/toggle-client", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ collection: params.collection, id: params.id, is_client: params.is_client }) }); if (!res.ok) { const text = await res.text(); throw new Error(`PB toggle failed (${res.status}): ${text || res.statusText}`); } return res.json(); } /** * Creates a new record on any collection. * Only allowlisted fields pass through. */ export async function pbCreate(params: { collection: string; fields: Record; }): Promise { const res = await fetch("/api/pb/records/create", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(params), }); if (!res.ok) { const text = await res.text(); throw new Error(`PB create failed (${res.status}): ${text || res.statusText}`); } return res.json(); } /** * Creates a new director or company record with is_client=true. * Only the allowlisted fields pass through — no arbitrary PB writes. * * Used by the Add Director / Add Company modal forms. */ export async function pbCreateClient(params: { collection: "ysterbaard_directors" | "ysterbaard_companies"; fields: Record; }): Promise { const res = await fetch("/api/pb/records/create", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(params), }); if (!res.ok) { const text = await res.text(); throw new Error(`PB create failed (${res.status}): ${text || res.statusText}`); } return res.json(); } /** * Updates editable fields on an existing director or company record. * Only allowlisted fields pass through. Use for the in-app edit form. */ export async function pbPatch(params: { collection: string; id: string; fields: Record; }): Promise { const res = await fetch(`/api/pb/records/${params.id}?collection=${params.collection}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(params.fields), }); if (!res.ok) { const text = await res.text(); throw new Error(`PB patch failed (${res.status}): ${text || res.statusText}`); } return res.json(); } /** * Deletes a director or company record permanently. */ export async function pbDelete(params: { collection: string; id: string; }): Promise { const res = await fetch(`/api/pb/records/${params.id}?collection=${params.collection}`, { method: "DELETE", }); if (!res.ok) { const text = await res.text(); throw new Error(`PB delete failed (${res.status}): ${text || res.statusText}`); } }