WFDASHDRIVER-APP/lib/pocketbase.ts
SJCoetz b80551fe4d refactor(ui): rebrand app, fix supplier naming
- rename `supplier` field to `suppliers` in OrderItem interface and PocketBase expand queries
- complete visual rebrand to warm cream (#f2f1ed) and dark brown (#26251e) color palette with custom CSS variables
- add accessibility improvements: skip-to-main content link, focus-visible styles, reduced motion support, and proper ARIA labels
- update all component styling to match new theme, including offline indicator, install prompt, login page, modals, map, bottom nav and global CSS
- standardize status colors: use green for success, red for errors, retain orange (#f54e00) as primary accent CTAs
2026-07-05 22:32:20 +02:00

154 lines
No EOL
4.9 KiB
TypeScript

import PocketBase from 'pocketbase';
import { AppSettings, DEFAULT_SETTINGS, OrderItem, DriverLocation } from './types';
const POCKETBASE_URL = process.env.NEXT_PUBLIC_POCKETBASE_URL || 'https://pocketbase-gnqc88suy8fqk6wyryjjdpvl.169.239.180.130.sslip.io';
export const pb = new PocketBase(POCKETBASE_URL);
// Disable auto-cancellation so React re-renders don't kill in-flight requests
pb.autoCancellation(false);
// Auth helpers
export const isAuthenticated = () => pb.authStore.isValid;
export const getCurrentUser = () => pb.authStore.model;
export const logout = () => {
pb.authStore.clear();
if (typeof window !== 'undefined') {
window.location.href = '/login/';
}
};
export const login = async (email: string, password: string) => {
return await pb.collection('users').authWithPassword(email, password);
};
// Get assigned collections (order_items assigned to current driver).
// The `order_items` schema has `supplier` (singular) and `driver`
// (singular) as relation field names — those are the keys to use
// in expand / sort / filter.
export const getAssignedCollections = async () => {
const user = getCurrentUser();
if (!user) return [];
// 1) Happy path — rich expand + sort by supplier name.
try {
const records = await pb.collection('order_items').getFullList<OrderItem>({
filter: `driver = "${user.id}" && (item_status = "assigned" || item_status = "driver_pool")`,
expand: 'supplier,order,purchase_order',
sort: 'supplier.suppliername',
});
return records as unknown as OrderItem[];
} catch (err: any) {
if (typeof console !== 'undefined') {
console.warn('[pocketbase] expand=supplier/sort failed, retrying without it:', err?.message || err);
}
}
// 2) Resilient fallback — items only, then batched suppliers.
const items = await pb.collection('order_items').getFullList<OrderItem>({
filter: `driver = "${user.id}" && (item_status = "assigned" || item_status = "driver_pool")`,
});
const supplierIds = Array.from(new Set(
items
.map((it) => (it as any).supplier)
.filter((id): id is string => typeof id === 'string' && id.length > 0)
));
let supplierMap = new Map<string, any>();
if (supplierIds.length > 0) {
try {
const filter = supplierIds.map((id) => `id = "${id}"`).join(' || ');
const suppliers = await pb.collection('suppliers').getFullList({ filter });
supplierMap = new Map(suppliers.map((s: any) => [s.id, s]));
} catch {
// Suppliers collection not reachable.
}
}
return items.map((it) => {
const supplierId = (it as any).supplier;
const supplier = supplierId ? supplierMap.get(supplierId) : null;
if (!supplier) return it;
return {
...it,
expand: { ...(it.expand || {}), supplier },
} as unknown as OrderItem;
});
};
// Update order item status when collected
export const updateCollectionStatus = async (
id: string,
status: string,
data: { qty_collected?: number; notes?: string; collected_date?: string; problem_type?: string; problem_flag?: boolean }
) => {
return await pb.collection('order_items').update(id, {
item_status: status,
...data,
});
};
// Report a problem on an order item
export const reportProblem = async (
orderItemId: string,
problemType: string,
description: string
) => {
return await pb.collection('order_items').update(orderItemId, {
item_status: 'problem',
problem_type: problemType,
problem_flag: true,
problem_notes: description,
});
};
// Location tracking - tries to save, fails silently if table doesn't exist
export const saveDriverLocation = async (location: Omit<DriverLocation, 'id'>) => {
try {
return await pb.collection('driver_locations').create(location);
} catch {
// Silently fail - table may not exist yet
}
};
// Get driver's location trail for today (for map polyline)
export const getDriverTrail = async (driverId: string) => {
try {
const today = new Date();
today.setHours(0, 0, 0, 0);
const records = await pb.collection('driver_locations').getFullList({
filter: `driver = "${driverId}" && timestamp >= "${today.toISOString()}"`,
sort: 'timestamp',
});
return records as unknown as DriverLocation[];
} catch {
return [];
}
};
// Settings helpers
const SETTINGS_KEY = '1driver_settings';
export const getSettings = (): AppSettings => {
if (typeof window === 'undefined') return DEFAULT_SETTINGS;
const saved = localStorage.getItem(SETTINGS_KEY);
if (saved) {
try {
return { ...DEFAULT_SETTINGS, ...JSON.parse(saved) };
} catch {
return DEFAULT_SETTINGS;
}
}
return DEFAULT_SETTINGS;
};
export const saveSettings = (settings: Partial<AppSettings>) => {
if (typeof window === 'undefined') return;
const current = getSettings();
const updated = { ...current, ...settings };
localStorage.setItem(SETTINGS_KEY, JSON.stringify(updated));
return updated;
};