# Ralph Progress Log
Started: 2026-06-24
Project: autopass-5.9-ux-corectie-modal-mobil
Branch: ralph/5.9-ux-corectie-modal-mobil
Source PRD: docs/prd/prd-5.9-ux-corectie-modal-mobil.md
Note: PRD APROBAT 2026-06-24 cu revizii obligatorii R1-R12 (raport AUTOPLAN). R1-R12 au prioritate unde difera de AC original — sunt deja incorporate in acceptance criteria.
---

## Iteratie: 2026-06-24
### Story implementat: US-001 - Eticheta umana scurta pe randul de afisare + cod brut disponibil pentru modal (R1) (tags: backend)
### Status: Complete

### Gates rulate:
- Typecheck: SKIP (techStack.commands.typecheck gol)
- Lint: SKIP (techStack.commands.lint gol)
- Tests: PASS (tests/test_web_submissions.py 22; suita completa 819 passed, 1 deselected)
- /review (backend): PASS — no issues (DRY respectat, defensiv, aditiv non-regresie)

### Ce s-a schimbat:
- app/web/routes.py: helper `_eticheta_problema(status, motiv)` + constanta `_STARI_CU_PROBLEMA`;
  `_submission_row_view` hoisteaza `motiv` intr-o variabila si adauga campul `eticheta_problema`
  (text uman scurt: motiv || eticheta_scurta; gol pe queued/sending/sent).
- app/web/labels.py: `parse_erori` expune cheia `cod` (cod brut de catalog) pe ramurile imbogatite
  (lista + dict), pentru ca modalul (US-004) sa-l deriveze via parse_erori(...)[0]['cod'] — fara decoder nou (R1 DRY).
- tests/test_web_submissions.py: 5 teste US-001 (sub_pill R1 rename, error/needs_mapping prezent, gol pe ok, defensiv JSON invalid).

