Make the clock app's oil & diesel screen scan-only

Remove the manual product buttons and the on-device booked-out list; the full
history stays in the Booked out table on the Oil & Diesel Stock page.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
David 2026-10-22 11:53:31 +02:00
parent df53130861
commit 1dc1472841
2 changed files with 4 additions and 25 deletions

View file

@ -1,7 +1,6 @@
import { requireClockUser } from '@/lib/auth';
import { clockPB } from '@/lib/clock-store';
import { movementAt } from '@/lib/oil-log';
import { StockOut, type StockLogRow } from '@/components/StockOut';
import { StockOut } from '@/components/StockOut';
export const dynamic = 'force-dynamic';
export const metadata = { title: 'Book out oil & diesel · Mega Plant' };
@ -14,13 +13,8 @@ export default async function StockPage() {
pb.collection('oil_stock_movements').getFullList({ expand: 'item,asset' }),
]);
const onHand = (id: string) => moves.filter(m => m.item === id).reduce((s, m) => s + (m.kind === 'used' ? -(m.litres || 0) : m.litres || 0), 0);
const log: StockLogRow[] = moves.filter(m => m.kind === 'used').map((m): any => ({ ...m, at: movementAt(m) })).sort((a, b) => b.at.localeCompare(a.at)).slice(0, 15).map(m => ({
id: m.id, at: m.at, litres: m.litres, item: String(m.expand?.item?.name || ''),
colour: String(m.expand?.item?.colour || ''), asset: String(m.expand?.asset?.code || ''), by: String(m.booked_by || ''),
}));
return <StockOut
items={items.map(i => ({ id: i.id, code: i.code, name: i.name, colour: i.colour || '#30638E', onHand: Math.round(onHand(i.id) * 100) / 100, tank: String(i.code).startsWith('FUEL') }))}
assets={assets.map(a => ({ id: a.id, code: a.code, name: a.name }))}
log={log}
/>;
}

View file

@ -1,19 +1,17 @@
'use client';
import { useRef, useState } from 'react';
import { useRouter } from 'next/navigation';
import { AlertTriangle, CheckCircle2, Droplets, ScanLine, X } from 'lucide-react';
import { AlertTriangle, CheckCircle2, Droplets, X } from 'lucide-react';
import { BadgeCamera } from './BadgeCamera';
import { clockDate, clockTime } from '@/lib/clock-display';
import { oilCodeFromQr } from '@/lib/stock-out';
type Item = { id: string; code: string; name: string; colour: string; onHand: number; tank: boolean };
type Asset = { id: string; code: string; name: string };
export type StockLogRow = { id: string; at: string; litres: number; item: string; colour: string; asset: string; by: string };
const fmt = (n: number) => n.toLocaleString('en-ZA', { maximumFractionDigits: 2 });
/** Scan the drum/tank QR, say how many litres and which vehicle, and it is booked out. */
export function StockOut({ items, assets, log }: { items: Item[]; assets: Asset[]; log: StockLogRow[] }) {
export function StockOut({ items, assets }: { items: Item[]; assets: Asset[] }) {
const router = useRouter();
const [item, setItem] = useState<Item | null>(null);
const [litres, setLitres] = useState('');
@ -64,11 +62,7 @@ export function StockOut({ items, assets, log }: { items: Item[]; assets: Asset[
{!item ? <>
<div className="mt-4"><BadgeCamera onDecode={onDecode} paused={busy} hint="Scan the QR on the drum or tank"/></div>
{unknownScan && <p role="alert" className="plant-clock-warning mt-3">That QR is not an oil or diesel label ({unknownScan}). Scan the label on the drum or tank, or pick below.</p>}
<div className="mt-4"><p className="text-sm font-medium">Or choose the product</p>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-2 mt-2">{items.map(i =>
<button key={i.id} onClick={() => pick(i)} className="plant-clock-button plant-clock-secondary justify-start" style={{ borderColor: i.colour }}>
<Droplets size={18} style={{ color: i.colour }}/><span className="text-left min-w-0 truncate">{i.name}</span></button>)}</div></div>
{unknownScan && <p role="alert" className="plant-clock-warning mt-3">That QR is not an oil or diesel label ({unknownScan}). Scan the label on the drum or tank.</p>}
</> : <form onSubmit={submit} className="plant-clock-panel mt-4 p-4 sm:p-5">
<div className="flex items-start justify-between gap-3">
<div className="min-w-0"><div className="flex items-center gap-2"><Droplets size={20} style={{ color: item.colour }}/><h2 className="text-lg font-semibold break-words">{item.name}</h2></div>
@ -84,15 +78,6 @@ export function StockOut({ items, assets, log }: { items: Item[]; assets: Asset[
{busy ? 'Saving…' : 'Book out'}</button>
</form>}
<div className="plant-clock-panel mt-5 p-4 sm:p-5">
<h2 className="text-base font-semibold flex items-center gap-2"><ScanLine size={16}/>Booked out</h2>
{log.length === 0 ? <p className="text-sm text-text-muted mt-3">Nothing booked out yet.</p>
: <ul className="divide-y divide-border mt-2">{log.map(r => <li key={r.id} className="py-3 flex items-center gap-3">
<Droplets size={18} className="shrink-0" style={{ color: r.colour }}/>
<span className="min-w-0 flex-1"><span className="block text-sm font-medium break-words">{fmt(r.litres)} L {r.item} → {r.asset || 'no vehicle'}</span>
<span className="block text-xs text-text-muted">{r.at?clockDate(r.at.replace(' ','T')):''}{r.by ? ` · ${r.by}` : ''}</span></span>
<span className="text-sm tabular-nums shrink-0">{r.at.includes('T')?clockTime(r.at):''}</span></li>)}</ul>}
</div>
</section>;
}