mega-plant/lib/oil-log.ts
David df53130861 Add Workforce pages, Plant Clock tab bar and oil & diesel book-out
- Workforce: Employees register, per-employee dashboards, Attendance table
- Plant Clock: scan-only clock, bottom tab bar (Employees / Oil & Diesel / Job cards)
- Oil & Diesel stock: diesel tank (650 L, opening 589 L), level overwrite dialog,
  QR on each card, book-out screen with searchable vehicle picker,
  filterable "Booked out" table (today/week/month/all/range)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 11:39:18 +02:00

47 lines
2 KiB
TypeScript

/** Date filtering for the oil/diesel "Booked out" table. Days are South African calendar days. */
export type LogPreset = "today" | "week" | "month" | "all" | "range";
const dayFmt = new Intl.DateTimeFormat("en-CA", { timeZone: "Africa/Johannesburg" });
/** "2026-10-22" for an instant, in SA time. PocketBase stamps look like "2026-10-22 08:15:00.000Z". */
export function saDay(stamp: string): string {
if (!stamp || Number.isNaN(new Date(stamp.replace(" ", "T")).getTime())) return "";
return dayFmt.format(new Date(stamp.replace(" ", "T")));
}
function addDays(day: string, n: number): string {
const d = new Date(`${day}T00:00:00Z`);
d.setUTCDate(d.getUTCDate() + n);
return d.toISOString().slice(0, 10);
}
/** Inclusive [from, to] day bounds for a preset; null bound = open. Weeks start Monday. */
export function presetBounds(preset: LogPreset, today: string, from = "", to = ""): [string | null, string | null] {
switch (preset) {
case "today": return [today, today];
case "week": {
const dow = (new Date(`${today}T00:00:00Z`).getUTCDay() + 6) % 7; // Mon=0
return [addDays(today, -dow), today];
}
case "month": return [`${today.slice(0, 7)}-01`, today];
case "range": return [from || null, to || null];
default: return [null, null];
}
}
export function inBounds(day: string, [from, to]: [string | null, string | null]): boolean {
if (!day) return !from && !to; // undated rows only show when no period is applied
return (!from || day >= from) && (!to || day <= to);
}
/**
* When a movement happened. `created` is NOT reliable on oil_stock_movements (the collection
* was made through the API, so the field never auto-fills), so book-outs store a full ISO
* instant in `occurred_on`. Older rows hold a bare date. Returns "" when neither is usable.
*/
export function movementAt(m: { [k: string]: any }): string {
for (const v of [m.occurred_on, m.created] as (string | undefined)[]) {
if (v && !Number.isNaN(new Date(v.replace(" ", "T")).getTime())) return v;
}
return "";
}