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>
36 KiB
36 KiB
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. Volající nikdy nezjišťuje, jestli běží Postgres, soubor, nebo pamět.
| Co | Kde | K čemu |
|---|---|---|
defineStore<T>(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 pro všechna úložiště včetně konektorů. Volá se jednou při startu, nikde jinde. |
refreshCache(kind), refreshAllCaches() |
src/data/store/cached.ts |
Registr cache. Po zápisu obnovit jen tu entitu, ne všechny. |
listByTenant(tenantIds, sortBy) |
src/data/store/cached.ts |
Filtr na firmu plus řazení nad cache. Místo sedmi kopií filter + sort v modulech entit. |
nowIso, minutesAgo, highestNumber, writableOrWarn |
src/data/store/types.ts |
Drobnosti pro moduly úložišť: časová značka, čas před N minutami, nejvyšší číslo ID pro čítač, varování při zápisu do úložiště jen pro čtení. |
mergeValues(current, patch) |
src/data/connectors/types.ts |
Sloučení hodnot konektoru při PATCH: prázdný řetězec maže, chybějící klíč nechává. Jedna implementace pro soubor i Postgres. |
flushStores() |
src/data/store/index.ts |
Dopíše rozepsané zápisy. Jen při ukončení procesu. |
storeByKind(kind) |
src/data/store/index.ts |
Uloziste podle druhu z registru. Jen pro export nastaveni; modul entity si bere svoje uloziste primo. |
seedFromFile(kind, seed?, file?) |
src/data/store/seedFile.ts |
Seed pro prazdne uloziste: ze seed/records.json, kdyz druh obsahuje, jinak z kodu. Vola defineStore v init, jinde se nevola. |
loadSeedFile(file), SEED_KINDS, isSeedKind |
src/data/store/seedFile.ts |
Nacteni a kontrola souboru (jednou za cestu, zod na obalku a id), seznam konfiguracnich druhu. SEED_KINDS je jediny seznam pro import i export. |
exportSeed() |
src/data/seedExport.ts |
Vsechny zaznamy konfiguracnich druhu z ulozist ve tvaru seed/records.json. Pouziva GET /api/admin/export. |
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. |
enqueue(input) |
src/runtime/queue.ts |
Zařadí běh. Klíč proti dvojímu zařazení drží jeden běh na jednu událost. |
claimBatch(limit, active) |
src/runtime/queue.ts |
Vezme další práci, spravedlivě po firmách, přeskočí to, co už běží. Místo, kde nad Postgresem musí být SKIP LOCKED. |
touchClaim(item) |
src/runtime/queue.ts |
Tlukot běžícího běhu. Bez něj se dlouhý běh po 30 minutách považuje za zaseknutý a vykoná se podruhé. |
findOpenIncident(source, tenantIds) |
src/data/incidentStore.ts |
Otevřený incident téže příčiny v téže firmě. Stejná chyba nezakládá druhý. |
attachAccess, tenantOrDeny, optionalTenantOrDeny, scopeOrDeny |
src/middleware/tenant.ts |
Přístup jednou na request do req.access, firma requestu z něj. Route si to nepočítá sama. |
safeRouter(), wrap(handler) |
src/middleware/asyncHandler.ts |
Router, ve kterém odmítnutá promise skončí jako 500 s logem. Každá nová route vzniká tady. |
rateLimit({name, windowMs, max}) |
src/middleware/rateLimit.ts |
Limit requestů v paměti, 429 s Retry-After. Jen na veřejných endpointech, kde se dá hádat. |
validationError(res, zodError, message?) |
src/middleware/validation.ts |
Jeden tvar chyby validace: z chyby zodu udělá issues po polích. |
timingSafeEqualString(a, b) |
src/lib/secure.ts |
Porovnání tokenu v konstantním čase. Webhook, příjem, pozvánky. |
assertAllowedUrl, readBodyLimited, readJsonLimited, describeFetchError |
src/net/guard.ts |
Vše, co volá ven: zákaz vnitřní sítě, čtení těla proudem s limitem, čitelný popis chyby sítě. HTTP skriptů, MCP, SMTP i ARES. |
publishOutputs(...) |
src/runtime/executor.ts |
Zápis výstupů kroku do kontextu, jednou pro vestavěné kroky i skripty. Holé jméno nepřepíše, co už v kontextu je. |
lookupCompany, searchCompanies, listCompanyPersons, placeholderEmail, isPlaceholderEmail |
src/ares/client.ts |
Registr ARES: firma podle IČ nebo názvu, statutáři, zástupná adresa a její rozpoznání. |
onTicketEvent(kind, ticket) |
src/runtime/triggers.ts |
Změna ticketu zařadí navázané automatizace, včetně ochrany proti smyčce. |
withRun(marker, work) |
src/runtime/context.ts |
Označí, který běh práci způsobil. Bez toho automatizace spouští sama sebe. |
findBuiltinStep(...) |
src/runtime/builtinSteps.ts |
Kroky, které sahají do našeho úložiště, ne ven přes HTTP. |
personView(user, membership) |
src/data/people.ts |
Jediné místo, kde z účtu a jednoho členství vzniká pohled Person. Řešitel je členství, ID řešitele je ID účtu. |
listPeople(tenantIds), listAllPeople(tenantIds) |
src/data/people.ts |
Řešitelé vybraných firem, jeden záznam na členství; druhá i s vypnutými účty (správa týmu). |
findPerson(id, tenantId) |
src/data/people.ts |
Řešitel v dané firmě, firma je povinná. Kdo v ní není členem, je undefined, i když účet existuje. |
personName(id) |
src/data/people.ts |
Jméno účtu bez ohledu na firmu, pro popisky u záznamů, které už prošly filtrem na firmu. |
personIdFor(user, tenantId) |
src/data/people.ts |
ID řešitele, kterým je uživatel ve firmě: ID účtu při členství, jinak null. Neptat se user.id přímo. |
findPersonByExternalId(value, tenantIds) |
src/data/people.ts |
Řešitel podle ID z cizí aplikace, například voicebotId. Externí ID visí na členství. |
migratePeople() |
src/data/migratePeople.ts |
Jednorázový převod starých záznamů řešitelů (ppl_) na účty při startu. Přepisuje odkazy přes remapPersonIds v tickets/remap.ts a automations/remap.ts. |
notify(input) |
src/data/notifications.ts |
Upozorní člověka. Nečeká se a nevyhazuje chyby, stejně jako audit. |
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/tickets/intake.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. Hodnoty z input.apply zapíše v obou případech, prázdné nemaže. |
getAgentStats(...) |
src/data/tickets/stats.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/tickets/queries.ts |
Ticket firmy podle externího ID. Klíč je dvojice firma a ID. |
createApp() |
src/app.ts |
Sestavi Express aplikaci: middleware, routery, health, Swagger, statika, SPA. Bez listen a bez nacteni dat, takze jde postavit v testu (supertest). index.ts ji jen spusti. |
config.serviceBaseUrlOverride(variable) |
src/config.ts |
Jedine cteni process.env s dynamickym nazvem (<SLUZBA>_BASE_URL). Vraci normalizovanou adresu nebo null. Nikde jinde se process.env necte. |
buildOpenApiDocument() |
src/openapi/index.ts |
Sklada OpenAPI z components.ts a paths/*.ts. Novy endpoint se popisuje v souboru sveho routeru, crudPaths v helpers.ts popise CRUD petici jednim radkem. |
pageFrom(query), paginate(items, page) |
src/routes/dashboard/shared.ts |
Strankovani seznamu ticketu a fronty behu: limit, offset, strop MAX_PAGE_LIMIT. Nepsat vlastni slice v route. |
memberOf(user, tenantId) |
src/routes/settings/shared.ts |
Je ucet clenem firmy? Sdili sprava uctu a resitelu. |
Moduly dat po rozdeleni
Puvodni soubory zustavaji jako fasady (ticketStore.ts, automationStore.ts,
services.ts), importy se nemeni. Kdo hleda, kde co je:
| Modul | Co drzi |
|---|---|
tickets/index.ts |
verejne API slozky, seed a initTickets v poradi, ve kterem se maji volat |
tickets/model.ts |
StoredTicket, defaultStatuses, channelLabels, toTicket (doplneni vychozich hodnot) |
tickets/state.ts |
pole ticketu, indexy podle ID a externiho ID, log, udalosti, citace ID |
tickets/persist.ts |
persist, touch (updatedAt a zapis v jednom), initTickets |
tickets/queries.ts |
listTickets s TicketFilter, getTicket, findTicket, findByExternalId, ticketWithinVisibility |
tickets/store.ts |
zapisy: createTicket, updateTicketStatus, assignTicket, setTicketType, setTicketTags, assignTicketGroup, claimTicket, addComment, noteAttachment (radek v logu a ticket.updated po zmene prilohy) |
tickets/intake.ts |
intakeEvent: udalost zvenku se stane ticketem nebo se navesi |
tickets/trace.ts |
appendTrace, flattenTrace, lastTraceId, describePayload: log prubehu |
tickets/stats.ts |
getWorkload, getAgentStats |
tickets/seed.ts, remap.ts |
ukazkova data (SEED_DEMO=1), preznaceni resitelu pri migraci |
automations/index.ts |
verejne API slozky, seed a initAutomations |
automations/model.ts |
StoredAutomation, rulesOf, matchOf (cteni podminky ve stare i nove podobe) |
automations/state.ts |
Map automatizaci, nextId, findWritable |
automations/persist.ts |
save, initAutomations, mirror |
automations/store.ts |
listAutomations, getAutomation, createAutomation, updateAutomation, regenerateWebhookToken, findByWebhookToken, recordRun, deleteAutomation |
automations/validation.ts |
countSteps, collectFlowIssues, deriveKind, withDerived: ciste funkce nad stromem |
automations/webhook.ts |
generateWebhookToken, withWebhookToken, recordWebhookCall, recentWebhookCalls, bodyForCall |
automations/runs.ts |
historie behu po dnech, KEEP_DAYS, statsOf |
automations/seed.ts, seedDemo.ts, remap.ts |
skutecne automatizace (vzdy), ukazkove (SEED_DEMO=1), preznaceni resitelu |
services/index.ts |
katalog za behu: findService, findOperation, actionsFor, serviceCatalog, setScriptActions, setMcpOperations, withRuntimeOptions, visibleServices |
services/catalog/index.ts |
services a serviceCategories slozene ze skupin; poradi tady je poradi v nabidce |
services/catalog/<skupina>.ts |
staticky zapis sluzeb jedne skupiny: triggers, incident, ticket, crm, finance, logistics, email, messaging, social, office, analytics, ai, mcp, tools, polstryn |
findByIntakeToken(token) |
src/data/tenants.ts |
operatorTenant() |
src/data/tenants.ts |
clearOtherOperators(keepId) |
src/data/tenants.ts |
checkUploads(uploads, existingCount) |
src/data/attachments.ts |
addAttachments(ticket, uploads, uploadedBy) |
src/data/attachments.ts |
listAttachments, getAttachment, removeAttachment |
src/data/attachments.ts |
sanitizeName, normalizeMime, decodeBase64, formatBytes |
src/data/attachments.ts |
jsonLimitFor(count, maxBytes), hasOwnBodyLimit(path) |
src/routes/bodyLimit.ts |
visibleTicketOrDeny(req, res) |
src/routes/ticketActions.ts |
brandOfOperator() |
src/routes/public.ts |
refreshCaches(), refreshEntity(kind) |
src/data/bootstrap.ts |
bootstrapData({databaseReady}) |
src/data/bootstrap.ts |
Skripty a konektory (server)
| Co | Kde | K čemu |
|---|---|---|
runScript(id, inputs, ctx) |
src/runtime/scripts/runner.ts |
Spustí skript. Nikdy nevyhodí výjimku, chybu vrací jako výsledek s celým hlášením. |
validateValues(...) |
src/runtime/scripts/values.ts |
Jedna kontrola pro vstupy i výstupy skriptu podle manifestu. |
scriptUtil |
src/runtime/scripts/util.ts |
Nádobíčko pro skripty: pick, first, num, date, day, list, addresses, quote, need, get, applyRules, fillJson. Skript nemá sahat na nic jiného. |
pick, pickText, jwtExpiry, parseBool, parseNumber |
src/runtime/scripts/util.ts |
Totéž pro server: pole bez ohledu na velikost písmen, exp z JWT, převody. Než napíšeš Number(x) s kontrolou NaN, je to tady. |
DETAIL_BYTES, truncate(value) |
src/runtime/scripts/util.ts |
Jeden limit na zkracování detailu chyby pro všechny vrstvy. Žádné vlastní slice(0, 600). |
createRedactor(...) |
src/runtime/scripts/util.ts |
Vyškrtá tajemství z textu před logováním, i v URL-encoded a JSON-escaped tvaru. Používá se u všeho, co jde do logu. |
scriptConfig(target) |
src/runtime/scripts/connections.ts |
Nastavení napojení bez tajných polí. Jediné, co skript dostane jako ctx.config. |
applyRules, fillJson |
src/runtime/scripts/mapping.ts |
Transformace dat: pole na pole s převody, nebo objekt na objekt. Viz 13-transformace-dat.md. |
getPath(obj, path) |
src/runtime/scripts/mapping.ts |
Čtení zakaznik.adresa.mesto z neznámého objektu. |
resolveTarget(...) |
src/runtime/scripts/connections.ts |
Z konektoru poskládá adresu a hlavičky. Přístupové údaje nikam jinam nevedou. |
serviceBaseUrl(service) |
src/runtime/scripts/connections.ts |
Adresa služby: naše aplikace ze SERVICES_BASE_URL, cizí (OpenAI) z jejího baseUrl. Přebít jde přes <SLUZBA>_BASE_URL. |
targetSecrets(target) |
src/runtime/scripts/connections.ts |
Co se musí vyškrtat z logu. Vrací i holý klíč bez předpony Bearer , protože v něm ho cizí služby vracejí v chybách. |
createHttp(...) |
src/runtime/scripts/http.ts |
HTTP se timeoutem, limitem odpovědi a rozlišením "zkusit znovu" a "marné". |
isPrivateHost(host) |
src/runtime/scripts/http.ts |
Míří jméno do vnitřní sítě? Jedno pravidlo pro HTTP i pro SMTP server z konektoru. |
sendMail(target, message) |
src/mail/smtp.ts |
Odešle e-mail přes SMTP z konektoru. Nikdy nevyhodí výjimku, vrací i to, jestli má smysl zkusit znovu. |
verifySmtp(target) |
src/mail/smtp.ts |
Přihlásí se na server bez odeslání zprávy. Tím se ověřuje konektor e-mailu. |
escapeHtml(value) |
src/data/templates.ts |
Escapuje dosazenou hodnotu v HTML šabloně. Značky autora šablony zůstávají, ostré závorky od zákazníka ne. |
ctx.http.postForm(...) |
src/runtime/scripts/http.ts |
Odeslání souboru (multipart/form-data). Obsah přichází jako Base64, hranici dopisuje runtime. |
scriptIdFor(serviceId, operationId) |
src/runtime/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. |
TicketAttachments |
components/dashboard/TicketAttachments.tsx |
Sekce priloh v detailu ticketu: seznam, stazeni pres fetch s tokenem, nahrani, smazani. Vlastni dotaz, obnovi se z ticket.updated. |
TicketAssignPanel |
components/dashboard/TicketAssignPanel.tsx |
Panel resitele a skupiny v detailu ticketu, ciselniky si bere sam. Vyclenen z TicketDetail. |
TenantsAdmin |
components/dashboard/settings/TenantsAdmin.tsx |
Sprava firem v Nastaveni vcetne priznaku provozovatele a kontaktnich udaju. Vyclenena ze Settings.tsx. |
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<T> |
lib/api.ts |
Jediná cesta na API: base path, token, ApiError s celým hlášením ze serveru. |
useApiQuery<T> |
hooks/useApiQuery.ts |
Načtení dat do stránky včetně reload, refreshing a total. S body pošle POST, s enabled: false se neptá, patchOn opraví data z události bez dotazu. |
useCollection(key), useAccess(), useCollectionSelector |
lib/collections.tsx |
Číselníky za firmu (lidé, skupiny, typy, služby, konektory, přístup) ze sdíleného skladu, opravované z událostí. Ne apiFetch na číselník ze stránky. |
patchTicketList(...) |
lib/ticketEvents.ts |
Oprava seznamu ticketů z payload.ticket v události. Použít jako patchOn. |
apiFetchWithMeta<T> |
lib/api.ts |
Jako apiFetch, ale vrací i X-Total-Count. Pro stránkované seznamy. |
useSubmit(fn) |
hooks/useSubmit.ts |
Odeslání formuláře: saving, chyba, reset. Dvanáct řádků, které si dřív psal každý formulář zvlášť. |
useUnsavedChanges(dirty) |
hooks/useUnsavedChanges.ts |
Varování při odchodu z rozepsaného formuláře nebo stromu. |
priorities, priorityLabel |
lib/options.ts |
Pevné číselníky. Stavy a kanály se berou ze serveru (/widget-data/options). |
plural(count, forms) |
lib/format.ts |
Skloňování počtu (1 ticket, 2 tickety, 5 ticketů). |
Field, Input, Select, Textarea |
components/ui/form/ |
Formulářové prvky s jednou sadou tříd (controlClass). Vlastní inputClass ve stránce je chyba. |
FilePicker |
components/ui/form/FilePicker.tsx |
Vyber priloh: tlacitko, skryty <input type="file">, seznam vybranych, kontrola poctu a velikosti pri vyberu. Soubory drzi rodic. |
readFileAsBase64, validateFiles, formatBytes, MAX_ATTACHMENT_BYTES |
lib/files.ts |
Soubor na base64 bez prefixu data:, kontrola davky proti limitum, velikost pro cloveka. Limity stejne jako na serveru. |
apiBlob(path) |
lib/api.ts |
Stazeni binarniho obsahu s hlavickou Authorization. Obycejny odkaz token neposle. |
useBrand() |
hooks/useBrand.ts |
Udaje provozovatele z /api/public/brand nad statickou zalohou config/brand.ts. Jeden dotaz na nacteni stranky, sdileny. |
Chip |
components/ui/Chip.tsx |
Štítek. |
Table, TableHead, Th, TableRow, Td |
components/ui/Table.tsx |
Tabulka seznamu v portalu (hlavicka verzalkami, radky s linkou). Ctyri stranky ji kreslily kazda jinak. Huste tabulky ticketu a vykonu zustavaji zvlast, jsou to jine tabulky. |
ServiceIcon |
components/ui/ServiceIcon.tsx |
Ikona sluzby podle klice z katalogu. Misto const Icon = serviceIcon(key) v JSX, ktere lint hlasi jako komponentu vytvorenou pri vykresleni. |
EntityForm |
components/dashboard/EntityForm.tsx |
Formular jedne entity v modalu, pouziva ho EntityAdmin. Pole z popisu sloupcu, hodnoty a chyby v propsech. |
useLatest(value) |
hooks/useLatest.ts |
Ref s posledni hodnotou pro callbacky, ktere nemaji byt v zavislostech effectu. Zapis v layout effectu, aby vykresleni zustalo ciste. |
useSyncFromSource(source, apply) |
hooks/useSyncFromSource.ts |
Prevzeti dat ze zdroje do rozepsaneho stavu uz pri vykresleni, ne v useEffect. Stara kopie neproblikne a stranka se nekresli dvakrat. |
TicketCard |
components/dashboard/TicketCard.tsx |
Karta ticketu pro dlaždice a mobil, varianta compact pro widgety. |
useMediaQuery(query), MD_UP |
hooks/useMediaQuery.ts |
Tabulka nebo karty podle šířky pres useSyncExternalStore, prohlizec je zdroj pravdy. TicketTable podle toho kreslí obojí, druhá komponenta není. |
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 |
hooks/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
- Nová entita v nastavení:
defineStorev modulu entity, řádek vbootstrap.ts,crudRoutervsrc/routes/settings/<entita>.tsa mount vsettings/index.ts, popis vSettings.tsx. Nic jiného se psát nemusí. - 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řeswithCache. Obojí nikdy. - Chybu se nesmí zkracovat. Server vrací celé hlášení, klient ho umí
zobrazit (
ErrorDetail). - Klient nepočítá práva. Co smí, říká
accessFor.