# 99 - Zaznam zmen
Nejnovejsi nahore.
## 2026-08-26 - na aplikaci je videt, ktery build to je
Nasazena aplikace o dva commity pozadu funguje a vypada skoro stejne jako
nova. Bez oznaceni buildu se to nepozna a hleda se chyba tam, kde zadna neni.
### Pridano
- **Oznaceni buildu v pate postranniho menu**, pod tlacitkem Odhlasit se:
verze, cas buildu a cislo commitu. Text jde oznacit jednim kliknutim, aby
se dal poslat dal.
- **Totez jako `` v hlavicce stranky.** V JS balicku uz
to je, ale ten se musi stahnout a rozbalit. Meta znacka je videt na jeden
`curl` bez prihlaseni - a prave to clovek potrebuje, kdyz zjistuje, co bezi
na produkci.
- Hodnoty dosazuje Vite pri buildu (`define` a plugin `build-stamp`
ve `vite.config.ts`), takze neplati pro repozitar, ale pro ten konkretni
balicek.
### Zmeneno
- **`.dockerignore` uz nevylucuje `.git/`.** Build z nej cte cislo commitu;
bez nej by se poznal jen cas buildu, ne co v nem je. Do vysledneho image se
`.git` nedostane, ten se sklada z ciste zakladni image.
## 2026-08-26 - rebrand na WorkNuke
Prevzeti vizualniho smeru z predlohy. Popis, jak znacka funguje v kodu, je
v [22-znacka-a-design.md](22-znacka-a-design.md).
### Zmeneno
- **Barevnost.** Akcent z cyanove na zelenou `#B7FF3C`, podklad z modrocerne
na zelenocernou `#0F1411`. Web i portal stoji na tychz tokenech, takze se to
projevilo naraz na obojim.
- **Druhy akcent uz neni barva.** `accent-*` ukazuje do teze zelene rady jako
`brand-*`. Klice zustaly, protoze je pouziva 341 mist; prepsat je vsechny
naraz by byla zbytecne velka zmena. Totez u utility `text-gradient`, ktera
uz negeneruje prechod, ale plnou zelenou.
- **Stav "v poradku" je modrozeleny.** Akcent je zeleny, a dve zelene na jedne
obrazovce, kazda o necem jinem, nikdo nerozlisi.
- **Pismo.** Archivo na nadpisy, IBM Plex Sans na text, IBM Plex Mono na cisla
a ID. Nadpisy maji tesny proklad, ktery Inter neumel bez trikareni.
- **Znak.** Plocha bomba misto pismene v gradientnim ctverci. `LogoMark` je
samostatny export, aby sel pouzit i uvnitr snimku v heru.
- **Tlacitko.** Plna zelena misto prechodu ze dvou barev. Prave tim vystupuje
z rady.
- **Hero.** Prokladany nadtitulek misto odznaku, dvouslovne radky nadpisu,
jedno primarni tlacitko plus textovy odkaz, ctyri slovesa misto tri odrazek
a snimek portalu vybihajici z prave hrany.
- **Navigace** je pri levem okraji a bez pilulky za aktivni polozkou. Jedina
pilulka na liste je primarni akce.
- **Nazev, claim a kontakt** v `brand.ts`.
### Opraveno u toho
- **Zare v heru je pozadi sekce, ne vrstva pod ni.** Prekryv se zapornym
z-indexem se schoval za podklad stranky a nebyl videt vubec.
### Co se zamerne nezmenilo
Klice v prohlizeci (`automia.token`, `automia.tenant`), ID firmy `tnt_automia`,
ID aplikace `csbot-prototype` a prihlasovaci e-maily v ukazkovych datech.
Vypadaji jako jmeno, ale jsou to identifikatory. Duvody jsou
v [22-znacka-a-design.md](22-znacka-a-design.md).
## 2026-08-26 - prepinac firmy: do sidebaru a prekresli obsah cely
Dodelavka predchoziho bodu. Tvrzeni "prepinac plati pro cely system" nebylo
pravdive: `useApiQuery` se na zmenu firmy zeptat umel, ale komponenty, ktere
volaji `apiFetch` primo - `EntityAdmin`, a tim cele Nastaveni a zalozky
Resitele a Skupiny v Lidech - o zmene nevedely a zustala na nich data
predchozi firmy.
### Opraveno
- **Obsah dashboardu ma `key` podle vybrane firmy**, takze prepnuti prestavi
cely strom komponent. Zadny dotaz nemuze zustat stary, protoze zadna stara
komponenta nezustane. Doplnovat zavislost do kazde komponenty zvlast je
totez zapomenuti, jen o patro niz.
### Zmeneno
- **Prepinac je v sidebaru nad navigaci**, ne v hlavicce. Vypada jako ovladaci
prvek: ram, ikona firmy, sipka. Predchozi verze mela pruhledny ram a splyvala
s popiskem - prepinac, ktery neni videt, je k nicemu.
- **V hlavicce zustal jen nazev aktivni firmy.** Driv tam stalo natvrdo
`tenants[0]`, takze po prepnuti ukazovala porad prvni firmu ze seznamu.
- Kdo patri do jedne firmy, vidi misto prepinace jeji nazev. Vyber z jedne
moznosti neni vyber.
## 2026-08-26 - prava plati za firmu, ne za cloveka
`permissionsOf(user)` scitalo role pres vsechna clenstvi. Kdo byl spravce
v jedne firme, jednal jako spravce ve vsech, kam patril. Uzivatel ve dvou
firmach je vzacny, dusledek ne - to neni edge case, to je diera v pravech.
### Zmeneno
- **Firma je povinny argument** u `permissionsOf`, `hasPermission`
i `hasAnyPermission`. Zamerne povinny: kdyby byl nepovinny, prvni volani bez
nej by tise vratilo vsechna prava. Prekladac tim rovnou nasel vsech ctrnact
mist, ktera se ptaji.
- **Kazde misto se pta za tu spravnou firmu.** Akce nad ticketem za firmu toho
ticketu, uprava zaznamu za firmu toho zaznamu, zalozky a helpdesk za firmu,
kterou ma clovek prepnutou. Kdo edituje zaznam jine firmy, musi mit pravo tam,
ne tam, kde je zrovna prepnuty.
- **Cizi firemni role se ignoruje**, i kdyby na ni clenstvi odkazovalo. Jinak by
stacilo pripsat si ji do clenstvi v jine firme a prava by se prenesla.
- **Cache prav ma v klici firmu.** Bez toho by prvni dotaz odpovedel i na druhou.
- **`readScope` cte z jedne firmy, te prepnute**, ne ze vsech, kam clovek patri.
V nastaveni se driv michaly typy ticketu a role napric firmami bez ohledu na
vyber - stejna vada jako u prepinace, jen na jinem miste.
### Opraveno
- **Systemove role se pri startu srovnaji s kodem** (`syncSystemRoles`).
Vychozi sada se pouzije jen do prazdneho uloziste, takze nove pravo
v katalogu se k uz bezici instalaci nikdy nedostalo - `ticket.create`
a obe prava helpdesku by spravci firmy nikdy nenabehla a zalozka by se
neobjevila. Meni se jen prava, jen u nasich roli, nazvu se to netyka.
## 2026-08-26 - prepinac firmy plati pro cely portal
Prepinac byl stav uvnitr stranky Prehled. Prepnuti na LogiTrans zmenilo prehled
a nic jineho: ostatni stranky volaly API bez `tenantId` a server sahnul po
vychozi firme, takze v Lidech zustali lide Automie. To neni nepohodli, to jsou
cizi data pod hlavickou jine firmy.
### Zmeneno
- **Vybrana firma je jedna hodnota pro cely dashboard**
(`web/src/lib/tenant.ts`), ne stav stranky. Prezije obnoveni stranky.
- **`tenantId` doplnuje `apiFetch`**, ne jednotlive stranky. Kdyz si to mela
pridavat kazda stranka sama, vetsina na to zapomnela - a zapomnetlivost byla
prave ta chyba. Nedoplnuje se, kdyz cesta uz firmu nese (proklik z widgetu),
u `scope=all` a mimo `/api/dashboard`.
- **`useApiQuery` se pri prepnuti zepta znovu.** Bez toho by na strance zustala
cisla predchozi firmy, dokud by ji nekdo neobnovil.
- **Prepinac je v hlavicce nad obsahem.** Driv byl v Prehledu, ted plati
viditelne pro vsechno. Nahradil i text, ktery ukazoval natvrdo prvni firmu
ze seznamu bez ohledu na to, co bylo vybrane.
- **Ulozena firma, do ktere uzivatel uz nepatri, spadne na vychozi.** Jinak by
po odchodu z firmy koncil kazdy dotaz na 403.
### Zjisteno u toho
- **Prava se scitaji pres vsechny firmy**, neprepocitavaji se podle vybrane.
Kdo je spravce v jedne firme a resitel v druhe, ma po prepnuti porad prava
spravce. Data se tim neprolomi, server je filtruje dal podle firmy, ale
tlacitka ukazuji vic, nez by mela. Zapsane v
[07-firmy-a-prava.md](07-firmy-a-prava.md), sekce Co chybi.
## 2026-08-26 - helpdesk: pozadavek, ktery vidi zadavatel i resitel
Zakaznik nemel jak poslat pozadavek. Ticket pritom patri jedne firme, kdezto
u helpdesku figuruji dve - ta, ktera se pta, a ta, ktera to resi.
### Pridano
- **Pole `Ticket.helpdeskSourceId`** s firmou, ktera pozadavek poslala.
Vlastnikem (`tenantId`) zustava ta, ktera ho **resi**. Zamerne tak: kdyby byl
vlastnikem zadavatel, mel by resitel pozadavek jen jako cizi ticket a nemel by
ho ve sve fronte, ve statistikach ani v prirazovani. Takhle je to na jeho
strane obycejny ticket a nemuselo se sahnout na nic, co uz funguje.
- **`Tenant.helpdeskProviderId`**, tedy komu firma posila pozadavky. Nastavuje
spravce platformy v Nastaveni, Firmy. Kdo koho obsluhuje je obchodni vztah,
ne volba klienta - kdyby si dodavatele vybiral uzivatel, poslal by pozadavek
nekomu, s kym nema smlouvu. Bez vyplneneho dodavatele se pozadavek nezalozi
a rekne se to nahlas.
- **Sekce Helpdesk** (`/dashboard/helpdesk`) a router
`/api/dashboard/helpdesk`: seznam vlastnich pozadavku, zalozeni, detail
s prubehem a komentar. Zamerne to **neni druhy seznam ticketu** - chybi tu
filtry, prirazovani i fronta, protoze zadavatele nezajima, kdo to ma u sebe.
- **Prava `helpdesk.view` a `helpdesk.create`.** Prideluje je admin te firmy
pres role, stejne jako u ostatnich prav.
### Zmeneno
- **`listTickets` umi filtrovat podle zdroje** (`helpdeskSourceIds`). Vyplnene
**nahrazuje** filtr podle vlastnika, protoze zadavatel vlastnikem neni.
Bezny seznam ticketu se nezmenil.
- **`getTicket` a `addComment` maji druhou cestu dovnitr.** Firma, ktera
pozadavek poslala, ho smi cist a pripsat k nemu komentar, i kdyz ho nevlastni.
Komentar je jedina zmena, kterou nad nim smi: stav, resitele a typ urcuje ten,
kdo to resi.
## 2026-08-26 - portal prestal ukazovat provozni veci a ticket jde zalozit rucne
Sada oprav podle toho, co v portalu drhlo.
### Zmeneno
- **Hlasky o ulozisti a o odchozi IP adrese vidi jen spravce platformy.**
Kam se uklada a z jake adresy volame ven resime my, ne zakaznik. "Data se
ukladaji do souboru na serveru" na nej navic pusobi jako priznani, ze mu tu
praci muzeme ztratit. Nemazou se, jen se schovavaji - nam poradi porad.
Totez plati pro radek "volano z IP" v historii overeni.
- **Typ ticketu se v automatizaci vybira ze seznamu.** Bylo to textove pole,
do ktereho mel clovek opsat ID typu odjinud. Novy druh pole `lookup` je
**ciselnik a volny text zaroven**: bud se vybere ze seznamu typu te firmy,
nebo se hodnota dosadi z dat (`{{data.typ}}`). Typy ticketu jsou vlastnost
firmy, takze nabidka chodi za tu, ve ktere clovek je.
- **Stav ticketu je otevreny naseptavac, ne ciselnik.** Stav je volny retezec
a vzdycky byl - ticket muze prijit z cizi aplikace s jejim vlastnim stavem.
Vyber ze seznamu tomu odporoval. Ted je to textove pole s nabidkou toho, co
firma uz pouziva (`GET /api/dashboard/tickets/statuses`), a nova hodnota
projde stejne dobre. Zapisuje se az pri opusteni pole, ne po kazdem pismenu.
- **"Kanal" se prejmenoval na "Odkud pozadavek prisel"** a rika o sobe, ze je
nepovinny a slouzi jen k filtrovani a ikone v seznamu.
- **Vstupni parametry u webhooku jsou oznacene jako nepovinne.** Webhook prijme
cokoliv; rucne vypsany seznam parametru neni podminka, ale pohodli pro
podminky - a vyplni se sam z vlepene ukazky tela. Prazdny seznam uz nehlasi,
ze "bez nich nelze pridat podminku".
### Pridano
- **Zalozeni ticketu rucne** (`POST /api/dashboard/tickets`, pravo
`ticket.create`, tlacitko Novy ticket na strance Tickety). Dosud ticket
vznikal jen z automatizace nebo z prichozi udalosti, takze pozadavek prijaty
telefonem nemel jak do systemu.
- **Zakaznik u ticketu je nepovinny.** Rucne zalozeny ticket je casto ukol, ne
pozadavek od nekoho zvenku; povinna firma a kontakt by znamenaly, ze si je
clovek vymysli. Ve formulari je zakaznik schovany pod odkazem.
### Co z teze davky jeste neni
- **Sekce Helpdesk** pro zakaznika vcetne sdileni ticketu mezi admin firmou
a tim, kdo ho zalozil.
- **Prepinac firmy nad celym dashboardem.** Dnes je to stav uvnitr stranky
Prehled, takze se prepnuti neprojevi v Lidech ani jinde - ostatni stranky
volaji API bez `tenantId` a server pouzije vychozi firmu. Ma to doplnovat
API vrstva na jednom miste, ne kazda stranka zvlast.
## 2026-08-26 - odesilani e-mailu ze schranky firmy
Sluzba E-mail byla v katalogu jako popis: bez udaju, bez vykonne casti. Ted se
odesila doopravdy, ze schranky, kterou si firma vyplni v konektoru.
### Pridano
- **Sluzba E-mail pres SMTP.** V konektoru server, port, sifrovani, uzivatel,
heslo, adresa a jmeno odesilatele a adresa pro odpovedi. Zadne udaje
v prostredi - kazda firma odesila ze sve schranky.
- **Vnitrni krok `email/send`.** SMTP neni HTTP a skript umi jen `ctx.http`,
takze operaci vykonava vnitrni krok stejne jako zalozeni ticketu. Pristupove
udaje pritom zustavaji v konektoru.
- **Druh pole `html`.** Telo zpravy se pise jako HTML a builder ho vykresli
jako vysoke pole s neproporcionalnim pismem. Runtime v nem **escapuje
dosazene hodnoty**: znacky autora sablony jsou zamer, ostre zavorky
v hodnote od zakaznika ne. Bez toho by text ticketu s `` prepsal
rozvrzeni zpravy a `