client: fix reset-error handling, undersized qty buttons, chart resize,
dedupe j()/errText(), stale-fetch guard, empty bar-picker state Six small, mostly-unrelated items from the #47 review round (#57): 1. 'Statistik zurücksetzen' now checks res.ok and shows the server's error instead of silently re-rendering the un-reset data on a 401/500 with no indication anything went wrong. 2. DayChart now tracks its container width via ResizeObserver and calls uPlot's setSize() on change — previously read once at plot-creation time, so a resize/orientation-change (more visible on the phone/ tablet path into /stats) left the chart the wrong width. 3. .qty-btn (cart +/- buttons) grown from 28x28px to 44x44px, the conventional minimum touch target — was the one conspicuously small interactive control in the money path, at a fast-moving bar counter. 4. Tablet's bar-config fetch (App.tsx) now guards against an out-of-order resolution if barId changes again while a previous api.config() call is still in flight. Currently unreachable (nothing triggers a second barId change mid-fetch today) but closes the gap before the next 'add a cancel button to the loading screen' change could make it live. 5. j()/errText() were duplicated byte-for-byte between Admin.tsx and Stats.tsx (Admin.tsx's own comment noted this exact duplication had already caused drift once before) — centralized in api.ts, both files now import instead of redeclaring. 6. Tablet's bar picker now shows an explicit empty-state message when zero bars exist (fresh install, or every bar deleted), previously indistinguishable from 'still loading'. Closes #57. Verified: tsc --noEmit and vite build both clean.
This commit is contained in:
parent
a7ec25997f
commit
7a58a4a1bb
7 changed files with 64 additions and 44 deletions
|
|
@ -1,21 +1,7 @@
|
|||
import { useEffect, useMemo, useState } from 'preact/hooks';
|
||||
import { formatCents } from '../api';
|
||||
import { errText, formatCents, j } from '../api';
|
||||
import { DayChart } from './DayChart';
|
||||
|
||||
async function j<T = any>(res: Response): Promise<T> {
|
||||
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
async function errText(res: Response): Promise<string> {
|
||||
try {
|
||||
const body = await res.json();
|
||||
return body.detail ?? body.title ?? body.error ?? res.statusText;
|
||||
} catch {
|
||||
return res.statusText;
|
||||
}
|
||||
}
|
||||
|
||||
interface Totals { bar_id: number; bar_name: string; tx_count: number; paid_cents: number; crew_count: number; pfand_returns: number }
|
||||
interface PerDrink { drink_id: number; drink_name: string; sold_qty: number }
|
||||
interface ByDay { day: string; tx_count: number; paid_cents: number; crew_count: number; pfand_returns: number }
|
||||
|
|
@ -128,7 +114,8 @@ function Login({ onAuthed }: { onAuthed: () => void }) {
|
|||
function Dashboard({ data, isAdmin, onReset }: { data: StatsData; isAdmin: boolean; onReset: () => void }) {
|
||||
async function reset() {
|
||||
if (!confirm('Wirklich die gesamte Statistik (alle Transaktionen) unwiderruflich löschen?')) return;
|
||||
await fetch('/admin/api/stats/reset', { method: 'POST' });
|
||||
const res = await fetch('/admin/api/stats/reset', { method: 'POST' });
|
||||
if (!res.ok) { alert(`Fehler: ${await errText(res)}`); return; }
|
||||
onReset();
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue