# Rejstřík znovupoužitelných funkcí a komponent K čemu je co, aby se za rok nepsalo znovu něco, co už existuje. Když píšeš druhou funkci, která dělá skoro totéž jako něco odsud, je to skoro vždycky chyba - buď se má použít ta původní, nebo se má rozšířit. Podrobný popis je vždycky v komentáři u samotné funkce. Tady je jen jedna věta a kdy to použít. ## Ukládání dat (server) Rozhoduje se na **jednom místě**, viz [14-databaze.md](14-databaze.md). Volající nikdy nezjišťuje, jestli běží Postgres, soubor, nebo pamět. | Co | Kde | K čemu | | --- | --- | --- | | `defineStore(kind)` | `src/data/store/index.ts` | Založí úložiště pro nový druh záznamu. Jeden řádek na entitu. | | `initStores({databaseReady})` | `src/data/store/index.ts` | Vybere režim. Volá se jednou při startu, nikde jinde. | | `flushStores()` | `src/data/store/index.ts` | Dopíše rozepsané zápisy. Jen při ukončení procesu. | | `withCache(store)` | `src/data/store/cached.ts` | Kopie v paměti pro **konfigurační** entity, které se čtou při každém requestu (uživatelé, role, firmy). Čte se synchronně, obnovuje se po zápisu. | | `withMirror(store)` | `src/data/store/mirror.ts` | Opačný směr než `withCache`: data se mění v paměti a po každé změně se celý záznam zapíše. Pro **provozní** data (tickety, automatizace, incidenty, rozložení). | | `isVisible(entity, options)` | `src/data/store/types.ts` | Vidí volající tenhle záznam? Prázdný seznam firem znamená "nic", ne "vše". | | `nowIso()` | `src/data/store/types.ts` | Časová značka. Ať se nepíše `new Date().toISOString()` na třiceti místech. | | `memorySnapshot` / `fileSnapshot` | `src/data/snapshot.ts` | Nižší vrstva pod `createLocalStore`: atomický zápis JSONu s debounce. Přímo se nepoužívá. | | `db()`, `query`, `queryOne`, `transaction` | `src/db/pool.ts` | Postgres. `dbFor(tenantId)` je připravený šev pro rozdělení na víc databází. | | `seal`, `open`, `sealAll`, `openAll` | `src/db/secretBox.ts` | Šifrování přístupových údajů konektorů (AES-256-GCM). Nic tajného se neukládá jinak. | | `runMigrations()` | `src/db/migrate.ts` | Migrace pod zámkem, jeden soubor = jedna transakce. | ## Entity a práva (server) | Co | Kde | K čemu | | --- | --- | --- | | `crudRouter(options)` | `src/routes/crud.ts` | Celý CRUD nad jednou entitou: seznam, detail, vytvoření, úprava, mazání, právo, audit. Nová entita v nastavení = jeden `crudRouter`, ne pět handlerů. | | `readScope(req)` | `src/routes/crud.ts` | Ze které firmy smí request číst. Povinný argument všech `list` volání. | | `accessFor(user, tenantId?)` | `src/data/access.ts` | Co uživatel smí: práva, záložky, výchozí firma. Klient si nic nedovozuje sám. | | `permissionsOf(user, tenantId)` | `src/data/permissions.ts` | Efektivní práva z rolí. Pětisekundová cache, `invalidatePermissions()` po zápisu. | | `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. | | `findByIntakeToken(token)` | `src/data/tenants.ts` | Firma podle tokenu příjmu. Určuje i to, v jakém rozsahu je externí ID unikátní. | | `refreshCaches()` | `src/data/bootstrap.ts` | Obnoví všechny kopie v paměti. Volá se po zápisu, který je může změnit. | | `bootstrapData({databaseReady})` | `src/data/bootstrap.ts` | Seznam všech entit a provozních dat. **Nová entita se přidává tady**, ne rozesetě po modulech. | ## Skripty a konektory (server) Viz [11-skripty-konektoru.md](11-skripty-konektoru.md). | Co | Kde | K čemu | | --- | --- | --- | | `runScript(id, inputs, ctx)` | `src/scripts/runner.ts` | Spustí skript. **Nikdy nevyhodí výjimku**, chybu vrací jako výsledek s celým hlášením. | | `validateValues(...)` | `src/scripts/values.ts` | Jedna kontrola pro vstupy i výstupy skriptu podle manifestu. | | `scriptUtil` | `src/scripts/util.ts` | Nádobíčko pro skripty: `pick`, `first`, `num`, `date`, `need`, `get`, `applyRules`, `fillJson`. Skript nemá sahat na nic jiného. | | `createRedactor(...)` | `src/scripts/util.ts` | Vyškrtá tajemství z textu **před** logováním. Používá se u všeho, co jde do logu. | | `applyRules`, `fillJson` | `src/scripts/mapping.ts` | Transformace dat: pole na pole s převody, nebo objekt na objekt. Viz [13-transformace-dat.md](13-transformace-dat.md). | | `getPath(obj, path)` | `src/scripts/mapping.ts` | Čtení `zakaznik.adresa.mesto` z neznámého objektu. | | `resolveTarget(...)` | `src/scripts/connections.ts` | Z konektoru poskládá adresu a hlavičky. Přístupové údaje nikam jinam nevedou. | | `createHttp(...)` | `src/scripts/http.ts` | HTTP se timeoutem, limitem odpovědi a rozlišením "zkusit znovu" a "marné". | | `scriptIdFor(serviceId, operationId)` | `src/scripts/lookup.ts` | Který skript obsluhuje operaci z katalogu. | ## Klient | Co | Kde | K čemu | | --- | --- | --- | | `EntityAdmin` | `components/dashboard/EntityAdmin.tsx` | Celá správa jedné entity: tabulka, modál, validace, mazání. Nová záložka nastavení = popis sloupců a polí, ne nová stránka. | | `parseJsonField` | `components/dashboard/EntityAdmin.tsx` | Textové pole s JSONem na hodnotu, s hlášením, kde je chyba. | | `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. | | `MappingEditor` | `components/dashboard/flow/MappingEditor.tsx` | Editor transformací v obou režimech (pole na pole, JSON). | | `DataState` | `components/dashboard/DataState.tsx` | Načítání, chyba, prázdno. Ať to každá stránka nekreslí po svém. | | `apiFetch` | `lib/api.ts` | Jediná cesta na API: base path, token, `ApiError` s celým hlášením ze serveru. | | `useApiQuery` | `lib/useApiQuery.ts` | Načtení dat do stránky včetně `reload`. S `body` pošle POST (dávkové načtení), s `enabled: false` se neptá vůbec. | | `cn(...)` | `lib/cn.ts` | Skládání tříd. Podmíněné třídy nikdy ručně přes šablonu. | | `format*` | `lib/format.ts` | Čísla, procenta, datum, relativní čas, trvání. Formátování se nepíše v komponentě. | | `serviceIcon(key)` | `lib/serviceIcons.ts` | Klíč ikony ze serveru na komponentu. Server neposílá komponenty. | | `usePageMeta` | `lib/usePageMeta.ts` | Titulek stránky. | | `Badge`, `Button`, `Modal`, `Card`, ... | `components/ui/` | Základní prvky. Nový vzhled tlačítka patří sem, ne do stránky. | ## Pravidla, která z toho plynou 1. **Nová entita v nastavení**: `defineStore` v modulu entity, řádek v `bootstrap.ts`, `crudRouter` v `settings.ts`, popis v `Settings.tsx`. Nic jiného se psát nemusí. 2. **Data, která se mění za provozu**, jdou přes `withMirror`. Data, která se čtou při každém requestu a mění zřídka, přes `withCache`. Obojí nikdy. 3. **Chybu se nesmí zkracovat.** Server vrací celé hlášení, klient ho umí zobrazit (`ErrorDetail`). 4. **Klient nepočítá práva.** Co smí, říká `accessFor`.