Poptavka z webu je ticket, prilohy, udaje provozovatele, kolacovy graf
Provozovatel portalu: firma s priznakem portalOperator (jen jedna, zapnuti odebere ostatnim) a novymi poli contactEmail, contactPhone, website vedle ico, dic, adresy a pravni formy. Verejny GET /api/public/brand vraci jeji udaje a web je bere pres useBrand() na kontaktu, v paticce, O nas, prihlaseni i v titulku; brand.ts je jen zaloha. Poptavka z webu zaklada u provozovatele ticket kanalu form: predmet "Poptavka: tema", telo JSON s poli formulare, tag Poptavka plus tema, zakaznik z formulare, poznamka v logu. Bez provozovatele se jen zaloguje. Prilohy ticketu: formular az 3 soubory po 5 MB, ticket az 10; nahrani, seznam, stazeni a smazani (pravo ticket.comment, strop viditelnosti, poznamky v logu, audit). Soubor jde v JSON jako Base64 a lezi v beznem ulozisti, bez nove zavislosti; strop tela jen na techto cestach. Vlastni widget s kreslenim Graf umi i pocet ticketu se seskupenim jako kolac (PieChart.tsx, ciste SVG, osm barev z tokenu, zbytek jako ostatni). OpenAPI rozdelene na mensi soubory (102 cest, 28 schemat overeno shodnych), 28 novych testu (135 celkem), dokumentace aktualizovana.
This commit is contained in:
@@ -2,6 +2,106 @@
|
||||
|
||||
Nejnovejsi nahore.
|
||||
|
||||
## 2026-09-09 - Kolacovy graf ve vlastnim widgetu
|
||||
|
||||
Vlastni widget s kreslenim "Graf" bral jen casovou radu; pokus o graf
|
||||
ticketu podle stavu skoncil hlaskou "Graf umi jen zdroj Casova rada". Server
|
||||
pritom seskupeny pocet uz umel (`ticketCount` s `groupBy`), jen ho klient
|
||||
kreslil vzdy jako sloupce.
|
||||
|
||||
`validateWidget` pousti graf i nad `ticketCount` se seskupenim (bez
|
||||
seskupeni dal ne, kolac z jedne vysece nic nerika) a klient seskupena data
|
||||
u kreslení "Graf" vykresli jako kolac (`widgets/PieChart.tsx`, cisté SVG bez
|
||||
knihovny, osm barev z tokenu, zbytek nad osm skupin jako "ostatni", legenda
|
||||
s cislem a podilem, odkaz na filtrovany seznam). Napoveda ve Widgetech ma
|
||||
priklad a uz nepouziva anglicke stavy `new/open/waiting`, ktere v datech
|
||||
nejsou (filtr `closed: false`). Test `tests/data/customWidgets.test.ts`.
|
||||
|
||||
## 2026-09-09 - Poptavka z webu je ticket, prilohy, udaje provozovatele
|
||||
|
||||
Kontaktni formular na webu poptavku jen zalogoval: kdo nesledoval log
|
||||
containeru, o ni nevedel, a odesilatel dostal "ozveme se", ktere nikdo
|
||||
nemohl splnit. Zaroven mel web kontakty a IC natvrdo v `brand.ts`, takze
|
||||
zmena telefonu znamenala novy build. Obe veci maji spolecny koren: portal
|
||||
nevedel, **ktera firma ho provozuje**. Tahle zmena to zavadi a stavi na tom.
|
||||
|
||||
### Provozovatel portalu
|
||||
|
||||
Firma dostala priznak `portalOperator` a kontaktni pole `contactEmail`,
|
||||
`contactPhone`, `website` (vedle `ico`, `dic`, `address`, `legalForm`).
|
||||
Nastavuje ho spravce platformy v Nastaveni, Firmy
|
||||
(`components/dashboard/settings/TenantsAdmin.tsx`, vyclenene ze `Settings.tsx`).
|
||||
|
||||
| Rozhodnuti | Proc |
|
||||
| ----------------------------------- | -------------------------------------------------------------------------------------------- |
|
||||
| priznak na firme, ne promenna | provozovatel je zaznam s IC a adresou, ktery uz v datech je; promenna by ho jen duplikovala |
|
||||
| prave jeden | dve firmy s poptavkami by znamenaly, ze ticket vznikne jen jedne a nikdo nevi ktere |
|
||||
| nastaveni u jine firmy sunda puvodni | `afterWrite` CRUD firem vola `clearOtherOperators`; zmenene firmy jdou do streamu jako `tenant.updated` |
|
||||
| jen spravce platformy | kdo portal provozuje, je nase rozhodnuti, stejne jako zalozeni firmy |
|
||||
| vypnuta firma provozovatelem neni | `operatorTenant` bere jen zapnutou, i kdyz priznak zustal |
|
||||
|
||||
### Poptavka z webu je ticket
|
||||
|
||||
`POST /api/contact` zaklada ticket provozovateli: kanal `form`, predmet
|
||||
`Poptávka: <tema>`, tagy `Poptávka` a tema, zakaznik z formulare,
|
||||
`externalSource` `web-form`, poznamka v logu ticketu. **Telo ticketu je JSON
|
||||
formulare** (`name`, `email`, `company`, `phone`, `topic`, `message`,
|
||||
`receivedAt`), ne slozena veta: `TicketBody` JSON ukaze jako tabulku
|
||||
a zadne pole se neztrati v prose; rozhodnuti zadavatele. Bez provozovatele
|
||||
se poptavka jen zaloguje (warn) a odpoved je porad 202, zvenku nema byt
|
||||
poznat, jak je portal nastaveny. Limit 5 za hodinu z adresy se nemeni
|
||||
a bezi **pred** parserem tela, aby vycerpane pokusy nenutily server cist
|
||||
megabajty. Formular bere az 3 prilohy po 5 MB (`FilePicker` v `Contact.tsx`).
|
||||
|
||||
Upozorneni e-mailem na novou poptavku zatim neni, resitel ji vidi
|
||||
v seznamu ticketu.
|
||||
|
||||
### Prilohy ticketu
|
||||
|
||||
Nova kolekce `attachment` (`src/data/attachments.ts`, typ
|
||||
v `src/shared/attachments.ts`), **ne pole ticketu**: obsah je velky, meni
|
||||
se nezavisle na ticketu a seznam se vraci bez nej. Obsah se uklada jako
|
||||
base64 uvnitr zaznamu obecneho uloziste. Zadna nova zavislost (multer,
|
||||
S3 klient): prototyp uklada jednotky MB a `defineStore` to zvladne; cenou
|
||||
je, ze v rezimu `file` kazda zmena prepise cely `attachment.json`. Blob
|
||||
uloziste (S3 nebo `bytea`) je dalsi krok, rozhrani modulu se tim nezmeni.
|
||||
|
||||
| Co | Hodnota |
|
||||
| ------------------------- | ----------------------------------------------------------------------- |
|
||||
| limity | 5 MB na soubor po dekodovani, 10 na ticket, nazev 200 znaku |
|
||||
| kontrola | `checkUploads` nad celou davkou pred prvnim zapisem: bud vse, nebo nic |
|
||||
| nazev | `sanitizeName`: bez cesty a ridicich znaku, jinak rozbije `Content-Disposition` |
|
||||
| endpointy | `GET/POST /api/dashboard/tickets/:id/attachments`, `GET .../:attachmentId/content`, `DELETE .../:attachmentId` |
|
||||
| pravo | `ticket.comment` za firmu ticketu, ticket pres `visibleTicketOrDeny` jako u detailu |
|
||||
| stopa | radek v logu ticketu (`noteAttachment`), `ticket.updated`, audit `ticket.attachment.add` / `.remove` |
|
||||
| stazeni | binarni odpoved chce `Authorization`, portal stahuje pres `apiBlob` a docasny odkaz |
|
||||
|
||||
Globalni `express.json` ma 256 kB, coz na soubory nestaci. Kontakt a prilohy
|
||||
maji vlastni parser se stropem z `jsonLimitFor` (`src/routes/bodyLimit.ts`)
|
||||
a globalni je preskakuje (`hasOwnBodyLimit` v `app.ts`), jinak by telo
|
||||
odmitl driv, nez se k nemu router dostane.
|
||||
|
||||
Portal: sekce priloh v detailu ticketu (`TicketAttachments.tsx`), panel
|
||||
resitele vyclenen do `TicketAssignPanel.tsx`; `TicketDetail` tim sel pod
|
||||
500 radku. `FilePicker` v `ui/form`, prace se soubory v `lib/files.ts`.
|
||||
|
||||
### Verejny brand
|
||||
|
||||
`GET /api/public/brand` bez prihlaseni, `Cache-Control` na minutu, vraci
|
||||
`name`, `legalName`, `ico`, `dic`, `address`, `legalForm`, `email`, `phone`,
|
||||
`website` provozovatele, bez nej same `null`. Web to sklada v `hooks/useBrand.ts`
|
||||
nad statickou zalohou `config/brand.ts`: `null` ze serveru nechava zalohu,
|
||||
`claim`, `description`, `social`, `support` a `founded` zustavaji staticke.
|
||||
Pouziva to `Contact`, `Footer`, `CallToAction`, `About`, `Login`, `Logo`
|
||||
a `usePageMeta`. Jeden dotaz na nacteni stranky, sdileny pres
|
||||
`useSyncExternalStore`.
|
||||
|
||||
### Testy
|
||||
|
||||
12 souboru, 130 testu (22 novych): uloziste priloh (`tests/data/attachments.test.ts`),
|
||||
kontakt (`tests/routes/contact.test.ts`), routy priloh
|
||||
(`tests/routes/attachments.test.ts`), verejny brand (`tests/routes/public.test.ts`).
|
||||
|
||||
## 2026-09-09 - Struktura podle zasad: rozdeleni souboru, lint, testy
|
||||
|
||||
`D:\GitHubRepository\CLAUDE.md` dostal zasady pro vsechny projekty (struktura
|
||||
|
||||
Reference in New Issue
Block a user