diff --git a/documentation/22-znacka-a-design.md b/documentation/22-znacka-a-design.md index 335e12b..ca7c208 100644 --- a/documentation/22-znacka-a-design.md +++ b/documentation/22-znacka-a-design.md @@ -144,3 +144,29 @@ zakaznika v ukazkovych datech a prejmenovat ho nema duvod. | Materialy mimo web | prezentace, obrazky pro socialni site | | Domena | `worknuke.cz` v `brand.ts`, ale nikde neni nasazena | | Prochazeni `accent-*` | 341 mist, ktera uz nemaji duvod pouzivat druhy klic | + +## Rozmazani jen tam, kde neco prekryva + +`.glass` je podklad skoro kazdeho panelu a **nema `backdrop-filter`**. + +Puvodne mel `blur(14px)`, jenze pod kartami je plna barva (`bg-ink-950` na +dashboardu): rozmazavala se jednolita plocha, zadny rozdil videt nebyl a kazda +karta si za to drzela vlastni rozmazanou vrstvu ve skladaci. Pri dvaceti +kartach na obrazovce to vytizilo GPU natolik, ze vedle portalu sekalo +prehravani videa. + +Rozmazani patri jen tam, kde pod nim neco opravdu projizdi: + +| Kde | Trida | +| -------------------------- | ----------------- | +| lepici hlavicka dashboardu | `backdrop-blur-xl` | +| lepici hlavicka webu | `backdrop-blur-xl` | +| podklad modalu | `backdrop-blur-sm` | +| prekryv menu na mobilu | `backdrop-blur-sm` | + +**Zadna nekonecna animace nad velkou rozmazanou plochou.** Zare na prihlaseni +a na webu jsou staticke: animovat pruhlednost objektu s `blur(120px)` znamena +prepocitavat obrovskou vrstvu porad dokola, i kdyz se na strance nic nedeje. + +Kdo ma v systemu vypnute animace (`prefers-reduced-motion`), nedostane ani +ty zbyle. diff --git a/documentation/99-zmeny.md b/documentation/99-zmeny.md index e987fe8..8f7d12d 100644 --- a/documentation/99-zmeny.md +++ b/documentation/99-zmeny.md @@ -2,6 +2,60 @@ Nejnovejsi nahore. +## 2026-09-08 - Otevrena stranka sekala prehravani videa + +Pri otevrenem portalu zacalo vedle nej sekat prehravani videa, po zavreni +stranky bylo hned dobre. Neslo o smycku v kodu: **zivy prenos udalosti je SSE, +jedno spojeni na celou aplikaci**, a `setTimeout` je v nem jen na odstup pri +znovupripojeni. Zdrzeni bylo jinde a byly to tri veci. + +### Rozmazavani plne barvy + +`.glass`, tedy podklad skoro kazdeho panelu, mel `backdrop-filter: blur(14px)`. +Jenze **pod kartami je plna barva** (`bg-ink-950` na dashboardu), takze se +rozmazavala jednolita plocha: zadny rozdil videt nebyl a kazda karta si za to +drzela vlastni rozmazanou vrstvu ve skladaci. V kodu je `glass` na 58 mistech, +na jedne obrazovce jich je snadno dvacet. + +Rozmazani zustava tam, kde pod nim neco opravdu projizdi: lepici hlavicka +dashboardu i webu, podklad modalu a prekryv menu na mobilu. To je pet mist, +kazde se svym `backdrop-blur-*` primo u sebe. + +### Nekonecne animace nad velkymi rozmazanymi plochami + +Prihlaseni a verejny web meli zare o velikosti 26 az 34 rem s `blur(120px)`, +kterym se **donekonecna animovala pruhlednost**. Animovat pruhlednost silne +rozmazaneho objektu znamena prepocitavat obrovskou vrstvu sedesatkrat za +sekundu, porad, i kdyz se na strance nic nedeje. + +Zare zustaly, animace zmizela. Krome toho pribylo `prefers-reduced-motion`: kdo +si v systemu animace vypnul, nema je dostat ani tady. + +### Kazda udalost prekreslila cely dashboard + +`EventStreamProvider` mel v tomtez kontextu **stav spojeni, odber i seznam +poslednich udalosti**. Seznam se meni pri kazde udalosti, takze se menila cela +hodnota kontextu - a prekreslil se kazdy, kdo si jen zaridil odber. To jsou +vsechny dotazy s `refetchOn` na strance, i kdyz o ten typ udalosti nestaly. + +Pri behu automatizace, ktera posila `automation.run` a `ticket.updated`, se tak +cely dashboard prekresloval nekolikrat za sekundu - a s nim dvacet rozmazanych +vrstev. + +Kontext je proto rozdeleny: `useEventStream` da stav a odber, `useEventLog` +seznam udalosti. Seznam si bere jen ten, kdo ho opravdu vypisuje, tedy toasty. + +### Filtr sekci nefiltroval + +Seznam ticketu sklada adresu dotazu v `useMemo`, ale v zavislostech chybely +`groupId`, `typeId`, `tag` a `stage`. Adresa se tedy po prepnuti sekce +neprepocitala a data se nenacetla znovu - filtr vypadal, ze nedela nic. + +Driv to nevadilo: byly to filtry, ktere sly nastavit jen odkazem a po nacteni +stranky se uz nemenily. Se zalozkou "Vsechny tickety" a jejim vyberem sekce se +z toho stala chyba, ktera je videt. + +Overeno: `groupId=grp_servicedesk` vrati jeden ticket, `groupId=none` zbyle dva. ## 2026-09-07 - Podminka se muze ptat na vic veci naraz Podminka byla **prave jedna otazka**. Slozitejsi vetveni se muselo skladat diff --git a/web/src/components/dashboard/EventStreamProvider.tsx b/web/src/components/dashboard/EventStreamProvider.tsx index cb94889..d78cf3f 100644 --- a/web/src/components/dashboard/EventStreamProvider.tsx +++ b/web/src/components/dashboard/EventStreamProvider.tsx @@ -3,10 +3,16 @@ import type { ReactNode } from 'react'; import { connectEventStream, type StreamStatus } from '@/lib/eventStream'; import type { DashboardEvent, DashboardEventType } from '@/types/events'; +/** + * Stav spojeni a odber. **Bez seznamu udalosti.** + * + * Seznam se meni pri kazde udalosti a kdyz byl v teze hodnote kontextu, prekreslil + * se pri kazde udalosti kazdy, kdo si jen zaridil odber - tedy vsechny dotazy + * s `refetchOn` na cele strance, i kdyz o ten typ udalosti nestaly. Pri behu + * automatizace to znamenalo prekreslovani celeho dashboardu nekolikrat za sekundu. + */ interface EventStreamState { status: StreamStatus; - /** Poslednich par udalosti, nejnovejsi prvni. */ - events: DashboardEvent[]; /** Prihlaseni k odberu. Vraci funkci pro odhlaseni. */ subscribe: (listener: (event: DashboardEvent) => void) => () => void; } @@ -15,13 +21,15 @@ const noop = () => () => {}; const EventStreamContext = createContext({ status: 'closed', - events: [], subscribe: () => { console.warn('[stream] subscribe mimo EventStreamProvider, udalosti nedorazi'); return noop(); }, }); +/** Poslednich par udalosti, nejnovejsi prvni. Zvlast, protoze se meni porad. */ +const EventLogContext = createContext([]); + const MAX_KEPT = 30; /** @@ -59,18 +67,29 @@ export function EventStreamProvider({ children }: { children: ReactNode }) { }; }, []); - const value = useMemo( - () => ({ status, events, subscribe }), - [status, events, subscribe], - ); + const value = useMemo(() => ({ status, subscribe }), [status, subscribe]); - return {children}; + return ( + + {children} + + ); } export function useEventStream(): EventStreamState { return useContext(EventStreamContext); } +/** + * Posledni udalosti. Bere si je jen ten, kdo je opravdu vypisuje. + * + * Kdo chce jen reagovat na udalost, pouzije `useEventListener` - ten se + * prekresluje jen kdyz sam neco zmeni, ne pri kazde udalosti na strance. + */ +export function useEventLog(): DashboardEvent[] { + return useContext(EventLogContext); +} + /** Zavola callback jen pri udalostech uvedenych typu. */ export function useEventListener( types: DashboardEventType[], diff --git a/web/src/components/dashboard/EventToasts.tsx b/web/src/components/dashboard/EventToasts.tsx index e27d10c..e448d2c 100644 --- a/web/src/components/dashboard/EventToasts.tsx +++ b/web/src/components/dashboard/EventToasts.tsx @@ -1,7 +1,7 @@ import { AlarmClock, Bell, CheckCircle2, LifeBuoy, UserCheck, Webhook, Workflow, X } from 'lucide-react'; import { useEffect, useState } from 'react'; import type { LucideIcon } from 'lucide-react'; -import { useEventStream } from '@/components/dashboard/EventStreamProvider'; +import { useEventLog } from '@/components/dashboard/EventStreamProvider'; import { cn } from '@/lib/cn'; import type { DashboardEvent, DashboardEventType } from '@/types/events'; @@ -27,7 +27,7 @@ const style: Record = { /** Bubliny o tom, co se prave stalo. Bez nich by zive zmeny nebyly poznat. */ export function EventToasts() { - const { events } = useEventStream(); + const events = useEventLog(); const [visible, setVisible] = useState([]); const [dismissed, setDismissed] = useState>(new Set()); diff --git a/web/src/components/home/CallToAction.tsx b/web/src/components/home/CallToAction.tsx index f800c1d..93c77c4 100644 --- a/web/src/components/home/CallToAction.tsx +++ b/web/src/components/home/CallToAction.tsx @@ -14,7 +14,7 @@ export function CallToAction() {