### Learnings:
- `parse_erori` traieste in app/web/labels.py (nu app/errors.py cum sugera AC#4) — `app/errors.py` defineste CATALOG-ul de coduri si `eroare()`.
- `_GESTIONABILE_WEB` == aceleasi 3 stari cu problema; am definit `_STARI_CU_PROBLEMA` separat pentru claritate semantica (gestionabil != are-problema conceptual).
- Eticheta de pe rand e adaugata DOAR in view-model; randarea sub pill apartine US-002 (de aceea testele sunt unitare pe _submission_row_view).

### Next:
- US-003 (modal, ui, requiresDesignReview) — independent, priority 15.
- US-002 depinde de US-001 (acum done) + US-003.
---
## Rate limit la iter 10 — sleep 1800

## Iteratie: 2026-06-25
### Story implementat: US-002 - Tabel trimiteri: eticheta umana sub stare, fara chevron, cod RAR simplu, rand declanseaza modalul (tags: ui)
### Status: Complete

### Gates rulate:
- Typecheck: SKIP (techStack.commands.typecheck gol)
- Lint: SKIP (techStack.commands.lint gol)
- Tests: PASS (tests/test_web_submissions.py 20; suita completa 826 passed, 1 deselected)
- Browser (gstack/E2E requiresBrowserCheck) si /review: DEFERATE la VERIFY (loop fara browser) — vezi AC E2E.

### Ce s-a schimbat:
- app/web/templates/_submissions.html: sub pill (col-stare) randeaza `r.eticheta_problema`
  (text mic `eticheta-problema s-error`, DOAR cand e ne-gol); col-operatie linia 2 arata
  codul RAR ca chip `cod-rar-cod` FARA prefixul "cod RAR:" (nemapat -> "nemapat" muted).
- app/web/templates/base.html: CSS nou `.eticheta-problema`, `.cod-rar-cod`, si regula R8
  44px touch-target pe `tr.trimitere-row` (min-height + padding 11px) + afordanta hover/focus.
- tests/test_web_submissions.py: 7 teste noi US-002 (eticheta sub pill + absenta pe ok, fara
  chevron, cod RAR fara prefix, nemapat, rand deschide modal, a11y+keyboard markup); actualizat
  `test_operatie_contine_cod_rar` (fara prefix) si `test_tabel_nu_are_coloana_motiv` (eticheta sub pill).

### Learnings:
- Mare parte din scope-ul US-002 era deja livrat de US-001 (campul `eticheta_problema`) si US-003
  (randul declanseaza modalul: role=button/tabindex/aria-haspopup, fara aria-expanded; htmx:beforeRequest
  fara toggle). Chevron-ul NU exista in cod (grep zero) -> R8 a insemnat doar adaugarea regulii 44px, nu eliminare.
- `motiv_uman` echo-ul mesajului RAR => eticheta_problema poate contine textul brut al erorii. Asta a
  intrat in conflict cu vechiul `test_tabel_nu_are_coloana_motiv` (5.8) care interzicea continutul Motiv
  in tabel; US-002 R1 il surfaceaza intentionat sub pill, deci testul a fost actualizat (pastreaza doar
  invarianta "fara coloana/celula Motiv").

### Next:
- US-004 (detaliu editabil in-place + butoane in modal), US-005 (poll nu inchide modalul), US-006/007/008 (responsive).
- VERIFY: ruleaza gstack E2E pe `/` pentru AC requiresBrowserCheck (eticheta sub pill, fara chevron, cod fara prefix, click deschide modal).
---

## US-006: Fundatie responsive — viewport, header/nav, modal full-screen mobil, breakpoint-uri

### Ce s-a livrat:
- app/web/templates/base.html: CONVENTIE breakpoint unic 768px documentata (CSS custom props nu
  merg in @media -> folosim consecvent `@media (max-width:767px)` pentru mobil; >=1024px = desktop
  neschimbat). Blocul `@media (max-width:767px)` extins cu:
  - Modal full-screen pe mobil: overlay fara padding + align/justify:stretch (fara backdrop lateral);
    dialog 100vw/100vh, fara border-radius/umbra, padding-top:56px, scroll intern; modal-close 44x44px.
  - Header colapsat: flex-wrap + gap + padding redus; h1 mai mic; main padding 16px.
  - Tinte touch >=44px: .icon-btn (toggle tema/cont hamburger), .tab-link, itemii .cont-menu.
  - Comentariul CSS desktop al modalului actualizat (full-screen mobil livrat, nu „vine in US-006").
- tests/test_web_responsive.py NOU: test_viewport_meta_prezent, test_modal_fullscreen_clasa_mobil,
  test_nav_colapsabil_sub_breakpoint (markup/CSS in HTML randat prin TestClient).

### Learnings:
- meta viewport era DEJA prezent (base.html:5) -> AC „confirmat/adaugat" = confirmat, neatins.
- Nav colapsabil (hamburger cont #cont-menu-toggle ☰ + dropdown hidden; tab-bar cu overflow-x:auto)
  era deja livrat de PRD-uri anterioare (5.5 US-006, 5.3 tab-bar). US-006 5.9 a adaugat DOAR
  tratamentul touch 44px sub breakpoint + modalul full-screen (modalul e nou din US-003 5.9).
- Desktop nefatat: regula de baza `.modal-dialog { max-width:680px }` ramane; doar media mobil o
  suprascrie. Test asigura ambele (max-width:680px prezent + media 767px cu 100vw/100vh/44px).

### Next:
- US-007 (responsive pagini continut), US-008 (responsive Acasa + login/signup) — depind de US-006 (gata).
- VERIFY: gstack browser la 375px pe `/` — fara scroll orizontal de pagina; ☰ deschide meniul cont;
  modal deschis ocupa tot ecranul si se inchide din `x` (requiresBrowserCheck + requiresDesignReview).
---

## US-004: Detaliu editabil in-place + butoane consolidate (in modal), ordine verticala R10

### Ce s-a livrat:
- app/web/templates/_trimitere_detaliu.html REscris pe ordinea verticala R10:
  (1) header #id + pill + motiv uman; (2) bloc eroare blocanta (card_erori) cand exista;
  (3) mapare inline (5.7) cand nemapate_inline, tintind #detaliu-modal-body; (4) formular
  editabil SAU context read-only; (5) actiuni jos; (6) <details> „Detalii tehnice" colapsat.
- Zero dublare: blocul read-only de grila (vechiul :19-32) si formularul de corectie s-au
  contopit. Campurile editabile (nr_inmatriculare, vin, data_prestatie, odometru_final,
  odometru_initial) apar O SINGURA DATA, ca inputuri pre-completate. Nr. pe rand propriu,
  VIN dedesubt (ambele latime plina), restul in grila.
- R9: operatie + cod RAR rezolvat read-only deasupra campurilor, folosind prez.cod_rar
  (fallback „nemapat"), fara eticheta separata „Cod RAR" (vechiul :23 eliminat).
- R2 (fix F7): buton primar CONDITIONAT DE STARE, unul singur per stare:
  * error -> „Re-pune in coada" pe /repune (error NU e editabil, /corecteaza ar da 403);
  * needs_data/needs_mapping -> „Salveaza si retrimite" pe /corecteaza (submit-ul formularului).
  Butonul gol „Re-pune in coada" duplicat (vechiul :55-59) eliminat.
- R11: UN SINGUR „Sterge" (outline distructiv var(--err)) pe RAND SEPARAT (clasa
  .detaliu-actiuni-jos, NU margin-left:auto), hx-confirm specific
  „Stergi definitiv trimiterea #{id}? Nu se poate anula."; full-width stivuit pe mobil
  prin regula noua `.detaliu-actiuni-jos button { width:100% }` in @media 767px (base.html).
- R5: hx-disabled-elt="find button" pe toate cele 3 forme (corectie/repune/sterge + mapare);
  inchiderea modalului pe succes vine din HX-Trigger inchideModal emis de rute (neschimbat).
- R4: scriptul inline modal-appropriate (curatat deja de US-003) pastrat; comentariu actualizat.
- Detalii tehnice colapsate: Nr. prezentare RAR, Cod HTTP, Reincercari, timestamps, mesaj RAR
  brut + cod brut (erori_3n[0].cod, doar cand exista).
- tests/test_web_corectie.py: 6 teste noi US-004 (test_camp_apare_o_singura_data,
  test_nr_si_vin_pe_randuri_separate, test_un_singur_buton_primar_per_stare,
  test_error_foloseste_repune, test_sterge_prezent_si_distinct, test_corectie_pastreaza_comportament).

### Learnings:
- F7 (CRITICAL): consolidarea naiva pe /corecteaza ar fi scos retry-ul randurilor `error`
  (_CORECTABILE=needs_data/needs_mapping; post_corectie da 403 pe error). Rezolvat prin buton
  primar conditionat de stare: error pastreaza fluxul existent /repune (re-pune in coada),
  needs_data/needs_mapping merg pe /corecteaza. Logica backend a rutelor NESCHIMBATA.
- _detaliu_ctx neatins (avea deja toate flag-urile necesare: editabil, gestionabil, status,
  prez.cod_rar, erori_3n). Zero logica noua in routes.py.
- test_web_lifecycle.py::test_buton_sterge_doar_pe_blocate ramane verde (error arata
  „Re-pune in coada", sent nu) — fluxul error->repune e pastrat.

### Next:
- VERIFY (requiresBrowserCheck + requiresDesignReview): gstack browser — needs_data arata
  fiecare camp o data (nr. rand propriu, VIN dedesubt), corectez data, „Salveaza si retrimite"
  -> queued; error arata „Re-pune in coada"; „Sterge" clar separat, rosu, full-width pe mobil.
---

## US-007: Responsive — paginile de continut (Mapari, Cont, Nomenclator, Integrare, Jurnal, Admin)

### Decizia per-tabel (R12):
- Mapari = CARD per rand sub 767px (tabele actionabile: selecturi nomenclator + butoane +
  kebab). Toate cele 4 tabele (De rezolvat, Mapari salvate, Formate coloane, Reguli text)
  primesc `data-eticheta` pe `<td>`-uri + clasa `tabel-card` pe `.tablewrap`.
- Jurnal = .tablewrap (scroll orizontal contained). Dens read-only, multe coloane (Cand/Sursa/
  Tip/Nivel/Cont/Cod/Mesaj) — cardul ar fi lung si zgomotos; scroll contained pastreaza tabularitatea.
- Nomenclator = .tablewrap (scroll contained). Dens read-only (Cod/Denumire/Actualizat).
- Admin = .tablewrap (scroll contained). Actionabil DAR admin-only, dens (8 coloane), actiunile sunt
  deja in kebab compact + bulk-bar; cardul ar fi cost mare pe o pagina low-traffic pe mobil. Risc minim.
- Cont / Integrare = fara tabele (doar formulare + blocuri de cod). Formularele stivate; blocurile
  `<pre>` de cod si tab-bar-urile aveau deja `overflow-x:auto` (contained).

### Ce s-a livrat:
- app/web/templates/base.html: in blocul `@media (max-width:767px)` (marker US-007):
  - `.tabel-card` — card per rand SCOPAT separat de `.tabel-trimiteri` (5.8 intact). thead ascuns,
    tr=card, td=block cu `::before` din `data-eticheta`; celulele fara eticheta (doar actiuni) nu
    afiseaza antet gol; selecturi/inputuri full-width, butoane in card width:100% + min-height:44px.
  - Formulare de continut pe o coloana: inputuri full-width (`width:100% !important` DOAR pe mobil,
    ca sa invinga latimile inline de desktop — desktop neschimbat), butoane >=44px + full-width,
    filtrele de jurnal stivuite (`#filtre-jurnal > div { width:100% }`). Scopat strict pe
    `#card-cont`, `#form-test-cheie`, `#jurnal-section #filtre-jurnal` (nu atinge taburile ARIA,
    butoanele de copiere absolute, tabelul de trimiteri sau modalul).
- app/web/templates/_mapari.html: `tabel-card` pe cele 4 `.tablewrap`; `data-eticheta` pe td-uri;
  override local in `<style>` (`@media 767px` -> select/input `max-width:none; min-width:0`) ca sa
  evite batalia de specificitate/ordine cu regula inline `#mapari-section td select`.
- app/web/templates/_integrare.html: `id="form-test-cheie"` pe formularul de test (ancora de scope).
- tests/test_web_responsive.py: +3 teste (test_tabele_continut_au_clasa_responsive,
  test_formulare_full_width_mobil, test_carduri_trimiteri_5_8_supravietuiesc — regresie 5.8).

### Gates rulate:
- Typecheck/Lint: SKIP (comenzi goale in techStack).
- Tests: PASS — tests/test_web_responsive.py 6; suita completa 838 passed, 1 deselected.
- Browser (E2E requiresBrowserCheck): DEFERAT la VERIFY (loop fara browser).

### Learnings:
- `.tablewrap` (overflow-x:auto, scroll contained) exista DEJA global din 5.5 si era aplicat pe toate
  tabelele de continut (Jurnal/Nomenclator/Mapari/Admin). US-007 a insemnat: (a) CONVERSIA Mapari la
  card (nou), (b) confirmarea scroll-contained pe restul, (c) tratamentul de formular pe mobil.
- Stilurile inline (`style="width:..."`) din templates inv ing stylesheet-ul fara `!important`;
  pe mobil am folosit `!important` scopat strict, evitand regresia pe desktop.
- Scoparea separata `.tabel-card` vs `.tabel-trimiteri` e CRITICA: ambele mecanisme de card coexista
  in acelasi bloc media; test de regresie confirma ca blocul 5.8 (.tabel-trimiteri thead/td::before)
  supravietuieste.

### Next:
- US-008 (responsive Acasa + login/signup), US-005 (poll nu inchide modalul).
- VERIFY: gstack browser la 375px pe /?tab=mapari, ?tab=jurnal, Cont/Nomenclator/Integrare, /admin —
  fiecare fara scroll orizontal de pagina, tabele lizibile, formulare pe o coloana.
---

========================================================================
## US-008 — Responsive Acasa (upload, status, filtre) + login/signup
========================================================================
Status: PASS (loop). E2E browser deferat la VERIFY.

### Ce s-a facut:
- base.html: bloc nou sub `@media (max-width:767px)` (marcat US-008), scopat strict pe
  id-urile sectiunilor de pe Acasa, ca sa NU atinga `.tabel-trimiteri` (5.8), modalul (US-003/004)
  sau paginile de continut (US-007):
  - `#import-section .drop-zone` -> coloana (flex-direction:column, align stretch);
    `#import-section #upload-btn` -> full-width, min-height:44px.
  - `#filtre-trimiteri` -> coloana; `> div` full-width; select/input[text]/input[date]
    `width:100% !important; max-width:none !important` (suprascrie max-width:180px inline pe vehicul);
    buton full-width >=44px.
  - `#status-bar > div` gap redus (randuri deja flex-wrap) -> fara scroll orizontal.
  - `.auth-card` -> max-width:100%; margin:24px auto (centrat, nu depaseste viewport pe mobil).
- login.html / signup.html: cardul primeste clasa `auth-card` (inputurile aveau deja width:100% inline).
- Tabelul de trimiteri (carduri 5.8) NEATINS — doar verificat intact (test US-007 existent).

### Teste (test_web_responsive.py, +2):
- test_acasa_fara_scroll_orizontal_mobil — ancore #import-section/#status-bar/#filtre-trimiteri +
  reguli mobil de stivuire/full-width (seed 1 submission ca sectiunea Trimiteri sa se randeze).
- test_login_signup_full_width_mobil — /login + /signup: .auth-card centrat, width:100%, max-width:100% mobil.

### Gates:
- Tests: PASS — suita completa 840 passed, 1 deselected (era 838; +2 US-008).
- Browser (E2E requiresBrowserCheck): DEFERAT la VERIFY (375px pe /, /login, /signup —
  fara scroll orizontal; upload + filtre pe o coloana; carduri trimiteri intacte).

### Learnings:
- `_coada.html` (filtrele + #submissions-wrap) se include in `_acasa.html` DOAR cand `are_trimiteri`
  e True (zero trimiteri = bara upload acopera CTA-ul). Testul de markup pentru `#filtre-trimiteri`
  trebuie sa semene un submission; regulile CSL traiesc oricum in base.html (mereu prezente).
- login/signup mosteneau deja `width:100%` inline + `margin:40px auto`; lipsea doar plafonarea
  latimii pe mobil — rezolvata centralizat prin clasa `.auth-card` (fara duplicare de stil inline).
---

## US-005 — Poll-ul nu mai inchide modalul si nu mai sterge bifele (R6)

### Implementare (app/web/templates/base.html, +1 bloc <script>):
- Poll-guard nou: listener `htmx:beforeRequest` pe body, scopat la `#submissions-wrap`
  (`d.elt.id !== 'submissions-wrap'` -> iese). Anuleaza (`evt.preventDefault()`) DOAR
  trigger-ul periodic — in htmx `load`/`every 15s` declanseaza request FARA
  `requestConfig.triggeringEvent`; `trimiteriChanged` si submit-ul/filtrul AU
  triggeringEvent -> `if (rc.triggeringEvent) return;` => TREC MEREU.
- Pauza activa cat timp `modalDeschis()` (#modal-detaliu nu e hidden) SAU `existaBifa()`
  (`#submissions-wrap input[name="submission_id"]:checked`).
- Resume: anularea unui beforeRequest NU opreste timer-ul htmx (`ot(e,t,r)` reprogrameaza
  poll-ul indiferent de firing) -> reia automat la urmatorul tic cand ambele conditii dispar.
  Resume explicit/imediat pe checkbox `change` (delegare pe body, prinde bifele post-swap):
  cand modal inchis + nicio bifa -> `htmx.trigger(document.body, 'trimiteriChanged')`
  (pastreaza filtrul via hx-include si trece de guard).
- Vechea pauza pe „rand expandat" (5.8) era deja eliminata de US-003 (detaliul inline ->
  modal global in afara #submissions-wrap); guard-ul o inlocuieste conceptual.

### F5 — evitarea blocajului permanent:
- Pauza e legata STRICT de trigger-ul periodic (lipsa triggeringEvent), nu de o stare sticky.
  Daca randul bifat paraseste filtrul, `trimiteriChanged`/submit-ul de filtru au triggeringEvent
  si nu sunt niciodata anulate -> lista se reincarca, pauza nu ramane lipita.

### Teste (tests/test_web_modal.py, +3):
- test_poll_pauzat_cat_modal_deschis — guard scopat la #submissions-wrap + modalDeschis + preventDefault.
- test_poll_pauzat_cat_exista_bifa — existaBifa (`...submission_id]:checked`) + resume delegat pe `change`.
- test_trimiteriChanged_inca_reincarca_cu_bifa (R6/F5) — `rc.triggeringEvent) return` => trimiteriChanged/filtru trec.
- Structural (modal in afara #submissions-wrap) ramane acoperit de test_modal_container_in_afara_submissions_wrap (US-003).

### Gates:
- Tests: PASS — suita completa 843 passed, 1 deselected (era 840; +3 US-005).
- Browser (E2E requiresBrowserCheck): DEFERAT la VERIFY (bifez 2 trimiteri + astept >15s -> bifele raman;
  deschid modalul + astept >15s -> ramane deschis cu datele intacte).

### Learnings:
- htmx pune pe pauza un poll fara a opri timer-ul daca anulezi `htmx:beforeRequest`: requestul curent
  e abandonat, dar `processPolling` se reprogrameaza singur -> pauza „leneasa" fara cleanup de timer.
- Distinctia sursa periodic vs user/HX-Trigger se face curat prin `requestConfig.triggeringEvent`
  (null pe load/poll, setat pe evenimente user/custom) — fara flag-uri ad-hoc.
---
