# PRD 5.22 — UI compact Trimiteri + bara contextuala "Aplica cod" + exemplu Reguli automate + fix buton "+" editor import **Stare**: inchis > Mockup aprobat de utilizator (artifact `6efc43e0`, v2, 2026-07-07). Decizii inchise: > tabel desktop pe un rand cu header; status = bulina + tooltip; mediu = litera T/P; > multi-cod = primul cod + badge `+N` cu tooltip; sub-linie de problema = textul brut > al erorii, mono, trunchiat, doar pe erori; bara "Aplica cod" contextuala la selectie; > Reguli automate = exemplu concret in loc de paragraf descriptiv; fix no-op silentios > la butonul "+" din editorul de import. ## 1. Obiectiv Patru imbunatatiri UI/UX aprobate pe mockup: (a) lista Trimiteri devine tabel compact pe un singur rand pe desktop, cu header de coloane si semnalizare compacta status/mediu; (b) actiunea bulk "Aplica cod" devine bara contextuala vizibila doar la selectie; (c) sectiunea "Reguli automate (text)" din Mapari isi inlocuieste descrierea cu un exemplu concret; (d) butonul "+" din editorul de prestatii (import) nu mai esueaza silentios — feedback vizibil la succes si la refuz. ## 2. Non-Goals (anti scope-creep) - NU se schimba logica backend a `bulk-fix` (`POST /trimiteri/bulk-fix`) — doar prezentarea. - NU se schimba masina de stari `submissions`, filtrele, paginarea sau modalul de detaliu. - NU se adauga functionalitate noua la regulile text (`/mapari/reguli-text`) — doar microcopy. - NU se redeseneaza restul editorului de rand din import (doar zona chips "+"). - NU se ating API v1, worker-ul sau schema DB. ## 3. Stories atomice ### US-001: Fix buton "+" chips prestatii — feedback la refuz si la succes **Ca** operator care editeaza un rand de import **vreau** ca apasarea pe "+" sa aiba mereu un efect vizibil **pentru ca** azi codul invalid sau duplicat produce un raspuns identic, fara niciun mesaj ("nu se intampla nimic"). - **Depinde de**: — - **Fisiere**: `app/web/routes.py` (`post_form_chips`), `app/web/templates/_chips_prestatii.html`, `tests/test_web_corectie_prestatii.py` (~3 fisiere) - **Test intai (RED)**: `tests/test_web_corectie_prestatii.py` — `test_add_cod_duplicat_afiseaza_mesaj`, `test_add_cod_invalid_afiseaza_mesaj`, `test_add_cod_reusit_marcheaza_chip_nou` - **Acceptance criteria**: - [ ] Ramurile `add`, `add_extra`, `add_flat` din `post_form_chips`: cod care nu trece verificarea in `nomenclator_rar` → mesaj de eroare in fragmentul re-randat ("Cod necunoscut in nomenclator"), nu raspuns identic. - [ ] Cod deja prezent in lista (dedup) → mesaj "X este deja in lista — nu a fost adaugat inca o data", pe TOATE cele 3 ramuri. - [ ] Adaugare reusita → chip-ul nou primeste clasa de evidentiere (`chip-nou`, outline verde) + mesaj de confirmare "X adaugat" (aria-live pentru SR). - [ ] Mesajele traiesc in fragmentul `_chips_prestatii.html` (swap `outerHTML` pe `#chips-section`) si dispar la urmatoarea actiune reusita. - [ ] `python3 -m pytest -q` verde. - **Verificare E2E**: browser pe import → Editeaza rand → alege cod deja adaugat → "+" → mesaj rosu vizibil; alege cod nou → "+" → chip verde evidentiat + mesaj. ### US-002: Reguli automate — exemplu concret in loc de descriere **Ca** utilizator al paginii Mapari **vreau** sa vad un exemplu de regula in loc de un paragraf explicativ **pentru ca** exemplul arata direct formatul si scuteste cititul. - **Depinde de**: — - **Fisiere**: `app/web/templates/_mapari.html`, `tests/test_web_mapari_text_rules.py` (~2 fisiere) - **Test intai (RED)**: `tests/test_web_mapari_text_rules.py` — `test_sectiune_reguli_afiseaza_exemplu_nu_descriere` - **Acceptance criteria**: - [ ] Textul descriptiv din capul sectiunii "Reguli automate (text)" (inclusiv empty-state) e inlocuit cu un singur exemplu formatat: `Daca operatia contine "verificare" → Cod RAR = OE-2 Intretinere si revizie`, cu pattern-ul si codul evidentiate vizual (chip warn / chip accent, ca in mockup). - [ ] Nicio schimbare functionala la tabel, adaugare, preview sau stergere reguli. - [ ] `python3 -m pytest -q` verde. - **Verificare E2E**: browser pe Mapari → sectiunea Reguli automate afiseaza exemplul. ### US-003: View-model rand trimitere — coduri multiple + eroare bruta **Ca** dashboard **vreau** ca randul de trimitere sa expuna lista completa de coduri RAR si textul brut al erorii **pentru ca** UI-ul compact afiseaza `primul cod +N` (tooltip cu toate) si sub-linia de eroare cu mesajul RAR, nu eticheta explicativa. - **Depinde de**: — - **Fisiere**: `app/payload_view.py`, `app/web/routes.py` (`_submission_row_view`), `tests/test_payload_view.py`, `tests/test_web_submissions.py` (~4 fisiere) - **Test intai (RED)**: `tests/test_payload_view.py` — `test_coduri_rar_lista_ordonata_dedup`, `test_coduri_rar_gol_fara_prestatii`; `tests/test_web_submissions.py` — `test_rand_expune_eroare_bruta_doar_pe_error` - **Acceptance criteria**: - [ ] `prezentare_din_payload` intoarce camp nou `coduri_rar`: lista ordonata, dedup, a codurilor RAR (`cod_prestatie`) din TOATE prestatiile (lista goala cand nu exista); campurile existente raman neschimbate (compatibilitate). - [ ] `_submission_row_view` expune `eroare_bruta`: mesajul RAR brut cand exista (`rar_error` → mesajul serverului, ex. `ORA-12899...`), altfel `motiv` scurt; sir gol pe stari fara problema. `needs_mapping` NU produce `eroare_bruta` (informatia "nemapat" traieste in coloana Cod RAR). - [ ] `python3 -m pytest -q` verde. - **Verificare E2E**: — (acoperit de US-004). ### US-004: Trimiteri desktop — tabel cu header, un rand per trimitere **Ca** operator pe desktop **vreau** trimiterile intr-un tabel compact cu header de coloane **pentru ca** este loc pe orizontala si scanez mai repede o lista pe un rand. - **Depinde de**: US-003 - **Fisiere**: `app/web/templates/_submissions.html`, `app/web/templates/base.html` (CSS), `tests/test_web_submissions_layout.py` (~3 fisiere) - **Test intai (RED)**: `tests/test_web_submissions_layout.py` — `test_desktop_header_coloane`, `test_status_bulina_cu_tooltip`, `test_mediu_litera_t_p`, `test_multicod_badge_plus_n`, `test_sublinie_eroare_bruta_doar_pe_error` - **Acceptance criteria**: - [ ] Pe desktop (>= breakpoint existent), lista devine tabel/grid cu header: (checkbox) · Vehicul · Cod RAR · Operatie · Data · Status · Mediu · (actiuni). Un rand per trimitere, fara wrap; operatia primeste ellipsis. - [ ] Status = DOAR bulina colorata (culorile claselor `s-*` existente), nume stare complet in `title` (tooltip); coloana are headerul "Status". - [ ] Mediu = litera in coloana proprie cu header "Mediu": `P` alb pe fundal `--err` (productie), `T` contur discret (testare); `title` cu numele complet. - [ ] Mai multe coduri: primul cod + badge `+N` cu `title` care listeaza toate codurile din `coduri_rar`; un singur cod → fara badge; niciun cod → `nemapat`. - [ ] Sub-linia de problema apare DOAR cand `eroare_bruta` e ne-goala: text mono, `--fs-xs`, culoare `--err`, un singur rand cu `text-overflow:ellipsis`, `title` cu textul complet. - [ ] Comportamente pastrate: click pe rand → modal detaliu; checkbox doar pe `gestionabil`; buton "Trimite" doar pe `tinut`; paginare/filtre neatinse. - [ ] `python3 -m pytest -q` verde. - **Verificare E2E**: browser HTMX pe http://localhost:8010/ (desktop 1280px) — randuri pe o singura linie, header vizibil, tooltip-uri corecte. ### US-005: Trimiteri mobil — bulina status + litera mediu **Ca** operator pe mobil **vreau** aceleasi semnale compacte (bulina + T/P) pe lista de 2 linii **pentru ca** pill-urile cu text ocupa spatiu inutil pe ecran mic. - **Depinde de**: US-004 - **Fisiere**: `app/web/templates/_submissions.html`, `app/web/templates/base.html` (media query), `tests/test_web_submissions_layout.py` (~3 fisiere) - **Test intai (RED)**: `tests/test_web_submissions_layout.py` — `test_mobil_pastreaza_doua_linii_cu_bulina_si_litera` - **Acceptance criteria**: - [ ] Sub breakpoint, randul revine la 2 linii (placuta / cod·operatie·data), iar in dreapta stau litera de mediu + bulina de status (+ butonul "Trimite" pe tinute). - [ ] Fara header de tabel pe mobil; fara scroll orizontal la 390px. - [ ] `python3 -m pytest -q` verde. - **Verificare E2E**: browser la 390px — lista lizibila, fara overflow orizontal. ### US-006: Bara contextuala "Aplica cod" — vizibila doar la selectie **Ca** operator **vreau** ca actiunile bulk sa apara doar cand am selectat randuri, cu text explicit **pentru ca** butonul permanent "Aplica cod" fara context e confuz. - **Depinde de**: US-004 - **Fisiere**: `app/web/templates/_submissions.html`, `app/web/templates/base.html` (CSS + JS numarare selectie), `tests/test_web_bulk_fix.py` (~3 fisiere) - **Test intai (RED)**: `tests/test_web_bulk_fix.py` — `test_bara_bulk_ascunsa_implicit`, `test_bara_bulk_texte_explicite` - **Acceptance criteria**: - [ ] Inputul de cod + "Aplica cod" + "Sterge selectate" dispar din capul listei; apar intr-o bara contextuala (stil accent, ca in mockup) randata ascunsa si afisata prin JS cand >= 1 checkbox e bifat; dispare la deselectare. - [ ] Bara contine: contor "N selectate", input cod RAR, buton "Aplica acest cod la N randuri", separator, buton "Sterge N randuri", actiune "Deselecteaza"; N se actualizeaza la fiecare bifare (JS, fara roundtrip). - [ ] Sub bara: hint pe un rand — "Aplica codul RAR doar operatiilor fara cod din randurile selectate, apoi le repune in coada." - [ ] Endpoint-urile, CSRF, `hx-confirm` pe stergere si `hx-disinherit` raman functional identice; `bulk_message` se afiseaza ca acum. - [ ] Starea barei supravietuieste re-randarii fragmentului (re-evaluare la `htmx:afterSwap` — dupa swap selectia se pierde, bara se ascunde). - [ ] `python3 -m pytest -q` verde. - **Verificare E2E**: browser — capul listei curat; bifez 2 randuri blocate → bara apare cu "2 selectate"; aplic cod → lista se reincarca, bara dispare, sumar vizibil. ## 4. Riscuri - **Tabel vs. lista pe acelasi fragment**: `_submissions.html` e folosit si de paginare/filtre/OOB — orice schimbare de structura trebuie sa pastreze id-urile (`#submissions-wrap`, `#bulk-trimiteri`, OOB-urile). Mitigare: pastram form-ul si id-urile existente, schimbam doar interiorul listei; testele de layout existente (`test_web_submissions_layout.py`) ruleaza inainte si dupa. - **`rar_error` brut poate contine detalii tehnice lungi**: afisare pe un rand cu ellipsis + `title`; nu se logheaza/expune nimic nou (textul exista deja in modal). - **JS-ul de numarare selectie** ruleaza peste randuri re-randate de HTMX: legare prin delegare pe container, nu pe noduri (altfel handler-ii mor la swap). - **Doua semnale rotunde alaturate pe mobil** (bulina status + litera mediu): litera are forma/continut diferit de bulina — validat in mockup; daca in practica se confunda, fallback aprobat: mediu doar pe randurile de productie. ## 5. Intrebari deschise — (toate inchise prin mockup v2 aprobat) ## 6. Valuri de executie (graful de dependente) ``` Val 1: [US-001] [US-002] [US-003] ← fara dependente, fisiere disjuncte → paralel Val 2: [US-004] ← deblocat de US-003 Val 3: [US-005] [US-006] ← deblocate de US-004; ating aceleasi fisiere → secvential intre ele ``` --- ## Raport VERIFY **Verdict: PASS** — 2026-07-07, E2E in browser (Playwright, uvicorn pe DB temporar, 7 trimiteri seedate: sent/queued/held/needs_mapping/error, test+prod, multi-cod). - Desktop 1280px: header Vehicul/Cod RAR/Operatie/Data/Status/Mediu; un rand per trimitere; bulina colorata cu title; `P` fill rosu / `T` contur; `OE-1 +2` cu tooltip; sub-linie `ORA-12899...` mono trunchiata doar pe rândul error; `nemapat` italic fara sub-linie. - Bara bulk: ascunsa implicit; la 2 checkbox-uri → "2 selectate · Aplica acest cod la 2 randuri | Sterge 2 randuri | Deselecteaza" + hint; aplicat OE-2 → ambele randuri → `queued`, bara dispare. - Mapari: exemplul "Daca operatia contine «verificare» → Cod RAR = OE-2 Intretinere si revizie" inlocuieste descrierea. - Chips "+": adaugare OE-1 → chip evidentiat (chip-nou) + "OE-1 adaugat."; duplicat → "OE-1 este deja in lista — nu a fost adaugat inca o data."; select gol → "Selecteaza un cod RAR din lista". - Mobil 390px: fara header, 2 linii, T/P + bulina in dreapta, fara scroll orizontal. - Suita completa: **1622 passed, 1 skipped**. **Cauza-radacina REALA a bug-ului "+" (gasita LA VERIFY, nu in analiza statica)**: form-urile parinte din modalele de editare au `hx-disabled-elt="find button"`; atributul se MOSTENESTE pe butoanele chips, iar `find button` rezolvat relativ la butonul "+" (fara butoane descendente) → `null` → TypeError intern htmx 1.9.12 INAINTE de request → nicio cerere, nicio eroare vizibila. Fix: `hx-disabled-elt="this"` explicit pe toate butoanele hx-post din `_chips_prestatii.html` (care si dezactiveaza butonul cat cererea e in zbor). No-op-urile silentioase server-side (cod invalid pe add/add_flat, duplicat) erau defecte reale suplimentare, reparate separat. Observatii non-blocante: (1) `bulk_message` ("N reusite") dispare imediat — HX-Trigger `trimiteriChanged` reincarca lista peste raspunsul cu mesaj (comportament pre-existent); (2) pe mobil, operatia e trunchiata agresiv pe randurile cu buton "Trimite" (spatiul e consumat de actiune + mediu + status) — acceptat, informatia completa e in modal.