# 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.
