mega-plant/components/SearchPicker.tsx
David c269cd00b4 Add Job Cards (vehicle-QR start/finish) and retire Operators/Mechanics pages
- Plant Clock "Job cards" tab: scan a vehicle QR to start a job, scan again to finish
  with odometer (or no meter), kind of work and a note
- /running/job-cards table with period, work, vehicle and mechanic filters
- Remove /running/operators and /running/mechanics; mechanic badge PDF moves to Employees

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 12:54:59 +02:00

28 lines
2 KiB
TypeScript

'use client';
import { useState } from 'react';
type Option = { id: string; label: string; sub?: string };
/** Type to filter, tap to choose. A native <select> cannot be searched on a phone. */
export function SearchPicker({ id, options, value, onChange, placeholder }: { id: string; options: Option[]; value: string; onChange: (id: string) => void; placeholder: string }) {
const chosen = options.find(o => o.id === value);
const [query, setQuery] = useState('');
const [open, setOpen] = useState(false);
const needle = query.trim().toLowerCase();
const matches = needle ? options.filter(o => `${o.label} ${o.sub ?? ''}`.toLowerCase().includes(needle)) : options;
return <div className="relative mt-1">
<input id={id} className="plant-clock-input" autoComplete="off" role="combobox" aria-expanded={open} aria-controls={`${id}-list`} placeholder={placeholder}
value={open ? query : chosen ? `${chosen.label}${chosen.sub ? ` · ${chosen.sub}` : ''}` : ''}
onFocus={() => { setQuery(''); setOpen(true); }}
onChange={e => { setQuery(e.target.value); setOpen(true); if (value) onChange(''); }}
onBlur={() => setTimeout(() => setOpen(false), 150)}
onKeyDown={e => { if (e.key === 'Enter') { e.preventDefault(); if (matches[0]) { onChange(matches[0].id); setOpen(false); } } if (e.key === 'Escape') setOpen(false); }}/>
{open && <ul id={`${id}-list`} role="listbox" className="absolute z-30 mt-1 w-full max-h-64 overflow-y-auto rounded-xl border border-border bg-surface shadow-lg">
{matches.length === 0 ? <li className="px-3 py-3 text-sm text-text-muted">Nothing matches “{query}”.</li>
: matches.map(o => <li key={o.id} role="option" aria-selected={o.id === value}>
<button type="button" className="w-full min-h-[44px] px-3 text-left text-sm hover:bg-bg"
onMouseDown={e => e.preventDefault()} onClick={() => { onChange(o.id); setOpen(false); }}>
<span className="font-semibold">{o.label}</span>{o.sub ? ` · ${o.sub}` : ''}</button></li>)}
</ul>}
</div>;
}