{t('cta.titleFirst')} {t('cta.titleAccent')} diff --git a/web/src/index.css b/web/src/index.css index 1a77fc5..9aa9913 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -174,11 +174,21 @@ Vlastni utility. Pouzivat jen pro veci, ktere se opakuji vsude. ------------------------------------------------------------------- */ -/* Sklenena karta - zaklad vetsiny panelu na webu i v dashboardu */ +/* + * Sklenena karta - zaklad vetsiny panelu na webu i v dashboardu. + * + * **Bez `backdrop-filter`.** Bylo tu `blur(14px)`, jenze pod kartami je plna + * barva (`bg-ink-950` na dashboardu), takze se rozmazavala jednolita plocha: + * zadny rozdil videt nebyl a kazda karta si za to drzela vlastni rozmazanou + * vrstvu ve skladaci. Pri padesati kartach a zivem streamu udalosti to + * vytizilo GPU natolik, ze vedle toho sekalo prehravani videa. + * + * Rozmazani zustava tam, kde neco opravdu prekryva: lepici hlavicka + * a podklady modalu maji svoje `backdrop-blur-*` primo u sebe. + */ @utility glass { background-color: --alpha(var(--color-ink-800) / 70%); border: 1px solid --alpha(var(--color-ink-600) / 70%); - backdrop-filter: blur(14px); } /* @@ -209,3 +219,19 @@ @utility mask-fade-b { mask-image: linear-gradient(to bottom, black, transparent); } + +/* + * Kdo si v systemu vypnul animace, nema je dostat ani tady. + * + * Nekonecne animace jsou to jedine, co na klidne strance porad zamestnava + * skladac - a prave ty jsou tady zbytne. + */ +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } +} diff --git a/web/src/pages/Login.tsx b/web/src/pages/Login.tsx index cc8bada..33365bd 100644 --- a/web/src/pages/Login.tsx +++ b/web/src/pages/Login.tsx @@ -85,8 +85,8 @@ export default function Login() {
-
-
+
+
diff --git a/web/src/pages/dashboard/Tickets.tsx b/web/src/pages/dashboard/Tickets.tsx index 6d69807..d5cf72d 100644 --- a/web/src/pages/dashboard/Tickets.tsx +++ b/web/src/pages/dashboard/Tickets.tsx @@ -121,7 +121,10 @@ export default function Tickets() { if (channel) params.set('channel', channel); const search = params.toString(); return `/api/dashboard/tickets${search ? `?${search}` : ''}`; - }, [scope, tenantId, assignee, status, channel]); + // Vsechno, co se do adresy dosazuje, musi byt i tady. Chybel `groupId` + // a spol., takze prepnuti sekce adresu neprepocitalo a seznam se nenacetl + // znovu - filtr vypadal, ze nefunguje. + }, [scope, tenantId, assignee, status, channel, typeId, tag, stage, groupId]); const workloadPath = useMemo(() => { const params = new URLSearchParams();