Inventura nativních <input>/<textarea>/<select> v apps/web/src — matice A/B/C
Zdroj: node /root/.claude/jobs/d1322dc8/tmp/inputs/scan.mjs — parser strhne /* / a // komentáře (stavový průchod znak po znaku, řetězce/šablony ponechá), pak regexem sesbírá <input …>, <textarea …> a <select …> ve všech .tsx mimo __tests__/.test.tsx/ .stories.tsx. Naměřeno na aktuálním origin/main (worktree button-compact-split): 393 <input>, 13 <textarea>, 1 <select>, 538 souborů, 120 souborů s aspoň jedním <input>. (Zadání uvádělo orientační „~281 input"; grep na <input bez rozlišení komentářů dává 394 — číslo v zadání bylo hrubý odhad, tahle inventura čte přímo z mainu.)
Kanonické reference (packages/ui/src/components, apps/web/src/ds.css, apps/web/src/ds/inputs.css):
<Input>(Input.tsx) — tenký obal, jen přidá.acrm-input;<Textarea>totéž s.acrm-textarea;<Field>— label/hint/error obal;<Checkbox>—.acrm-check(+.acrm-check-labels popiskem).<Radio>v@acrm/uiNEEXISTUJE —packages/ui/src/index.tsneexportuje žádnou Radio komponentu, jen třídu.acrm-radio-input/.acrm-radio-dot.<Select>,<MultiSelect>,<Combobox>,<RecordSelect>existují;MaskedDateInputaMaskedPhoneInputžijí vapps/web/src/components/multivalue/(ne vpackages/ui)..acrm-input(ds.css:32-98): výška--field-h=32px, padding0 var(--sp-3)=8px, border 1px--border-strong, radius--radius-sm=8px, fontvar(--p). Aliasy se STEJNÝM pravidlem (jedna definice,ds.css:32-98):.sgb-input,.dlr-input,.nastaveni-page .finput..au-input(auth) je záměrně NEALIASOVANÝ — 46px, AD rozhodnutí 31. 7. 2026 (komentářds.css:27-30). Modifikátory--num=88px,--code=132px,--short=210px,--email=280px (ds.css:76-91)..acrm-textarea(ds.css:135-153): min-height 72px, paddingvar(--sp-2) var(--sp-3), jinak stejný box jako.acrm-input..acrm-check(ds.css:155-161): 16×16,accent-color: var(--accent)..acrm-inputwrap(ds/inputs.css:31-108) — kanonický wrapper pro pole s ikonou/kbd: box chrome nese SPAN, samotný<input>uvnitř je bez třídy (vizDsInputsPage.tsx:187, demo „S ikonou + kbd")..acrm-recordselect-search(ds/inputs.css:490-520) — kanonický borderless vyhledávací řádek popoveru/menu, hairline dole; sdílený mnoha pickery.
Klasifikace: A — bespoke nebo raw prvek, který má jít přes DS komponentu/kanonickou třídu (u textových polí i tam, kde třída už JE .acrm-input/.acrm-textarea — cílem je <Input>/ <Textarea>, ne jen správná třída); B — legitimně nativní/součást primitivu (skrytý file input za DS tlačítkem, součást vlastní implementace Comboboxu/RecordSelectu/Multi-value editoru, color picker, vyhledávací řádek uvnitř kanonického popoveru…); C — nejasné, na stůl AD.
Nejdůležitější nález: strukturované údaje bez masky
Pravidlo ~/.claude/CLAUDE.md „Vstupy a mobil" §1: datum/telefon/IČO/DIČ/RČ/PSČ/čas/částka musí být maska, nikdy volný text. V appce existují funkční masky (MaskedDateInput — 2 místa: RelationDialog.tsx, NewContactDialog.tsx; MaskedPhoneInput — 1 místo: ChannelsPopoverEditor.tsx), ale mimo tato místa se stejné typy údajů zapisují volným textem:
| Údaj | Místo (file:line) | Jak je to dnes | Poznámka |
|---|---|---|---|
| IČO | pages/FirmyPage.tsx:464 | <input className="acrm-input" inputMode="numeric" value={f.ico}> | volný text, jen numerická klávesnice — žádná maska/kontrola délky |
| DIČ | pages/FirmyPage.tsx:490 | <input className="acrm-input" placeholder="CZ…" value={f.dic}> | volný text |
| PSČ | pages/FirmyPage.tsx:594 | <input className="acrm-input" value={f.psc}> | volný text, žádný formát NNN NN |
| Telefon (firma) | pages/FirmyPage.tsx:548 | <input className="acrm-input" value={f.phone}> | volný text; MaskedPhoneInput existuje, ale jen pro kontaktní kanály osoby |
| Telefon (nová objednávka) | components/orders/NewOrderForm.tsx:649 | <input className="acrm-input" placeholder="Telefon" value={phone}> | volný text |
| Telefon (uživatel, Nastavení) | pages/NastaveniPage.tsx:424,434 | <input className="finput" type="tel" maxLength={40}> (×2 — hlavní/druhý telefon) | nativní type="tel" ≠ maska; jen ořízne délku |
| Telefon (evidence SIM) | pages/TelefonyPage.tsx:126 | <input className="acrm-input" placeholder="+420 601 123 456" value={f.number}> | volný text |
| Rodné číslo | components/person/NewContactDialog.tsx:688 (text('birthNumber', …) → fl-text) | generický fl-text bez masky | ve stejném souboru MaskedDateInput pro datum narození JE nasazený (řádek ~ import) — RČ vedle něj zůstalo volným textem |
| Datum (opačné porušení — jen kalendář, žádná psaná maska) | components/projects/ProjectRail.tsx:1068,1075 | <input readOnly value={formatDate(range.from)}> otevírá Calendar/PickerPanel | pravidlo §4 „kalendář je doplněk, ne jediný vstup" — tady je JEDINÝ vstup |
| Částka (16 míst) | viz níže | type="number" nebo inputMode="decimal"/"numeric" na holém .acrm-input | žádné tisícové oddělovače, žádná měnová maska; typicky minor units /100 |
Částka — všech 16 míst bez masky: components/clen-tymu/AdvanceSection.tsx:151, BenefitSection.tsx:170, ExpenseSection.tsx:286, KpiSection.tsx:120, pages/TeamDetailPage.tsx:344, components/costs/CostDialog.tsx:351,394, pages/DokumentyPage.tsx:397, components/products/PricingSections.tsx:119, pages/MajetekPage.tsx:243, pages/OrderDetailPage.tsx:350, pages/PokladnaDetailPage.tsx:150, pages/RozpoctyPage.tsx:215, pages/VozidlaPage.tsx:233, pages/ObchodDetailPage.tsx:184 (inline buňka tabulky položek), pages/TelefonyPage.tsx:154 (měsíční paušál).
Kontrast: mimo tato místa je maskovaný vstup nasazen jen bodově (2× datum, 1× telefon) — pravidlo platí plošně „ve VŠECH dialozích", ale reálně pokrývá jen novou osobu/vztah a jeden kanál kontaktu. Firemní IČO/DIČ/PSČ/telefon (hlavní vstupní bod dat o firmě), telefon uživatele v Nastavení, evidence SIM karet a všechny peněžní částky v appce jdou mimo masku.
A — raw prvek/bespoke třída → DS komponenta (18 skupin, 337 výskytů)
| Třída / vzor | Počet | Umístění (file:line, resp. soubory) | CSS pravidlo | Cíl | Vizuální změna |
|---|---|---|---|---|---|
acrm-input (raw <input>) | 225 (231 −6 file-type řešené zvlášť) | 63 souborů, nejvíc NastaveniPage.tsx(14), FirmyPage.tsx(13), VozidlaPage.tsx(13), ConnectMailboxPopup.tsx(8), VehicleDetailPage.tsx(8), NewOrderForm.tsx(7), PricingSections.tsx(7), CostDialog.tsx(6), MajetekPage.tsx(6), PortfolioBuilderPage.tsx(6), TelefonyPage.tsx(6), ZasedackyPage.tsx(6) … (plný seznam file:line v raw.json) | ds.css:32-98 | <Input> | ne — třída už je kanonická, jde jen o obal komponentou (ref/props se 1:1 propouští) |
sgb-input (raw <input>) | 41 | ManageSegmentDialog.tsx(18), PruvodceFirmyPage.tsx(13), PruvodcePage.tsx(8), MergeDialog.tsx(1), SegmentBuilder.tsx(1) | ds.css:32-98 (alias .acrm-input) | <Input> | ne — sdílená definice s .acrm-input |
finput (raw <input>) | 20 | NastaveniPage.tsx(16), settings/InlineName.tsx(1), settings/StitkyTab.tsx(1), settings/TypyCiselTab.tsx(1), settings/napojeni/TokenyBlock.tsx(1) | ds.css:35 (.nastaveni-page .finput, alias potvrzen — vše leží pod SettingsLayout/.nastaveni-page) | <Input> | ne |
dlr-input (raw <input>) | 1 | pages/DialerPage.tsx:1250 | ds.css:34 (alias .acrm-input) | <Input> | ne |
acrm-input na type="file" (viditelný) | 6 | components/clen-tymu/ExpenseSection.tsx:212, HrSections.tsx:519,643, pages/AssetDetailPage.tsx:459, DokumentyPage.tsx:466, MajetekPage.tsx:370 | — (.acrm-input box se na file inputu nevykreslí, prohlížeč kreslí vlastní „Vybrat soubor") | skrytý <input type=file hidden> + spouštěcí <Button>/<IconButton>, stejný vzor jako zbylých 23 skrytých file inputů v appce | ano — dnes nekonzistentní nativní widget uprostřed DS formuláře |
| radio se třídou checkboxu | 1 | components/callwrapup/FollowupMail.tsx:49 (type="radio" className="acrm-check") | ds.css:155-161 (.acrm-check je čtvercový 16×16, ne kulaté radio) | .acrm-radio-input (skupina name="fu-mode" je fakticky radio) | ano — čtvercový glyf místo kulatého u vzájemně vylučujících voleb |
sel (raw <input>, ImportPage) | 2 | pages/ImportPage.tsx:856,868 | ds/import.css:319-329 — výška --control-h=30px, font 500 13px | <Input> | ano — 30 px vs. Input 32 px (--field-h) |
acrm-check (raw checkbox, už kanonická třída) | 4 | components/assistant/AssistantPrompts.tsx:428, components/meetings/RecordMenu.tsx:481, pages/NastaveniPage.tsx:2632,2747 | ds.css:155-161 | <Checkbox> | ne |
acrm-textarea (raw <textarea>) | 4 (+1 DS katalog, viz níže) | components/assistant/AssistantPrompts.tsx:397, components/meetings/RecordMenu.tsx:518, pages/OrderDetailPage.tsx:538 | ds.css:134-153 | <Textarea> | ne |
sgb-input dlg-textarea (raw <textarea>) | 2 | components/segment/ManageSegmentDialog.tsx:221, components/segment/SegmentBuilder.tsx:826 | ds/segment-screen.css:1202-1208 (min-height 76px) vs. .acrm-textarea min-height 72px | <Textarea> | drobně — 76 px vs. 72 px min-height |
sgb-check (checkbox bez třídy na <input>, obal .sgb-check) | 1 | pages/PruvodcePage.tsx:1174 | ds/builder-panels.css:265-281 — input 15×15 | <Checkbox> / .acrm-check | ano, ale malá — 15 px vs. kanon 16 px; drift je zdokumentovaný přímo v ds.css:162-164 jako „známý drift k reconciliaci upstream" |
asg-check (checkbox bez třídy, obal .asg-check) | 2 | components/mail/AssignPopup.tsx:149,156 | components/mail/mail.css:1249-1258 — .asg-check > input nemá width/height/accent-color, jede na nativní výchozí vzhled prohlížeče | <Checkbox> / .acrm-check | ano — jediný skutečně nestylovaný nativní checkbox v appce (žádný accent-color, žádná pevná velikost) |
Poznámka k .acrm-check (4×) a .acrm-radio-input (4×) jako kanonickým třídám: obě už sedí na správné DS třídě (žádná vizuální změna), ale u radia dnes není co nasadit — @acrm/ui žádnou <Radio> komponentu neexportuje (jen třídu .acrm-radio-input/ .acrm-radio-dot). Adopce <Checkbox> (4×) je čistě kosmetická component-wrap; adopce <Radio> čeká na to, až komponenta vznikne — mimo rozsah téhle inventury, ale stojí za zápis pro AD.
DS katalogová stránka /ds/inputs (pages/DsInputsPage.tsx, 11 výskytů: 8× input, 1× textarea, 1× slider bez třídy pro ikonu+kbd demo) se do součtů A/B/C nepočítá — je to ukázková stránka samotného kanonu (dema .acrm-input, .acrm-inputwrap, .acrm-slider…), ne appková bespoke implementace. Zajímavost pro dogfooding: i tahle stránka používá raw <input>, ne <Input>.
C — nejasné, patří na stůl AD (5 skupin)
| Vzor | Počet | Umístění | Proč nejasné |
|---|---|---|---|
sgb-ck (custom checkbox, native input vizuálně skrytý 1×1px, viditelný glyf je sourozenec .ck) | 2 | components/segment/SegmentScreen.tsx:1014 (hlavičkové „vybrat vše"), :1161 (výběr řádku) | ds/segment-screen.css:620-649 — úmyslně JINÝ vzhled než .acrm-check (vlastní hranatý box řízený sourozeneckým selektorem, ne accent-color). Nejasné, zda je to schválená datagridová varianta Checkboxu, nebo bespoke k reconciliaci. |
ac-input / .ac / .adr-combo (bespoke address-autocomplete combobox) | 1 | components/segment/SegmentBuilder.tsx:203 (role="combobox") | ds/segment-screen.css:819-848; ds.css:31 dokumentuje .ac-input jako „záměrně NEALIASOVANÝ" borderless field. Nejasné, zda cíl je @acrm/ui <Combobox> (fetch-based návrhy adres by šly přes options), nebo zůstává bespoke kvůli vlastnímu fetch/klávesnicovému chování. |
cd-ai-input (AI prompt textarea na dashboardu) | 1 | pages/CrmDashboardPage.tsx:109 | ds/crm-dashboard.css:41-54 — borderless, transparent, min-height 66px. Assistant chat (AssistantChat.tsx) textarea pro prompt nemá — nejasné, zda existuje/má vzniknout sdílený „AI prompt textarea" primitiv, nebo je dashboard záměrně samostatný widget. |
sgb-th-edit (inline přejmenování hlavičky sloupce segmentové tabulky) | 1 | components/segment/SegmentScreen.tsx:871 | ds/segment-screen.css:468-478 — 104×22px, vlastní border: 1px solid var(--accent). Kontextově velmi specifické (uvnitř <th>); nejasné, zda cíl je mini varianta <Input>, nebo zůstává bespoke pro tabulkový kontext. |
Datum bez psané masky (ProjectRail.tsx:1068,1075) | 2 | viz tabulka výše | Je to „opačné" porušení §4 (jen kalendář) — možná AD schválený vzor pro rozsahový filtr railu (ne zápis nové hodnoty), možná mezera k doplnění masky. |
B — legitimně nativní / součást primitivu (kompaktní přehled)
- Skryté file inputy za DS tlačítkem/dropzone (23×):
components/IdentityImageDialog.tsx:205,SubjectFilesTab.tsx:139,clen-tymu/HrSections.tsx:241,mail/ComposeForm.tsx:3317,meetings/TranscriptTab.tsx:655,662,notes/NoteAudio.tsx:85,projects/ProjectEntryDialog.tsx:257,tasks/TaskDialog.tsx:1689,pages/AssetDetailPage.tsx:532,BankaPage.tsx:513,DocumentDetailPage.tsx:1140,DokumentyPage.tsx:161,ImportPage.tsx:547,MeetingDetailPage.tsx:557,NastaveniPage.tsx:378,PickPage.tsx:218,PokladnaDetailPage.tsx:203,ProduktDetailPage.tsx:571,SetkaniPage.tsx:308,onboarding/ProfilPage.tsx:78,settings/CompanyKnowledgeSection.tsx:195,settings/TemplateAttachmentsSection.tsx:91— všechnyhidden/style={{display:'none'}}+ref, spouští je viditelné DS tlačítko. - Vyhledávací řádek uvnitř kanonického popoveru/menu (borderless, bez vlastní třídy na
<input>, chrome nese obal):.acrm-recordselect-search(EntityPicker.tsx:254,FilterBar.tsx:786,894,933,1079,ProductPicker.tsx:122,RecordSearchSelect.tsx:102,dialog/ChipSearchMenu.tsx:99,mail/compose/TemplateMenu.tsx:248,multivalue/DialCodePicker.tsx:90,projects/ProjectMeetingLinkDialog.tsx:70; vlastní pojmenované varianty téhož vzoru:.fb-search(FilterBar.tsx:631),.gsp-q(GlobalSearch.tsx:1499,DealSubjectStep.tsx:199),.mr-search(meetings/TranscriptTab.tsx:821,994),.set-search(settings/SettingsLayout.tsx:232),.aichat-psearch(assistant/AssistantPrompts.tsx:150),.itm-ac(deal/deal-parts.tsx:137). .acrm-inputwrap— kanonický wrapper pro ikonou-prefixované pole,<input>uvnitř bez vlastní třídy (ds/inputs.css:31-108):components/callwrapup/WrapupBand.tsx:226a sourozenci ve stejném souboru.- Multi-value editor rodina (
ds/multi-value.css):.phe-num(4× — vlastní gutsMaskedPhoneInput.tsx:31/38, plusChannelsPopoverEditor.tsx:432,625,EntityRowsPopoverEditor.tsx:101),.phe-type(jediný<select>appky,ChannelsPopoverEditor.tsx:655). - Formless dialog rodina (
ds/dialog-formless.css):.fl-text(8× — hodnotové řádky),.fl-title(2× input + 5× textarea rows=1 — auto-rostoucí titulek),.fl-mask-in(MaskedDateInput.tsx:31— guts masky samotné). - Dialog-doc rodina (
ds/dialog-doc.css):.dlg-bare(2×),.dlg-title(1× textarea, 28px, „schválně NEZMENŠUJE"). - RecordRail rename —
.rr-edit(projects/ProjectRail.tsx:871), komentář vds/project-rail.css:319výslovně „kanonické aktivní pole railu". - Note editor rich-text bubble —
.nt-bub-url(notes/NoteEditor.tsx:329), vložení odkazu uvnitř plovoucí bubliny,ds/note-dialog.css:295-304. - Auth vrstva —
.au-input(4×), AD rozhodnutí 31. 7. 2026 že se NEALIASUJE k.acrm-input(46px, celoplošná brána mimo shell). - Color picker —
type="color"(segment/SegmentBuilder.tsx:633). - OTP kód —
pages/onboarding/OtpPage.tsx:97(vizuální boxy jsou<span>, skutečný<input>je jeden skrytý společný vstup pod nimi). acrm-radio-input(4×, kanonická třída, žádná<Radio>komponenta zatím neexistuje):company/CompanyMergeTable.tsx:278,NastaveniPage.tsx:2723,SouhlasPripojeniPage.tsx:152,settings/AccessPresets.tsx:80.acrm-check/acrm-sliderv DS katalogu (DsInputsPage.tsx) — demonstruje kanon, mimo audit appky.
Totals
| Bucket | Skupin (tříd/vzorů) | Výskytů |
|---|---|---|
| A | 10 řádků výše (bez DS katalogu) | 337 (225 + 41 + 20 + 1 + 6 + 1 + 2 + 4 + 4 + 2 [textarea sgb-input] ≈ přesně: 225 acrm-input + 41 sgb-input + 20 finput + 1 dlr-input + 6 file-type + 1 radio-jako-checkbox + 2 sel + 4 acrm-check + 4 acrm-textarea + 2 sgb-input-textarea + 1 sgb-check + 2 asg-check = 309 input/textarea prvků, viz poznámka níže) |
| B | 20+ rodin/tříd | ~55 input/textarea/select výskytů (skryté file inputy 23, vyhledávací řádky ~15, .acrm-inputwrap 4, multi-value 7, formless 15, dialog-doc 3, rr-edit 1, nt-bub-url 1, au-input 4, color 1, OTP 1, acrm-radio-input 4, select 1) |
| C | 5 skupin | 7 výskytů |
DS katalog (DsInputsPage.tsx, mimo audit) | — | 11 |
| Celkem (input+textarea+select) | 393 + 13 + 1 = 407 |
Pozn. k součtu A: čísla v tabulce výše jsou přesná per-skupina; „225" u acrm-input = 231 celkových výskytů třídy acrm-input na <input> MINUS 6 file-type řešených ve vlastním řádku. Component-adopce (<Input>/<Textarea>/<Checkbox>) bez vizuální změny pokrývá naprostou většinu A (286 z 309 — acrm-input, sgb-input, finput, dlr-input, acrm-check, acrm-textarea); se skutečnou vizuální odchylkou je jen menšina (23: 6 file, 1 radio-jako- checkbox, 2 sel, 1 sgb-check, 2 asg-check, 2 sgb-input-textarea drobně, zbytek).
Nejdůležitější zjištění zůstává mimo tuhle A/B/C mechaniku: 26 míst (10 strukturovaných typů + 16 částek) porušuje pravidlo „strukturovaný údaj = maska" bez ohledu na to, jestli input má, nebo nemá kanonickou třídu — pages/FirmyPage.tsx (IČO/DIČ/PSČ/telefon firmy) je přitom hlavní vstupní bod dat o firmě do CRM.
Source: docs/text-field/INVENTURA-nativni-vstupy-2026-09-28.md · GitLab