fix(shifts): operator filter — select over real operators, no more focus loss
The admin operator filter was a free-text input that broke three ways at once: its visibility hangs off the query response (scope === "all") and its value is part of the query key, so every keystroke started a new query, data went undefined for the round-trip, and the input UNMOUNTED mid-keystroke (lost focus, list blanking that read as a page reload). Filtering also silently failed — the server matches the operator by exact username, so partial text matched nothing. - keepPreviousData on the shifts query: previous data (and scope) stays live during refetch, so filter controls never unmount and the list never blanks on preset/filter changes. - The filter is now a <select> of operators that HAVE shifts: the server returns the distinct list (signed z-reports + the open shift's holder) on GET /api/shifts, admin scope only — operators still can't see other names. Exact match by construction. Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
@@ -177,6 +177,28 @@ export class ShiftService {
|
||||
* The open shift (no z_report yet) is intentionally excluded — it's not a
|
||||
* completed accountability period. Use `currentOpenShift()` for the live one.
|
||||
*/
|
||||
/**
|
||||
* Every operator that HAS a shift (closed z_reports + the open one, if any),
|
||||
* distinct + sorted — feeds the admin filter dropdown so it can only ever ask
|
||||
* for an operator that exists (the filter is an exact username match).
|
||||
*/
|
||||
listOperators(): string[] {
|
||||
const rows = this.#db
|
||||
.select()
|
||||
.from(ledgerEvents)
|
||||
.where(eq(ledgerEvents.type, "shift_z_report"))
|
||||
.all();
|
||||
const names = new Set<string>();
|
||||
for (const r of rows) {
|
||||
const op = ((r.payload ?? {}) as { operator?: string }).operator ?? r.identity;
|
||||
if (op) names.add(op);
|
||||
}
|
||||
const open = this.currentOpenShift();
|
||||
const openOp = open ? (((open.payload ?? {}) as { operator?: string }).operator ?? open.identity) : null;
|
||||
if (openOp) names.add(openOp);
|
||||
return [...names].sort((a, b) => a.localeCompare(b));
|
||||
}
|
||||
|
||||
listShifts(opts: { operator?: string; from?: string; to?: string } = {}): ShiftSummary[] {
|
||||
const rows = this.#db
|
||||
.select()
|
||||
|
||||
Reference in New Issue
Block a user