Seed z repozitare, zatezove testy, worker bez spanku, dialogy bez rozmazani

Nastaveni prezije nasazeni: seed/records.json se pri prazdnem ulozisti
nacte misto ukazkovych dat (zive uloziste se nikdy neprepisuje). Soubor
nese soucasny stav produkce (firmy s provozovatelem, role, typ ticketu,
akce, widgety, skupiny, rozlozeni). Novy GET /api/admin/export a skript
npm run seed:export pro dalsi exporty, Dockerfile slozku kopiruje.

Vykonnostni testy (npm run test:perf) nad 200 firmami a 10 000 tickety
a zatezovy skript (npm run load) proti bezici instanci vcetne davky
udalosti na webhook. Mereni odhalilo strop workeru: po obsazeni vsech
mist spal sekundu, takze fronta odbavila nejvys 4 behy za sekundu. Ted
ceka na prvni dokonceny beh: 500 udalosti za 1,3 s (395 behu/s). Strop
posluchacu streamu zvednut na 2 000.

Dialogy: prekryv modalu a menu v portalu bez backdrop-blur, tecka Zive
pulzuje jen pri navazovani spojeni - rozmazani cele obrazovky pod trvalou
animaci sekalo video vedle portalu. Bublina udalosti drzi 0,5 s.

Dokumentace 14, 19, 20, 22, 04, 01, 03, 15 a 99 aktualizovana.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
JiriUhlir
2026-09-09 20:41:42 +02:00
co-authored by Claude Fable 5.1
parent c25e826766
commit ea9387bea9
44 changed files with 3512 additions and 28 deletions
@@ -280,7 +280,7 @@ function DashboardShell() {
type="button"
aria-label="Zavřít menu"
onClick={() => setSidebarPath(null)}
className="fixed inset-0 z-40 bg-ink-950/70 backdrop-blur-sm lg:hidden"
className="fixed inset-0 z-40 bg-ink-950/80 lg:hidden"
/>
)}
+8 -2
View File
@@ -14,8 +14,14 @@ import { useEventLog } from '@/components/dashboard/EventStreamProvider';
import { cn } from '@/lib/cn';
import type { DashboardEvent, DashboardEventType } from '@/types/events';
/** Jak dlouho bublina zustane, nez sama zmizi. */
const LIFETIME_MS = 6_000;
/**
* Jak dlouho bublina zustane, nez sama zmizi.
*
* Pul sekundy je rozhodnuti zadavatele (2026-09-09): bublina je zablesk
* "neco se stalo", ne text ke cteni. Podrobnosti jsou v seznamu, na ktery
* udalost ukazuje, a cislo u zalozky zustava.
*/
const LIFETIME_MS = 500;
const MAX_VISIBLE = 4;
// Partial: zmeny ciselniku (`person.updated` apod.) maji obecny vzhled nize.
@@ -1,9 +1,15 @@
import { useEventStream } from '@/components/dashboard/EventStreamProvider';
import { cn } from '@/lib/cn';
/*
* Pulz jen kdyz se neco deje (pripojuji, obnovuji). Stav "Zive" je vychozi
* a trva hodiny; nekonecna animace v hlavicce s `backdrop-blur` nutila
* prohlizec prepocitavat rozmazani porad dokola, a s otevrenym dialogem
* (dalsi vrstva pres celou obrazovku) to sekalo video vedle portalu.
*/
const meta = {
connecting: { label: 'Připojuji', dot: 'bg-warn-400', text: 'text-warn-400', pulse: true },
open: { label: 'Živě', dot: 'bg-ok-400', text: 'text-ok-400', pulse: true },
open: { label: 'Živě', dot: 'bg-ok-400', text: 'text-ok-400', pulse: false },
reconnecting: {
label: 'Obnovuji spojení',
dot: 'bg-warn-400',
+8 -1
View File
@@ -74,7 +74,14 @@ export function Modal({
return createPortal(
<div className="fixed inset-0 z-[60] flex items-start justify-center overflow-y-auto p-4 sm:items-center sm:p-6">
<div className="fixed inset-0 bg-ink-950/80 backdrop-blur-sm" onClick={onClose} aria-hidden />
{/*
Prekryv bez rozmazani, schvalne. `backdrop-blur` pres celou obrazovku
znamena, ze kazda animace pod dialogem (pulzujici tecka, spinner,
obnova seznamu) prepocita rozmazani cele plochy, a to jeste pres
rozmazanou hlavicku pod nim. Vedle portalu pak sekalo video. Tmavy
prekryv s vyssi krytim dela tutez praci zadarmo.
*/}
<div className="fixed inset-0 bg-ink-950/85" onClick={onClose} aria-hidden />
<div
role="dialog"