Runtime vykonava strom, prokliky z widgetu, oprava ukladani rozlozeni
Runtime: `src/runtime/executor.ts` jde krok po kroku, u podminky se vetvi, do poli dosadi parametry, akci pusti pres runScript a vystupy pripise do kontextu pro dalsi krok. Cely prubeh jde do logu ticketu vcetne toho, co sluzba vratila. Pouzivaji ho obe cesty: akce na ticketu i webhook. Opraveno: rozlozeni dashboardu s vlastnim widgetem se NEDALO ULOZIT. `validateLayout` znala jen vestaveny katalog, takze kazdy pokus skoncil hlaskou "widget v katalogu neexistuje" - presne to, co hlasil uzivatel. Katalog je ted jedna funkce a pouziva ji nabidka i kontrola. Zaroven je za konkretni firmu, driv slo polozit dlazdici jedne firmy na dashboard druhe. Prokliky: z widgetu lidi na cloveka, ze seskupeni na vyfiltrovany seznam ticketu. Odkazy sklada server, protoze on jediny zna filtr widgetu. Seznam ticketu cte filtr z adresy a umi filtrovat na typ, tag a skupinu. Tabulky: spolecna `TicketTable` pro seznam i detail osoby. Na mobilu se neposouva do strany, uzka obrazovka dostane karty. Detail osoby ma velkou tabulku se zalozkami "ma u sebe" a "vyresil" a prepinacem pohledu. Odebrano: simulace vcetne tlacitka, dialogu i endpointu. Trojice pohledu nad tickety - vyber firmy je select, "moje" je prepinac, driv to delalo totez dvakrat. Pridan zmereny rozbor kapacity pro 200 firem (19-kapacita-200-firem.md): soucasny stav to nezvladne, protoze data jsou v pameti a vypis je linearni. Zmereno na 5 000 ticketech, vcetne toho, co s tim a kolik serveru to chce. Overeno 7 kontrolami proti bezicimu serveru. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
29de584df8
commit
5d186dcd2e
@@ -16,7 +16,6 @@ React aplikaci ze slozky `dist/public`.
|
||||
| Prihlaseni | hotovo | JWT, demo ucty |
|
||||
| Dashboard | hotovo | prehled, tickety, incidenty, automatizace, nastaveni |
|
||||
| Zivy dashboard pres SSE | hotovo | zmeny se projevi bez obnoveni stranky |
|
||||
| Simulace provozu | hotovo | tlacitko v postrannim menu portalu |
|
||||
| Katalog sluzeb | hotovo | 29 sluzeb, 7 kategorii vcetne Obecne |
|
||||
| Builder automatizaci | hotovo | strom akci, vetveni podminkou |
|
||||
| Webhook s registrovanou adresou | hotovo | token generuje server, verejny endpoint validuje data |
|
||||
@@ -48,7 +47,7 @@ React aplikaci ze slozky `dist/public`.
|
||||
| Telo akce jako strom | hotovo | tentyz editor jako automatizace |
|
||||
| Audit a prepnuti na jiny ucet | hotovo | prepnuti je vychozi jen pro cteni, vse v auditu |
|
||||
| Bugs a wishes | chybi | vyvojarska agenda, samostatna evidence vedle ticketu |
|
||||
| Beh automatizaci | chybi | ulozeny strom se nevykonava, neni runtime |
|
||||
| Beh automatizaci | castecne | strom se vykona, ale synchronne a bez fronty |
|
||||
| Uloziste konektoru | hotovo | Postgres, nebo JSON soubor. Udaje vzdy sifrovane |
|
||||
| Uloziste pro zbytek | hotovo | tickety, automatizace, incidenty, rozlozeni, entity |
|
||||
| Monetizace a cena za krok | navrh | popis v 16-monetizace.md, neni naprogramovane |
|
||||
@@ -66,9 +65,11 @@ v portalu i v `/health/ready` a rozhoduje o nem jedno misto, viz
|
||||
| `file` | neni databaze, je `DATA_DIR` | prezije restart, ne redeploy |
|
||||
| `memory` | neni ani `DATA_DIR` | ztrati se pri restartu |
|
||||
|
||||
Beh automatizaci zatim neexistuje: ulozeny strom se nevykonava, provoz se dela
|
||||
simulaci. Popis, jak to ma vypadat, je v
|
||||
[10-runtime-a-kapacita.md](10-runtime-a-kapacita.md).
|
||||
Beh automatizaci uz existuje, ale je **synchronni v requestu**: webhook ceka,
|
||||
nez cely strom dobehne, a pri padu procesu se rozdelany beh ztrati. Neni fronta
|
||||
ani opakovani. Co to znamena pro vetsi provoz a co s tim, je zmerene
|
||||
v [19-kapacita-200-firem.md](19-kapacita-200-firem.md), navrh fronty
|
||||
v [10-runtime-a-kapacita.md](10-runtime-a-kapacita.md).
|
||||
|
||||
Obsah verejneho webu je ukazkovy. Nazev firmy, reference, tym i cisla jsou
|
||||
vymyslene a pred ostrym pouzitim se musi nahradit. Firemni udaje jsou na jednom
|
||||
@@ -77,8 +78,9 @@ miste v `web/src/config/brand.ts`.
|
||||
Zivy stream drzi seznam posluchacu v pameti jedne instance. Pri vice instancich
|
||||
by ho musel nahradit sdileny kanal, napriklad Redis pub/sub.
|
||||
|
||||
Log ticketu zatim plni simulace, ne skutecny beh. Zaznamy jsou realisticke,
|
||||
ale nevznikly vykonanim ulozeneho stromu - runtime neexistuje.
|
||||
Log ticketu uz plni skutecny beh: kazdy krok stromu se do nej zapise vcetne
|
||||
toho, co sluzba vratila. Ukazkova sada ticketu ma log psany rucne, aby bylo
|
||||
co ukazat i na prazdne instanci.
|
||||
|
||||
## Dalsi krok
|
||||
|
||||
@@ -124,4 +126,5 @@ pro frontu, beh kroku a rozpocet na 150 klientu, a
|
||||
| [16-monetizace.md](16-monetizace.md) | **navrh**: cena za krok a balicky |
|
||||
| [17-nastaveni-a-prava.md](17-nastaveni-a-prava.md) | prava, typy, akce, widgety, prepnuti uctu |
|
||||
| [18-ticketovaci-system.md](18-ticketovaci-system.md) | udalosti, externi ID, statistiky, pohledy |
|
||||
| [19-kapacita-200-firem.md](19-kapacita-200-firem.md) | zmereno, co zvladne soucasny stav |
|
||||
| [99-zmeny.md](99-zmeny.md) | zaznam zmen, nejnovejsi nahore |
|
||||
|
||||
@@ -80,7 +80,6 @@ Vyzaduji `Authorization: Bearer <token>`:
|
||||
| POST | `/api/admin/impersonate/stop` |
|
||||
| GET | `/api/admin/impersonate/candidates` |
|
||||
| GET | `/api/admin/audit` |
|
||||
| POST | `/api/simulate` |
|
||||
|
||||
Sprava zaznamu ma u kazde entity stejnou petici (seznam, detail, vytvoreni,
|
||||
uprava, mazani) na `/api/dashboard/settings/<entita>`, protoze ji dela jedna
|
||||
@@ -246,21 +245,6 @@ s 403. Kazde prepnuti i ukonceni je v auditu vcetne toho, kdo to byl doopravdy.
|
||||
|
||||
Svuj puvodni token si klient odklada do `sessionStorage`, server o nem nic nevi.
|
||||
|
||||
## Simulace
|
||||
|
||||
`POST /api/simulate` vyvola provozni udalost pro nahled ziveho dashboardu.
|
||||
Zamerne meni skutecna data, ne jen posila falesnou notifikaci.
|
||||
|
||||
Akce: `ticket.created`, `ticket.resolved`, `incident.started`,
|
||||
`incident.resolved`, `automation.run`.
|
||||
|
||||
U `ticket.created` urcuje `channel` (whatsapp, facebook, instagram, email, voice,
|
||||
form, portal), odkud pozadavek prisel, a podle toho se poskladá i log ticketu. `knownCustomer: false` znamena, ze CRM firmu nedohleda -
|
||||
ticket zustane bez zakaznika i bez resitele a v logu je videt proc.
|
||||
|
||||
Nevyplnena pole server doplni ukazkovou hodnotou. U akci s "resolved" se bez
|
||||
zadaneho id pouzije prvni nevyrizeny zaznam.
|
||||
|
||||
## Sluzby a konektory
|
||||
|
||||
Popis modelu je v [12-sluzby-a-konektory.md](12-sluzby-a-konektory.md), tady jen API.
|
||||
|
||||
@@ -37,6 +37,8 @@ Volající nikdy nezjišťuje, jestli běží Postgres, soubor, nebo pamět.
|
||||
| `hasPermission(...)` | `src/data/permissions.ts` | Jedna kontrola. Používá ji `crudRouter` i ruční handlery. |
|
||||
| `navFor(...)` | `src/data/tenantFeatures.ts` | Průnik toho, co firma má, a toho, na co má člověk právo. Navigace chodí ze serveru. |
|
||||
| `recordAudit(input)` | `src/data/audit.ts` | Zápis do auditu. Nevrací chybu a nečeká se - rozbitý audit nesmí rozbít aplikaci. |
|
||||
| `runFlow(steps, context, options)` | `src/runtime/executor.ts` | Vykoná strom kroků. Nikdy nevyhodí výjimku, chyba je výsledek. Používá to akce na ticketu i webhook, aby se strom choval všude stejně. |
|
||||
| `widgetCatalog(tenantIds, userId)` | `src/data/widgets.ts` | Jediná definice toho, co jde položit na dashboard. Používá ji nabídka i kontrola ukládaného rozložení. |
|
||||
| `intakeEvent(input)` | `src/data/ticketStore.ts` | Přijme událost zvenku: podle externího ID buď založí ticket, nebo ji navěsí na existující. Jediná cesta, kterou se událost stává ticketem. |
|
||||
| `getAgentStats(...)` | `src/data/ticketStore.ts` | Výkon řešitelů: odbavené, mediány časů, vrácené, fronta. Používá to widget i detail osoby, aby čísla seděla. |
|
||||
| `findByExternalId(...)` | `src/data/ticketStore.ts` | Ticket firmy podle externího ID. Klíč je dvojice firma a ID. |
|
||||
@@ -69,6 +71,7 @@ Viz [11-skripty-konektoru.md](11-skripty-konektoru.md).
|
||||
| `TicketActions` | `components/dashboard/TicketActions.tsx` | CTA akcí na ticketu plus typ, tagy a vlastní pole. Seznam akcí chodí ze serveru už vyfiltrovaný. |
|
||||
| `ErrorDetail` | `components/dashboard/ErrorDetail.tsx` | Rozbalovací celé chybové hlášení s kopírováním. Chyba se nikdy nezkracuje. |
|
||||
| `CustomWidgetCard` | `components/dashboard/widgets/CustomWidget.tsx` | Vykreslí widget, jehož data počítá server: číslo, pruhy, tabulka výkonu, časová řada, seznam, data z konektoru. |
|
||||
| `TicketTable` | `components/dashboard/TicketTable.tsx` | Tabulka ticketů pro všechna místa. Na mobilu se místo posouvání do strany kreslí karty. |
|
||||
| `TicketEvents` | `components/dashboard/TicketEvents.tsx` | Příchozí události ticketu včetně celého přijatého JSONu. |
|
||||
| `ViewSwitch` | `components/dashboard/ViewSwitch.tsx` | Přepínač tabulka nebo dlaždice. Používají ho všechny seznamy. |
|
||||
| `FlowCanvas` s `start` | `components/dashboard/flow/FlowCanvas.tsx` | Tentýž strom kroků i bez spouštěče - pro tělo akce, které spouští člověk. |
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
# Kapacita: 200 firem, 10 automatizací, 15 lidí na firmu
|
||||
|
||||
Otázka zněla: zvládne to současný stav s Postgresem, a co to bude chtít
|
||||
za server. Odpověď je změřená, ne odhadnutá.
|
||||
|
||||
**Krátce: v současném stavu ne.** Runtime běží, ale datová vrstva je psaná
|
||||
na stovky až tisíce ticketů, ne na miliony. Co konkrétně to zastaví a v jakém
|
||||
pořadí to opravit, je níž.
|
||||
|
||||
## Co se měřilo
|
||||
|
||||
Jeden proces, režim souboru, tickety se posílaly přes příjem událostí.
|
||||
Měřeno na vývojovém stroji, tedy horní hranice latence, ne serveru.
|
||||
|
||||
| Ticketů | Výpis seznamu | Statistiky řešitelů | Soubor |
|
||||
| --- | --- | --- | --- |
|
||||
| 103 | 1,9 ms | 1,6 ms | 153 kB |
|
||||
| 503 | 6,1 ms | 12,1 ms | 729 kB |
|
||||
| 1 003 | 14,2 ms | 2,7 ms | 1,4 MB |
|
||||
| 2 003 | 18,8 ms | 21,1 ms | 2,9 MB |
|
||||
| 5 003 | 52,9 ms | 1,7 ms | 7,2 MB |
|
||||
|
||||
Příjem událostí: **130 až 190 událostí za sekundu** včetně celého kola
|
||||
HTTP, uložení a zápisu do logu.
|
||||
|
||||
Z toho plyne:
|
||||
|
||||
- **jeden ticket = 1,4 kB** v uložišti,
|
||||
- **výpis roste lineárně**, zhruba 10 mikrosekund na ticket, protože se
|
||||
filtruje pole v paměti,
|
||||
- v režimu souboru se **při každé změně přepisuje celý soubor**. Při pěti
|
||||
tisících ticketech je to 7 MB zápisu kvůli jednomu komentáři. S Postgresem
|
||||
to odpadá, tam je to jeden řádek.
|
||||
|
||||
## Kolik toho bude
|
||||
|
||||
200 firem, 10 automatizací každá, 15 lidí. Odhad provozu:
|
||||
|
||||
| Veličina | Výpočet | Za den | Za měsíc |
|
||||
| --- | --- | --- | --- |
|
||||
| Běhy automatizací | 2 000 automatizací, 100 běhů denně | 200 000 | 6 mil. |
|
||||
| Kroky | 3 kroky na běh | 600 000 | 18 mil. |
|
||||
| Tickety | 200 firem, 200 denně | 40 000 | 1,2 mil. |
|
||||
| Řádky logu | 5 na ticket plus kroky | ~800 000 | 24 mil. |
|
||||
|
||||
Špička není průměr. 7 kroků za sekundu v průměru znamená ve špičce klidně
|
||||
100 za sekundu, protože e-shopy neposílají objednávky rovnoměrně.
|
||||
|
||||
## Co to v současném stavu zastaví
|
||||
|
||||
### 1. Všechna data jsou v paměti procesu
|
||||
|
||||
`withMirror` drží tickety, automatizace a incidenty v poli a při startu je
|
||||
**všechny načte**. Při 1,2 milionu ticketů měsíčně je to 1,7 GB jen na tickety,
|
||||
a to bez logu. Aplikace se nenastartuje.
|
||||
|
||||
Opravit se musí tak, že se tickety čtou dotazem s indexem a stránkováním,
|
||||
ne z pole. `withMirror` má zůstat na tom, co je malé a čte se pořád (rozložení
|
||||
dashboardu), ne na provozních datech.
|
||||
|
||||
### 2. Výpis prochází všechno
|
||||
|
||||
`listTickets` filtruje pole přes všechny firmy. Změřeno 10 mikrosekund na
|
||||
ticket, takže při milionu ticketů je jeden výpis 10 sekund. Musí to být
|
||||
`WHERE tenant_id = ... AND status = ... ORDER BY updated_at LIMIT 50` nad
|
||||
indexem, tedy jednotky milisekund bez ohledu na objem.
|
||||
|
||||
### 3. Log je uvnitř ticketu
|
||||
|
||||
Ticket si nese `trace` i `events` jako součást jednoho záznamu. Každý řádek
|
||||
logu tím přepisuje celý ticket i s celým dosavadním logem. Po padesáti krocích
|
||||
je to padesát zápisů, každý delší než ten předchozí.
|
||||
|
||||
Log patří do vlastní tabulky s cizím klíčem na ticket a s retencí. Při 24
|
||||
milionech řádků měsíčně a plné odpovědi služby u každého je to řádově
|
||||
**stovky GB za měsíc**, takže retence není detail, ale podmínka provozu.
|
||||
|
||||
### 4. Runtime běží v požadavku
|
||||
|
||||
`runFlow` vykoná strom rovnou v HTTP požadavku. U ruční akce je to správně,
|
||||
u webhooku ne:
|
||||
|
||||
- odesílatel čeká, než doběhnou všechny kroky,
|
||||
- při pádu procesu se rozdělaný běh ztratí, protože není kde by byl zapsaný,
|
||||
- není retry, není omezení souběhu na jednu službu, nic to nebrzdí.
|
||||
|
||||
### 5. Statistiky se počítají celé
|
||||
|
||||
`getAgentStats` projde všechny tickety firmy při každém zobrazení widgetu.
|
||||
Při desítkách tisíc na firmu to jde, při milionu ne. Patří to do agregace
|
||||
počítané dávkově, nebo aspoň do dotazu s `GROUP BY` nad indexem.
|
||||
|
||||
### 6. Jedna instance
|
||||
|
||||
Živý stream (SSE), mezipaměť widgetů nad konektory i kopie konfigurace
|
||||
v paměti jsou vázané na proces. Při dvou instancích za load balancerem se
|
||||
rozejdou. Řeší to `LISTEN/NOTIFY` na obnovu kopií a sdílený kanál na stream.
|
||||
|
||||
## Konektory nejsou v naší moci
|
||||
|
||||
Za rychlost a výsledek cizí služby neručíme, a proto se s tím musí počítat
|
||||
v návrhu, ne v provozu:
|
||||
|
||||
| Riziko | Co s tím |
|
||||
| --- | --- |
|
||||
| Služba odpovídá pomalu | Timeout na krok, ne na celý běh. Běh se uspí a pokračuje. |
|
||||
| Služba je chvíli mimo | Opakování s rostoucí prodlevou, ne hned a ne donekonečna. |
|
||||
| Služba je mimo dlouho | Vypnout ji po sérii chyb a nezkoušet každý běh znovu, ať netrpí ostatní. |
|
||||
| Služba má limit volání | Strop souběžných volání **na dvojici firma a služba**, ne globálně. |
|
||||
| Služba odpoví dvakrát jinak | Klíč proti dvojímu provedení u kroku, aby se nevystavila druhá faktura. |
|
||||
| Služba je pomalá jen pro jednu firmu | Fronta po firmách, aby jedna firma nezablokovala ostatní. |
|
||||
|
||||
Timeout a rozlišení "zkusit znovu" a "marné" už v `scripts/http.ts` je,
|
||||
klíč proti dvojímu provedení taky. Chybí to, co je nad tím: fronta, opakování
|
||||
a vypínání služby po sérii chyb.
|
||||
|
||||
## Co je potřeba udělat, v pořadí
|
||||
|
||||
1. **Tickety, běhy a log do Postgresu jako tabulky**, ne jako JSON v paměti.
|
||||
Indexy na `(tenant_id, updated_at)`, `(tenant_id, external_id)` unikátní,
|
||||
`(tenant_id, assignee_id, status)`. Stránkování na všech výpisech.
|
||||
2. **Log a události zvlášť** od ticketu, s retencí. Bez toho to zaroste.
|
||||
3. **Fronta běhů.** Tabulka `run_queue`, výběr přes `SELECT ... FOR UPDATE
|
||||
SKIP LOCKED`, worker jako druhý proces téhož obrazu. Webhook jen zapíše
|
||||
událost a odpoví, běh se stane na pozadí.
|
||||
4. **Opakování a vypínání služby** kolem kroku.
|
||||
5. **Agregace statistik** dávkově, ne při každém zobrazení.
|
||||
6. **`LISTEN/NOTIFY`** na obnovu kopií v paměti a sdílený kanál na živý stream.
|
||||
|
||||
Body 1 až 3 jsou podmínka, aby to vůbec šlo pustit. Zbytek je o tom, aby to
|
||||
bylo použitelné.
|
||||
|
||||
## Kolik serveru to bude chtít
|
||||
|
||||
Po těch úpravách, pro zadaných 200 firem:
|
||||
|
||||
| Část | Kolik | Proč |
|
||||
| --- | --- | --- |
|
||||
| Web a API | 2 instance, 1 vCPU a 1 GB každá | Požadavky jsou krátké, jde hlavně o dostupnost při restartu. |
|
||||
| Worker běhů | 2 instance, 1 vCPU a 512 MB každá | Kroky čekají na cizí službu, procesor se skoro nepoužije. Jeden proces zvládne stovky souběžných kroků. |
|
||||
| Postgres | 4 vCPU, 8 GB RAM, 200 GB disku | 10 až 20 zápisů za sekundu v průměru je málo, disk sežere log. |
|
||||
| Celkem | ~8 vCPU, ~11 GB RAM | |
|
||||
|
||||
Kritické číslo není procesor, ale **disk pod databází** a retence logu.
|
||||
S plnou odpovědí služby u každého kroku je to zhruba 20 GB měsíčně na
|
||||
každých 20 milionů řádků, takže 200 GB je půl roku provozu. Buď se odpovědi
|
||||
po měsíci zahazují a nechá se jen shrnutí, nebo se počítá s tím, že disk
|
||||
poroste.
|
||||
|
||||
Škálování je vodorovné: víc událostí znamená víc workerů, ne větší stroj.
|
||||
Jediné, co se škáluje svisle, je databáze, a ta má u tohoto objemu ještě
|
||||
velkou rezervu.
|
||||
|
||||
## Co funguje už teď
|
||||
|
||||
Aby to nevypadalo hůř, než to je: runtime **běží**. Webhook vykoná strom,
|
||||
akce na ticketu vykoná strom, kroky si předávají výstupy, podmínka větví
|
||||
a celý průběh se zapíše do logu ticketu i s tím, co která služba vrátila.
|
||||
Na jednu firmu s desítkami ticketů denně je současný stav použitelný.
|
||||
Na 200 firem ne.
|
||||
@@ -2,6 +2,50 @@
|
||||
|
||||
Nejnovejsi nahore.
|
||||
|
||||
## 2026-08-13 - runtime, prokliky z widgetu a kapacitni rozbor
|
||||
|
||||
### Pridano
|
||||
|
||||
- `src/runtime/executor.ts`: **strom se konecne vykonava**. Jde krok po kroku,
|
||||
u podminky se vetvi, do poli dosadi `{{parametry}}`, akci pusti pres
|
||||
`runScript` a vystupy pripise do kontextu, aby na ne mohl dalsi krok
|
||||
odkazat. Cely prubeh jde do logu ticketu vcetne toho, co sluzba vratila.
|
||||
Pouzivaji ho **obe** cesty: akce na ticketu i webhook automatizace.
|
||||
- Z widgetu se da prokliknout na to, co je za cislem: z lidi na cloveka,
|
||||
ze seskupeni na uz vyfiltrovany seznam ticketu. Odkazy sklada **server**,
|
||||
protoze on jediny zna filtr widgetu.
|
||||
- Seznam ticketu cte filtr z adresy (`?typeId=`, `?tag=`, `?assignee=`, ...),
|
||||
takze proklik vede na spravny vyber a odkaz jde poslat kolegovi.
|
||||
- Seznam ticketu umi filtrovat na typ, tag a skupinu.
|
||||
- Spolecna `TicketTable`: jedna tabulka pro seznam i pro detail osoby.
|
||||
**Na mobilu se neposouva do strany**, uzka obrazovka dostane karty.
|
||||
- [19-kapacita-200-firem.md](19-kapacita-200-firem.md): zmereny rozbor toho,
|
||||
co se stane pri 200 firmach, a co to bude chtit za server.
|
||||
|
||||
### Opraveno
|
||||
|
||||
- **Rozlozeni dashboardu s vlastnim widgetem se nedalo ulozit.** `validateLayout`
|
||||
znala jen vestaveny katalog, takze kazdy pokus skoncil hlaskou, ze widget
|
||||
v katalogu neexistuje. Katalog je ted jedna funkce (`widgetCatalog`) a pouziva
|
||||
ji nabidka i kontrola.
|
||||
- Katalog widgetu je za konkretni firmu, ne za vsechny firmy uzivatele. Driv
|
||||
slo polozit dlazdici jedne firmy na dashboard druhe, kde k ni data nikdy
|
||||
neprisla.
|
||||
|
||||
### Odebrano
|
||||
|
||||
- **Simulace** vcetne tlacitka, dialogu i endpointu `/api/simulate`. Provoz se
|
||||
ted dela prijmem udalosti, ktery je skutecny.
|
||||
- Trojice pohledu nad tickety. Vyber firmy je select, "moje" je prepinac -
|
||||
driv to delalo totez dvakrat.
|
||||
|
||||
### Overeno
|
||||
|
||||
7 kontrol proti bezicimu serveru: ulozeni rozlozeni s vlastnim widgetem,
|
||||
oddeleni katalogu po firmach, vykonani stromu akce i webhooku, zapis behu do
|
||||
logu, odkazy z widgetu a filtrovani podle nich. K tomu mereni na 5 000
|
||||
ticketech, ze ktereho vychazi rozbor kapacity.
|
||||
|
||||
## 2026-08-13 - ticketovaci system: udalosti, externi ID, statistiky, widgety
|
||||
|
||||
Popis v [18-ticketovaci-system.md](18-ticketovaci-system.md).
|
||||
|
||||
Reference in New Issue
Block a user