Adds complete initial codebase for the RJ Bulk Supplies fuel delivery backoffice: - Authentication pages and protected route layouts - Customer, supplier, fuel purchase, and delivery note management - Custom reporting and customer statement sending tools - Full UI component library and Tailwind styling - Server actions, API routes, and PocketBase integration - Development scripts and project configuration updates - Updated README and package metadata
123 lines
3.9 KiB
TypeScript
123 lines
3.9 KiB
TypeScript
import { cookies } from "next/headers";
|
|
import PocketBase from "pocketbase";
|
|
|
|
export const PB_URL =
|
|
process.env.POCKETBASE_URL ||
|
|
"http://pocketbase-zinlbpsext2ro0lgb2558vfy.169.239.180.130.sslip.io";
|
|
|
|
/**
|
|
* Plain fetch helper for RSC — avoids the PB SDK's auto-cancel wrapper.
|
|
*/
|
|
async function pbFetch(path: string, token?: string, init?: RequestInit) {
|
|
const res = await fetch(`${PB_URL}${path}`, {
|
|
...init,
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
...(token ? { Authorization: token } : {}),
|
|
...(init?.headers || {}),
|
|
},
|
|
cache: "no-store",
|
|
});
|
|
if (!res.ok) {
|
|
const text = await res.text();
|
|
throw new Error(`PB ${init?.method || "GET"} ${path} failed: ${res.status} ${text}`);
|
|
}
|
|
return res.json();
|
|
}
|
|
|
|
/**
|
|
* Get a PB client pre-loaded with the user's auth token from the cookie.
|
|
* Use this for actions/route handlers; for RSC use pbFetch() directly.
|
|
*/
|
|
export async function getServerPB() {
|
|
const cookieStore = await cookies();
|
|
const cookie = cookieStore.get("pb_auth");
|
|
const pb = new PocketBase(PB_URL);
|
|
if (cookie?.value) {
|
|
pb.authStore.save(cookie.value, null);
|
|
}
|
|
return pb;
|
|
}
|
|
|
|
/**
|
|
* Browser-side PB (realtime subscriptions, etc.)
|
|
*/
|
|
export function getBrowserPB() {
|
|
return new PocketBase(PB_URL);
|
|
}
|
|
|
|
/**
|
|
* PB record list response shape (subset we use).
|
|
*/
|
|
export interface PBList<T = any> {
|
|
items: T[];
|
|
page: number;
|
|
perPage: number;
|
|
totalItems: number;
|
|
totalPages: number;
|
|
}
|
|
|
|
/**
|
|
* Server-side PB helpers using plain fetch (RSC-safe).
|
|
*/
|
|
export async function pbList<T = any>(
|
|
collection: string,
|
|
opts: { page?: number; perPage?: number; filter?: string; sort?: string; fields?: string; expand?: string } = {},
|
|
token?: string,
|
|
): Promise<PBList<T>> {
|
|
const params = new URLSearchParams();
|
|
if (opts.page) params.set("page", String(opts.page));
|
|
if (opts.perPage) params.set("perPage", String(opts.perPage));
|
|
if (opts.filter) params.set("filter", opts.filter);
|
|
if (opts.sort) params.set("sort", opts.sort);
|
|
if (opts.fields) params.set("fields", opts.fields);
|
|
if (opts.expand) params.set("expand", opts.expand);
|
|
return pbFetch(`/api/collections/${collection}/records?${params}`, token);
|
|
}
|
|
|
|
/**
|
|
* Auto-paginated version of pbList. PB caps perPage at 1000 server-side,
|
|
* so for collections with >1000 records this is the only way to get them all.
|
|
* Loops server-side pagination until every page is consumed.
|
|
*/
|
|
export async function pbListAll<T = any>(
|
|
collection: string,
|
|
opts: { filter?: string; sort?: string; fields?: string; expand?: string; perPage?: number } = {},
|
|
token?: string,
|
|
): Promise<T[]> {
|
|
const perPage = opts.perPage ?? 1000;
|
|
const out: T[] = [];
|
|
let page = 1;
|
|
// Safety cap at 50 pages (50,000 records) — adjust if you have more
|
|
for (let i = 0; i < 50; i++) {
|
|
const res = await pbList<T>(collection, { ...opts, page, perPage }, token);
|
|
out.push(...res.items);
|
|
if (page >= res.totalPages) break;
|
|
page++;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
export async function pbGet<T = any>(collection: string, id: string, expand?: string, token?: string): Promise<T> {
|
|
const params = new URLSearchParams();
|
|
if (expand) params.set("expand", expand);
|
|
return pbFetch(`/api/collections/${collection}/records/${id}?${params}`, token);
|
|
}
|
|
|
|
export async function pbCreate<T = any>(collection: string, body: any, token?: string): Promise<T> {
|
|
return pbFetch(`/api/collections/${collection}/records`, token, {
|
|
method: "POST",
|
|
body: JSON.stringify(body),
|
|
});
|
|
}
|
|
|
|
export async function pbUpdate<T = any>(collection: string, id: string, body: any, token?: string): Promise<T> {
|
|
return pbFetch(`/api/collections/${collection}/records/${id}`, token, {
|
|
method: "PATCH",
|
|
body: JSON.stringify(body),
|
|
});
|
|
}
|
|
|
|
export async function pbDelete(collection: string, id: string, token?: string): Promise<void> {
|
|
await pbFetch(`/api/collections/${collection}/records/${id}`, token, { method: "DELETE" });
|
|
}
|