5.12 (livrat): editare in modal a randurilor de preview, cont obligatoriu inainte de import, formular editare extras (_form_editare, _editare_preview_modal), plus suita de teste aferenta (preview edit/compact, mapare op, form editare, signup, admin panel). Design + planificare: - docs/design.md: sistem de design (tokeni, breakpoints, scara control, componente, a11y). - docs/prd/prd-5.12-* si prd-5.13-* (5.13 cu raport /autoplan: CEO+Design+Eng, audit trail). Curatare: sterse PNG-urile de test/mockup temporare din radacina. Nota: implementarea CSS 5.13 (responsive compact + sistem butoane) NU e inca facuta — planul revizuit cere refactorul testelor fragile din test_web_responsive.py INAINTE de CSS. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
46 KiB
PRD 5.12 — Editare unificata in modal + cont cu companie/email/CUI obligatorii + rafinari import (calendar data, mapare cu antet+prima inregistrare, un singur Salveaza, preview compact) + responsive tableta/mobil
Stare: inchis (verify-pass 2026-06-26; 8 stories TDD prin agent team, VERIFY context curat PASS + 1 FAIL remediat, /code-review high 3 buguri reparate; regresie 987 passed/1 skipped/0 failed; asteapta confirmare commit — poarta umana)
Proces complet:
docs/ROADMAP.md§5. Contractul RAR (sursa de adevar de contract):docs/api-rar-contract.md. Starea trece:draft → aprobat → in-executie → verify-pass → inchis(actualizata de lead). Acest PRD nu repeta strategia/contractul — le linkeaza.Continua 5.11 (prd-5.11). Backendul de trimitere (worker, masina de stari de trimitere, idempotenta, contract RAR) ramane NEATINS. Atingeri de schema permise (ambele coloane noi, migrare defensiva
_migrate, ca la 3.3b/3.5/3.6):accounts.email(US-001) siimport_rows.reviewed(US-007, marcaj „verificat" per rand de preview). Vezi Non-Goals.
1. Obiectiv
Continuam dogfooding-ul de first-run inceput in 5.11. Sase frictiuni confirmate E2E in browser
(Playwright pe exemple/prezentari_test.csv, 2026-06-26) — toate UI/UX, plus o regula de date pe
conturi:
- Editarea unui rand din preview e rupta vizual si arunca o eroare JS. Modul de editare inline
(
tr.preview-editcudisplay:blockintr-un tabeltable-layout:fixed) colapseaza coloanele — antetul si formularul se randeaza pe verticala, caracter cu caracter (reprodus identic cuimage copy.png). La click pe Anuleaza se arunca in consolaTypeError: Cannot read properties of null (reading 'htmx-internal-data')(reprodus live). Decizie utilizator: editarea trebuie sa fie un MODAL, ca la Trimiteri, refolosind ACELASI formular (fara cod duplicat). - Data prestatiei se scrie doar manual (input text cu hint
YYYY-MM-DD). Trebuie sa se poata alege si din calendar (<input type="date">nativ, decizie utilizator — zero dependinte JS). - Conturile nu au reguli minime de identitate. Confirmat in baza: toate conturile au
cui=NULL, iar conturile create din CLI/teste nu au niciun utilizator → fara email. Decizie utilizator: un cont inregistrat trebuie sa aiba obligatoriu companie, email si CUI. - Maparea coloanelor nu arata datele. Pasul 2 listeaza nume de coloana + 2 exemple stivuite, dar nu se vede clar capul de tabel (numele coloanelor) + valorile primei inregistrari, ca operatorul sa stie ce mapeaza.
- Panoul „Operatii de mapat la cod RAR" cere un Salveaza per rand. La un fisier cu N operatii nemapate sunt N butoane „Salveaza" si N submit-uri. Trebuie un singur buton Salveaza care salveaza toate maparile odata.
- Tabelul de preview (pasul 3) nu e compact si are o coloana neclara. Randurile sunt foarte inalte (VIN-ul se sparge pe verticala), iar coloana „Verificat?" nu are sens evident in acest pas (operatorul nu intelege bifa). Trebuie lista mai compacta si coloana clarificata/eliminata.
- Pe tableta si mobil interfata arata prost si articolele din header se suprapun. Header-ul are grila
desktop
1fr auto 1fr(min-height:92px, logo 60px) si un singur prag mobil@media (max-width:767px), dar nimic pentru tableta (768–1024px) — acolo logo + titlu + badge mediu + comutator tema + versiune- hamburger se inghesuie si se suprapun. Tot fluxul (header, import, preview, modal, Trimiteri, Mapari, Cont) trebuie compact, functional si ergonomic pe tableta si telefon, cu tinte touch si fara suprapuneri.
Toate sunt UI/UX, cu o singura exceptie de date controlata: identitatea contului (companie/email/CUI obligatorii, US-001/002).
2. Non-Goals (anti scope-creep)
- Nu atingem worker-ul, reconcilierea, idempotenta,
build_key, masina de stari de trimitere sau contractul RAR. - Nu schimbam canalul API (
POST /v1/prezentari//valideaza) si nici logica de mapare (mapping.pyresolve_prestatii). Maparea operatie→cod ramane neschimbata; doar UI-ul de mapare din pasul de import se reorganizeaza (US-005) si reuseazasave_mapping/reresolve_accountexistente. - Nu stergem coloanele DB
auto_send(deja neutralizate in 5.11) si nu reintroducem conceptul. - Nu schimbam stocarea editarii de preview: ramane
import_rows.override_json(Approach B din 3.6), rutaPOST /_import/{id}/rand/{i}/editeazaramane sursa de adevar; doar suprafata de editare trece din rand-inline in modal. - Nu facem editare in bloc / multi-rand si nici editare a operatiei/codului RAR din modalul de rand (codul se mapeaza din panoul „Operatii de mapat", ca azi).
- Nu schimbam fluxul de login/parola; un cont poate avea in continuare mai multe loginuri (
users), dar primeste un email canonic de contact peaccounts(US-001). - Nu rescriem validarea de continut (
validation.py);<input type="date">produce totYYYY-MM-DD, acceptat azi.
3. Stories atomice
Fiecare story: cea mai mica unitate care lasa sistemul functional. Backend + UI pentru acelasi comportament = 2 stories. Toate rutele web noi sub
require_login, scoped pe contul din sesiune (404 cross-account), CSRF pe toate POST-urile.Cerinta transversala (toate story-urile cu UI): responsive obligatoriu. Fiecare suprafata noua/atinsa (US-002..007) se verifica E2E pe 3 viewport-uri: desktop (≥1280px), tableta (768–1024px) si mobil (≤767px / ~390px) — fara overflow orizontal (
scrollWidth <= clientWidth), fara suprapuneri, tinte touch ≥44px, modal full-screen pe mobil. US-008 acopera header-ul + cadrul global; fiecare story isi verifica propria suprafata pe cele 3 viewport-uri.
US-001: Backend — companie/email/CUI obligatorii pe cont (accounts.email + validari)
Ca administrator al gateway-ului vreau ca orice cont sa aiba companie, email si CUI pentru ca azi conturile pot exista fara email (CLI/teste) si fara CUI, deci nu pot fi identificate fiscal/contactate.
- Depinde de: —
- Fisiere:
app/schema.sql(coloanaaccounts.email+_migratedefensiv),app/accounts.py(create_accountaccepta+valideazaemail; helperaccount_is_complete),app/web/auth_routes.py(signup: CUI devine obligatoriu; scrieaccounts.email),tools/account.py(CLI create cere--email+--cui),tests/test_accounts.py,tests/test_signup.py(~6 fisiere) - Test intai (RED):
tests/test_accounts.py—test_create_account_fara_email_ridica,test_create_account_fara_cui_ridica,test_email_normalizat_lowercase_trim,test_migrare_adauga_coloana_email_idempotent,test_account_is_complete_false_pe_legacy_incomplet;tests/test_signup.py—test_signup_fara_cui_422,test_signup_scrie_email_pe_account,test_signup_cui_existent_mesaj_prietenos(NU mesajul tehnic cuactivate --account). - Acceptance criteria:
- Migrare:
accounts.email TEXT(nullable la nivel de schema pentru conturile legacy),_migratedefensiv idempotent (causers.is_adminla 3.3b). Contul de sistem id=1 ramane fara email (exceptat). create_account(conn, name, cui, email, active)—name/cui/emailgoale →ValueErrorcu cauza+fix (catalogerrors.pydaca exista cod potrivit);emailnormalizat (trim+lower);cuinormalizat (trim+upper, ca azi). CUI duplicat → mesajul existent.- Signup web:
cuidevine obligatoriu (azi optional); la succes scrieaccounts.email = email-ul utilizatorului. Lipsa CUI → re-randare formular cu eroare (422), pastrand campurile. - CUI duplicat la signup = mesaj prietenos, NU cel tehnic (decizie user 2026-06-26, optiunea 1):
„Aceasta firma (CUI …) e deja inregistrata. Cere accesul de la administratorul contului." — fara
referinta la CLI
activate --account. Model: 1 firma = 1 cont = 1 login; fluxul de invitatie/alaturare a unui al doilea email pe aceeasi firma e deferit la TODOS (optiunea 2). - Canal de contact concret in mesaj (T3 gate /autoplan, aprobat 2026-06-26): mesajul include un
email/canal de suport configurabil din settings (ex.
support_email); daca setarea lipseste, fallback la formularea de mai sus. Operatorul primeste un pas urmator real, nu doar „cere accesul". Nu mai lasam mesajul tehnic ridicat decreate_accountsa ajunga verbatim in signup — detectam CUI duplicat in handler-ul de signup si compunem mesajul prietenos acolo (NUerror=str(exc)). - CLI
tools/account.py createcere--email+--cui(refuza fara ele);--with-keyneschimbat. account_is_complete(row)(companie + email + CUI ne-goale) — helper pur, fara efecte.- NU atinge
users,submissions, worker-ul sau idempotenta.
- Migrare:
- Verificare E2E: TestClient — signup fara CUI → 422; signup complet →
accounts.emailpopulat;create_accountfara email/cui → ValueError.
US-002: UI — gate de activare + pagina Cont editeaza companie/email/CUI + banner legacy
Ca operator/administrator vreau sa vad si sa completez companie/email/CUI pentru ca conturile incomplete (legacy) trebuie aduse la regula fara re-inregistrare.
- Depinde de: US-001
- Fisiere:
app/web/templates/_cont.html(sectiune noua „Date firma"),app/web/routes.py(rutaPOST /cont/date-firmascoped sesiune + CSRF; contextaccount_meta+cont_incomplet),app/web/templates/admin.html+app/web/routes.py(gate activare peaccount_is_complete),app/web/templates/_banner.htmlsau_acasa.html(banner „Completeaza datele firmei"),tests/test_web_cont.py,tests/test_admin.py(~6 fisiere) - Test intai (RED):
tests/test_web_cont.py—test_cont_afiseaza_companie_email_cui,test_post_date_firma_actualizeaza,test_post_date_firma_cui_duplicat_eroare,test_banner_cont_incomplet_pe_legacy;tests/test_admin.py—test_activare_cont_incomplet_refuzata. - Acceptance criteria:
_cont.htmlare o sectiune „Date firma" (deasupra cheii API) cu companie + email + CUI editabile, prefilled dinaccounts;POST /cont/date-firmavalideaza (reusecreate_account-style) + CSRF + scoped sesiune; eroare pe CUI duplicat / camp gol, mesaj 3-niveluri.- Banner ne-blocant „Completeaza datele firmei (email/CUI)" pe Acasa cand
account_is_completee fals; dispare dupa completare. NU blocheaza importul/uploadul. - In panoul admin, butonul Activeaza e dezactivat (cu tooltip) pe conturi incomplete — nu activam la RAR un cont fara identitate completa.
- Fara regresie pe rutele existente din
_cont.html(cheie API, creds RAR).
- Verificare E2E: browser pe
/?tab=cont— completez email+CUI → banner dispare; admin nu poate activa un cont incomplet.
US-003: UI — pasul „Potriveste coloanele" arata antet + prima inregistrare
Ca operator vreau sa vad numele coloanelor din fisier si valorile primului rand pentru ca sa stiu exact ce date mapez la fiecare camp RAR.
- Depinde de: —
- Fisiere:
app/web/templates/_mapcoloane.html,app/web/routes.py(web_upload_import/web_save_mapare_coloanepaseaza dejasample_rows; expuneprima_inregistrare),tests/test_web_mapcoloane.py(~3 fisiere) - Test intai (RED):
tests/test_web_mapcoloane.py—test_mapcoloane_arata_cap_tabel_coloane,test_mapcoloane_arata_valori_prima_inregistrare,test_mapcoloane_fara_randuri_degradeaza(fisier cu antet, fara randuri de date → fara crash). - Acceptance criteria:
- Deasupra (sau langa) randurile de mapare, un mic tabel orizontal cu un cap de tabel = numele
coloanelor din fisier si un rand = valorile primei inregistrari (truncate la o lungime
rezonabila,
titlepe valoare integrala). Foloseste.tablewrappentru scroll orizontal pe mobil. - Fiecare coloana din capul de tabel ramane vizual asociata cu select-ul ei de mapare (ex. aceeasi ordine, sau evidentiere la hover) — operatorul vede „coloana X (valoare „...") → campul canonic Y".
- Fisier fara randuri de date → se arata doar capul de tabel, fara „prima inregistrare" (fara crash).
- Nicio schimbare de backend de parsare/mapare; doar randare (datele exista deja in
sample_rows).
- Deasupra (sau langa) randurile de mapare, un mic tabel orizontal cu un cap de tabel = numele
coloanelor din fisier si un rand = valorile primei inregistrari (truncate la o lungime
rezonabila,
- Verificare E2E: browser pasul 2 — upload
prezentari_test.csv→ vad antetul real + valorile randului 1.
US-004: UI+backend — un singur „Salveaza" pe „Operatii de mapat la cod RAR"
Ca operator vreau sa salvez toate maparile de operatii dintr-un singur click pentru ca azi e cate un buton per operatie si trebuie apasat pe fiecare.
- Depinde de: —
- Fisiere:
app/web/templates/_preview_import.html(panoul de mapare → un singur<form>),app/web/routes.py(ruta nouaPOST /_import/{id}/mapare-operatiiplural; pastreazamapare-operatiesingular pentru compat sau o inlocuieste — vezi AC),tests/test_web_mapare_op.py(~3 fisiere) - Test intai (RED):
tests/test_web_mapare_op.py—test_mapare_operatii_salveaza_multiple_intr_un_post,test_mapare_operatii_ignora_randuri_neselectate(op fara cod ales → nesalvata, nu eroare),test_mapare_operatii_re_rezolva_blocatele(randurile cu cod ales trec dinneeds_mapping). - Acceptance criteria:
- Panoul „Operatii de mapat la cod RAR" devine UN singur
<form>cu un select per operatie + un singur buton „Salveaza maparile" la final. POST /_import/{id}/mapare-operatiiprimeste perechi(cod_op_service, cod_prestatie)(liste paralele), apeleazasave_mappingpentru fiecare operatie cu cod ales (reuse exact, fara logica noua de mapare), apoi o singura recompute_web_compute_preview+ re-randare#import-section.- Operatiile fara cod ales (
— alege cod RAR —) sunt ignorate (nu produc eroare, nu se salveaza). - Toggle-ul auto_send NU reapare (eliminat in 5.11).
- CSRF + scoped sesiune + guard batch committed (409) pastrate.
- Panoul „Operatii de mapat la cod RAR" devine UN singur
- Verificare E2E: browser pasul 3 — aleg coduri pentru toate operatiile, un click pe „Salveaza maparile" → toate randurile trec din „Cod RAR lipsa", o singura re-randare.
US-005: Refactor — formular de editare partajat (DRY) intre Trimiteri si preview
Ca dezvoltator vreau un singur formular de editare de continut pentru ca sa nu existe cod duplicat intre modalul Trimiteri si editarea de preview (sursa bug-urilor inline din 3.6/5.11).
- Depinde de: —
- Fisiere:
app/web/templates/_form_editare.html(NOU — partial cu campurile vehicul/data/odo),app/web/templates/_trimitere_detaliu.html(consuma partial-ul),app/web/templates/_macros.html(macrocampextins cutip='date'),tests/test_web_form_editare.py(~4 fisiere) - Test intai (RED):
tests/test_web_form_editare.py—test_form_editare_are_input_date_pe_data_prestatie,test_trimitere_detaliu_foloseste_form_partajat,test_camp_macro_randeaza_type_date. - Acceptance criteria:
- Partial
_form_editare.htmlrandeaza grila responsiva existenta (repeat(auto-fit, minmax(200px,1fr))) cu campurile:nr_inmatriculare,vin,data_prestatie,odometru_final,odometru_initial, plus map de erori per-camp (tiparcorectie_errors). Parametrizat prin: URL de POST, valorile curente, harta de erori, eticheta butonului primar. data_prestatie=<input type="date">(calendar nativ); valoarea ramaneYYYY-MM-DD. Daca valoarea curenta nu eYYYY-MM-DDvalid, inputul degradeaza grijuliu (gol + hint), fara crash._trimitere_detaliu.htmlrandeaza acelasi partial in ramuraeditabil— comportamentul modalului Trimiteri (post/corecteaza, select cod RAR pe needs_data/needs_mapping) ramane identic.- Macro
campsuportatip='date'fara sa strice apeluriletype='text'existente.
- Partial
- Verificare E2E: browser — modalul Trimiteri (rand
needs_data) arata un calendar la Data prestatie; salvarea+revalidarea functioneaza ca azi.
US-006: UI — „Editeaza" din preview deschide MODALUL (acelasi formular), nu rand inline
Ca operator vreau sa editez un rand de preview intr-un modal curat pentru ca editarea inline e rupta vizual si arunca eroare la Anuleaza.
- Depinde de: US-005
- Fisiere:
app/web/templates/_preview_rand.html(scoate ramuraediting/tr.preview-edit+ scriptul de mutual-exclusion; butonul „Editeaza" tinteste modalul global),app/web/routes.py(ruta GET fragment editare preview → randeaza_form_editare.htmlin#detaliu-modal-body; POST/_import/{id}/rand/{i}/editeazaramane, dar raspunde cu inchidere modal + OOB pe rand+contoare),app/web/templates/_preview_import.html(foloseste modalul global#detaliu-modal),tests/test_web_preview_edit.py(~5 fisiere) - Test intai (RED):
tests/test_web_preview_edit.py—test_editeaza_preview_serveste_fragment_modal(NUtr.preview-edit),test_salvare_preview_inchide_modal_si_oob_rand,test_anuleaza_nu_lasa_rand_orfan(regresie pe eroarea htmx null),test_editare_preview_scoped_404_alt_cont,test_editare_batch_committed_409. - Acceptance criteria:
- Butonul „Editeaza" pe rand face
hx-getcatre fragmentul de editare cuhx-target="#detaliu-modal-body"(acelasi mecanism de modal ca la Trimiteri, deschis prin clasa/markup existent inbase.html). - Fragmentul randeaza
_form_editare.htmlcu POST la/_import/{id}/rand/{i}/editeaza,hx-target="#detaliu-modal-body". La succes: modalul se inchide (HX-Trigger: inchideModal, ca la/corecteaza) si randul + contoarele se actualizeaza prin OOB swap (reuseinclude_oob). - Ramura
editing/tr.preview-edit+ scriptul inline de mutual-exclusion sunt ELIMINATE din_preview_rand.html(sursa colapsarii pe verticala + a eroriihtmx-internal-datala Anuleaza). - „Anuleaza" = inchiderea modalului (mecanismul global), fara cerere catre
/_import/.../rand/{i}, deci fara eroarea JS reprodusa. Test de regresie pe consola curata. - Mutatie pura pe
override_jsonpastrata (ruta neschimbata logic); scoping JOIN→404, guard committed→409 raman. - Pe eroare de validare, modalul ramane deschis cu valorile + erorile per-camp (tipar Trimiteri).
- Butonul „Editeaza" pe rand face
- Verificare E2E: browser pasul 3 — Editeaza → modal cu calendar + campuri; completez data → Salveaza → modal se inchide, randul trece pe „Gata de trimis", contoarele cresc; Anuleaza → modal se inchide, 0 erori in consola.
US-007: UI — preview compact + scoaterea coloanei „Verificat?"
Ca operator vreau o lista de preview compacta si fara coloane neclare pentru ca randurile sunt prea inalte (VIN pe verticala) si nu inteleg bifa „Verificat?".
- Depinde de: US-006
- Fisiere:
app/schema.sql(coloanaimport_rows.reviewed+_migratedefensiv),app/web/templates/_preview_rand.html,app/web/templates/_preview_import.html(scoate coloanacol-verificat+ logica inlinereviewed_rowsdin tabel),app/web/templates/_form_editare.html/ fragmentul modal (buton „Confirma valorile" peneeds_review),app/web/templates/base.html(latimicol-*recalibrate, anti-overflow),app/api/v1/import_router.py+app/web/routes.py(citescreviewedin_resolve_row_for_preview/_web_compute_previewcaneeds_review-confirmat →ok; gaten_confirmatla commit folosestereviewed, nu bife inline; ruta care seteazareviewed=1),tests/test_web_preview_compact.py,tests/test_import_review.py(~7 fisiere) - Test intai (RED):
tests/test_web_preview_compact.py—test_preview_fara_coloana_verificat,test_preview_vin_nu_se_sparge_pe_verticala(VIN intr-o singura linie / wrap controlat);tests/test_import_review.py—test_needs_review_exclus_din_gata_pana_la_confirmare,test_confirmare_in_modal_seteaza_reviewed_si_devine_ok,test_reviewed_nu_intra_in_payload_sau_idempotency(marcaj separat, NU camp de continut),test_migrare_adauga_coloana_reviewed_idempotent,test_editare_valoare_pe_needs_review_reseteaza_reviewed(daca schimbi valoarea, re-cere confirmare). - Acceptance criteria:
- Coloana „Verificat?" eliminata din tabelul de preview; antetul si celulele scad la 8 coloane.
- Randuri compacte: VIN nu se mai sparge pe verticala (latime minima pe coloana Vehicul /
white-spacecontrolat); fara overflow orizontal la 1280px (scrollWidth <= clientWidth); cardurile <768px raman. - Decizie inchisa (Q1): confirmare in modal, rand exclus pana confirmi. Un rand
needs_review: - apare cu pill „Verifica valori" + motivul concret in „Note" (data ambigua / formule Excel / coercion); - este exclus din „gata de trimis" (nu intra inn_confirmat) pana cand operatorul il deschide in modal (US-006) si apasa „Confirma valorile" (sau il corecteaza), ceea ce seteazaimport_rows.reviewed=1; abia atunci randul devineokla recalculul_resolve_row_for_preview. - Banner discoverability deasupra tabelului (T1 gate /autoplan, aprobat 2026-06-26): cand exista randuri
needs_review, un banner ne-blocant deasupra tabelului explica: „Randurile cu Verifica valori nu pleaca la RAR pana le deschizi si confirmi in modal." Fara el, gate-ul mutat din coloana vizibila in modal devine usor de ratat (operatorul crede ca pill-ul e informativ). Bannerul dispare candsummary.needs_review == 0. - Buton explicit „Confirma valorile" (T2 gate /autoplan, aprobat 2026-06-26): in modal (US-006), randurile
needs_reviewau un buton SEPARAT „Confirma valorile" care seteazareviewed=1— atestare explicita, distincta de salvarea unei corectii de continut. NU se seteazareviewed=1implicit la orice save (altfel operatorul ar atesta o valoare ambigua fara intentie). Salvarea unei CORECTII pe un rand deja confirmat reseteazareviewed(vezi AC urmator). - Marcaj separat, nu camp de continut:
import_rows.reviewed(nullable/int, migrare defensiva) NU intra in payload, inoverride_jsonsau in cheia de idempotenta. Daca utilizatorul schimba o valoare a unui rand deja confirmat,reviewedse reseteaza (re-cere confirmare). - Comitul ramane gate HARD pe
n_confirmat(niciun rand ambiguu nu pleaca la RAR fara confirmare umana explicita) — acum derivat dinreviewed, nu din bife inlinereviewed_rows. - Bara de confirmare („Trimite la RAR") si contoarele raman corecte dupa editari/confirmari (OOB), fara coloana Verificat?.
- Guard committed→409 si scoping JOIN→404 pe ruta de confirmare (acelasi tipar ca
/editeaza).
- Verificare E2E: browser pasul 3 (cu un xlsx cu data ambigua / VIN numeric) — lista compacta, fara
coloana Verificat?, VIN pe o linie; randul
needs_reviewramane exclus din „gata de trimis" pana il confirm in modal („Confirma valorile") → devine „Gata de trimis", contorul creste.
US-008: UI — responsive tableta + mobil (header fara suprapuneri + cadru compact/ergonomic)
Ca operator pe telefon/tableta vreau o interfata compacta, fara articole de header suprapuse pentru ca azi pe mobil arata prost si elementele din header se calca unele pe altele.
- Depinde de: — (header/cadru global); coordonat cu US-006/007 pentru modal+preview pe mobil
- Fisiere:
app/web/templates/base.html(header grid + media queries tableta 768–1024 + mobil ≤767, modal full-screen,.cont-menu, tinte touch), eventualapp/web/templates/_status.html/_acasa.html(contoare + nav pe randuri inguste),tests/test_web_responsive.py(~3 fisiere) - Test intai (RED):
tests/test_web_responsive.py—test_header_are_breakpoint_tableta(exista reguli@mediaintre 768 si 1024 pentru header),test_header_elemente_nu_au_min_height_fix_pe_mobil,test_modal_full_screen_pe_mobil(clasa/regula prezenta). (Testele de markup/CSS; pixel-level la E2E.) - Acceptance criteria:
- Header fara suprapuneri pe tableta (768–1024px): logo + titlu + badge mediu + comutator tema +
versiune + hamburger se aseaza fara sa se calce (grid/flex care wrap-uieste sau ascunde versiunea/
titlul lung);
min-height:92pxnu forteaza inghesuirea. Pe mobil (≤767px) raman regulile existente, verificate ca nu se suprapun la ~390px latime. - Compact + ergonomic: spatieri reduse pe mobil, tinte interactive ≥44px (butoane, pill-uri, linkuri nav, intrari hamburger), fara dublu-scroll; modalul de editare (US-006) e full-screen pe mobil (nu o casuta minuscula).
- Fara overflow orizontal pe niciuna din paginile principale (Acasa/import, preview pas 3, Mapari,
Cont, login/signup) la 768px si la ~390px (
scrollWidth <= clientWidth). - Contoarele de status + nav-ul „Trimiteri/Mapari" se aseaza pe randuri lizibile pe mobil (fara taiere).
- Light/Dark/Petrol/Auto raman corecte pe toate viewport-urile (fara regresie de tema).
- Header fara suprapuneri pe tableta (768–1024px): logo + titlu + badge mediu + comutator tema +
versiune + hamburger se aseaza fara sa se calce (grid/flex care wrap-uieste sau ascunde versiunea/
titlul lung);
- Verificare E2E: browser Playwright cu
browser_resizela 390×844 (mobil), 820×1180 (tableta) si 1280×800 (desktop) — screenshot pe Acasa/import, preview pas 3 (cu modal deschis) si Cont; header fara suprapuneri pe toate trei; 0 overflow orizontal; tinte touch ok.
4. Riscuri
- R1 — Gate
needs_reviewla scoaterea coloanei. Coloana „Verificat?" era gate-ul HARD prin care randurile cu valori ambigue (data ambigua, formula Excel) intrau in trimitere doar dupa bifa umana. Scoaterea ei naiva ar auto-include randuri ambigue (declaratie ireversibila la RAR). Mitigare: confirmarea se muta in modalul de editare (US-007 AC);n_confirmatramane gate HARD. Vezi Q1. - R2 — Refactor formular partajat (US-005) atinge modalul Trimiteri (cale LIVE).
_trimitere_detaliu.htmle folosit pentru corectii reale care re-trimit la RAR. Mitigare: US-005 = refactor fara schimbare de comportament; teste byte-compat pe post/corecteaza+ regresia existenta verde inainte de US-006/007. - R3 —
<input type="date">si valori ne-YYYY-MM-DD. Fisiere cu data in alt format ajung in editare ca text ne-valid pentru inputul date (s-ar goli). Mitigare: AC US-005 — degradare grijulie (gol + hint), fara pierdere tacuta; data ramane editabila si re-validata la salvare. - R4 — Migrare
accounts.email. Conturi legacy raman cuemail=NULL. Mitigare: coloana nullable +account_is_complete(banner + gate activare), nu hard-block; contul de sistem id=1 exceptat. - R5 — Eroarea htmx
htmx-internal-data. Reprodusa la Anuleaza pe editarea inline. Mitigare: US-006 elimina complet ramura inline + scriptul; test de regresie pe consola curata. - R6 — Responsive = fisier fierbinte
base.html. US-008 atinge header + media queries, fisier partajat cu alte story-uri (US-007 latimicol-*). Mitigare: serializare la lead (NU paralel pebase.html); verificare pixel pe 3 viewport-uri ca breakpoint-ul de tableta nu strica desktop-ul/mobilul existent.
5. Intrebari deschise
Se rezolva cu utilizatorul ÎNAINTE de executie (poarta de aprobare PRD).
- Q1 (gate
needs_review) — INCHIS (user, 2026-06-26): confirmare in modal, rand exclus pana confirmi. Context —needs_reviewapare cand validarea TRECE dar parsarea fisierului a fost incerta, in 3 cazuri (sursa:import_parse.py+import_router.py:201-230), aproape exclusiv la xlsx (la CSV nu se declanseaza — de-aceea coloana e goala in cazul comun):- Data ambigua — zi ≤12 si format neclar (
05.06= 5 iun. sau 6 mai?). - Coloana cu formule Excel fara valori calculate (rata mare de celule goale).
- Coercion suspect la citire xlsx — VIN numeric (pierde zerourile din fata) / odometru ca float.
Decizie: scoatem coloana mereu-prezenta „Verificat?"; randul
needs_reviewramane exclus din „gata de trimis" pana e deschis in modal si confirmat („Confirma valorile") sau corectat, persistandimport_rows.reviewed=1(marcaj separat, NU camp de continut → nu intra in payload/idempotenta). Implementat in US-007.
- Data ambigua — zi ≤12 si format neclar (
- Q2 (model cont-email) — INCHIS (user, 2026-06-26): model A (email canonic pe
accounts), cu 1 firma = 1 cont = 1 login. CUI ramane unic; al doilea email pe acelasi CUI e respins la signup cu mesaj prietenos (US-001). Fluxul de invitatie/alaturare (mai multi utilizatori per firma) → TODOS. - Q3 (CLI legacy
tools/account.py): facem--email/--cuiobligatorii rupe scripturile vechi de test? Daca da, pastram un flag--allow-incompletedoar pentru teste, sau actualizam fixture-urile.
6. Valuri de executie (graful de dependente)
Val 1 (paralel, fisiere disjuncte):
[US-001] accounts.email + validari companie/email/CUI (schema/accounts/auth_routes/cli)
[US-003] mapcoloane: antet + prima inregistrare (_mapcoloane.html/routes)
[US-004] un singur Salveaza pe operatii (_preview_import.html/routes)
[US-005] formular de editare partajat (DRY) + input date (_form_editare/_trimitere_detaliu/_macros)
Val 2 (deblocate de Val 1):
[US-002] Cont editeaza date firma + gate activare + banner (dep US-001; _cont/admin/routes)
[US-006] Editeaza preview → modal (acelasi formular) (dep US-005; _preview_rand/_preview_import/routes)
Val 3 (deblocat de US-006; ating base.html → serializate):
[US-007] preview compact + scoate „Verificat?" + gate review (dep US-006; _preview_*/base.css/import_router)
[US-008] responsive tableta+mobil + header fara suprapuneri (base.html media queries; coordonat cu US-006/007)
Fisiere fierbinti partajate (serializate de lead, NU paralel pe acelasi fisier): routes.py
(US-001/002/003/004/006), _preview_import.html (US-004/006/007), _preview_rand.html (US-006/007),
base.html (US-007 latimi col-* + US-008 header/media queries — serializate strict intre ele). Vezi ROADMAP §5.5.
Raport VERIFY
Faza VERIFY rulata de subagent verificator independent (context curat, PRD-only, ROADMAP §5.6), 2026-06-26. Lead orchestrare prin agent team (8 teammates Sonnet TDD pe valuri cu fisiere disjuncte;
routes.pysibase.htmlserializate ca fisiere fierbinti). Backend trimitere (worker, masina de stari de trimitere, idempotentabuild_key, contract RAR, canal API) NEATINS — confirmatgit diff --stat(app/worker/, app/idempotency.py, app/mapping.py, app/validation.py = 0 modificari).
Rezultat: PASS (toate 8 stories)
- Suita:
python3 -m pytest -q-> 987 passed, 1 skipped, 0 failed (baseline 934 -> +53 teste noi). Live RARFINALIZATA= opt-in indisponibil in mediu (normal, ca la livrabilele anterioare). - PASS/FAIL per story (dovezi cod + teste, verificator independent):
- US-001 accounts email/CUI — PASS (migrare defensiva, create_account valideaza, account_is_complete id=1 exceptat, signup CUI obligatoriu + mesaj prietenos T3, CLI --email/--cui).
- US-002 Cont date firma + gate activare + banner — PASS.
- US-003 mapcoloane antet + prima inregistrare — PASS (confirmat E2E browser).
- US-004 un singur Salveaza pe operatii — PASS (ruta plurala, D#12 skip invalid).
- US-005 formular editare partajat + input date — PASS (D#5/D#6/D#10).
- US-006 Editeaza preview -> MODAL — PASS (ramura inline eliminata, Anuleaza fara eroare htmx, E2E 0 erori consola).
- US-007 preview compact + gate review in modal — PASS (reviewed marcaj separat, NU in payload/idempotenta; gate HARD pe ambele canale; T1 banner; T2 buton Confirma; D#9 reset).
- US-008 responsive tableta + mobil — PASS (E2E pe 390/820/1280, header fara suprapuneri, D#13 verificat).
- Invariante critice: R2 (submissions neatins dupa editare preview) PASS; reviewed in afara
payload/override/idempotency PASS; migrari idempotente PASS; ramura inline
tr.preview-editeliminata PASS.
VERIFY a gasit 1 FAIL -> remediat TDD, re-confirmat
- FAIL:
signup.htmleticheta CUI „(optional)" + input fararequired(contrazicea AC US-001 „CUI obligatoriu"; serverul respingea corect 422 dar UI comunica gresit). Reparat TDD (eticheta*+required), test de locktest_signup_html_cui_obligatoriu_ui.
Faza CLOSE — /code-review high (8 unghiuri prin subagenti, verificare cod first-hand)
3 buguri reale reparate TDD (regresie finala 987 passed):
- HIGH —
confirma-reviewfoloseahx-swap="none"-> scriptulupdateN()din continutul principal nu se executa ->n_confirmatramanea stale -> „Trimite la RAR" pica pe gate HARD 422 (fluxul confirma->commit US-007 rupt la prima incercare). Fix: formularul Confirma valorile aliniat lahx-target="#detaliu-modal-body"hx-swap="innerHTML"(ca /editeaza). - MEDIUM — email duplicat la signup arata mesajul gresit „firma e deja inregistrata" (
"deja folosit"prindea siValueError("email deja folosit")dincreate_user). Fix: detectie email-dup inaintea CUI-dup, mesaj specific emailului. - MEDIUM (a11y) — butonul Editeaza din preview deschidea modalul ocolind
open()(fara inert/focus-trap/ focus-return). Fix: handler-ul globalhtmx:beforeRequesttrateaza si.btn-editeaza->open(); JS inline eliminat. Notat ca debt (neblocant): API preview re-deriva needs_review peste DBresolved_statuscross-channel (web commit numara oricumreviewed=1); mesaje prietenoase „camp gol" dead-code in cont_date_firma/signup (edge mascat de HTML required);zip()truncheaza la liste POST inegale;idcont in mesajul CUI-duplicat; duplicari de cleanup (context modal, markup banner, N query nomenclator).
Nedovedit in sesiune
- Live RAR
FINALIZATAprin--send(opt-in, lipsa creds/mediu) — risc minim, backend trimitere NEATINS.
GSTACK REVIEW REPORT (/autoplan, 2026-06-26)
Branch: main · Commit: 283299f · Voci: Claude subagents (CEO/Design/Eng/DX) + verificare cod first-hand.
Codex = INDISPONIBIL (usage limit, reset 2026-07-18) -> mod [subagent-only] pe toate fazele.
Restore point: vezi comentariul HTML din capul fisierului. Test plan: ~/.gstack/projects/romfast-rar-autopass/main-prd5.12-test-plan-20260626.md.
Rezumat
PRD matur: Q1/Q2 inchise de user, Non-Goals clare, graf de valuri, R1-R6. Rutele si fisierele citate exista toate in cod. Review-ul a confirmat fezabilitatea si a gasit 4 lacune de specificatie reale (nu blocante, dar de inchis inainte de executie) + cateva rafinari. Niciun User Challenge (un singur model activ -> nu se poate forma consens cross-model; recomandarile de mai jos sunt sugestii, nu provocari).
Decision Audit Trail
| # | Faza | Decizie | Clasificare | Principiu | Rationament | Respins |
|---|---|---|---|---|---|---|
| 1 | CEO | NU splitam in 5.12a/5.12b | Taste | P3/P6 | Valurile izoleaza deja US-001/002 (Val1/Val2) pe fisiere disjuncte; split adauga overhead de release fara castig tehnic pt. echipa mica | Split in 2 release-uri (CEO subagent) |
| 2 | CEO | Respins „testeaza worker-ul intai" (F1/F3/F8) | Mechanical | P3 | Non-Goals ingheata explicit worker/contract/idempotenta; conflateaza acest PRD UI cu munca de backend separata | CEO subagent F1/F3/F8 |
| 3 | CEO | First-run E2E smoke -> ramane in TODOS (deja listat) | Mechanical | P3 | Deja deferat din 5.11; recomandat, neblocant | A bloca 5.12 pe el |
| 4 | CEO | needs_review: pastram gate-ul, nu cerem date de utilizare | Mechanical | P1 | Gate-ul e safety-critical (declaratie ireversibila RAR); US-007 muta UI-ul, nu sterge gate-ul | CEO F5 (gather usage data first) |
| 5 | Eng | Partial partajat = DOAR campuri vehicul+data+err/fix; cod_prestatie select + nemapate_inline RAMAN in _trimitere_detaliu |
Mechanical | P5/P4 | _trimitere_detaliu are 2 surse de cod (select + sectiune mapare inline) imposibil de absorbit fara branching fragil |
Partial „atotcuprinzator" |
| 6 | Eng | US-005 parametrizeaza si fix_map (+ aria-label cu VIN) |
Mechanical | P1 | Forma preview are fix-hints + aria-label cu context VIN; lista PRD le omitea -> ar pierde info la extractie | A lasa lista PRD ca atare |
| 7 | Eng | import_rows.reviewed INTEGER DEFAULT 0 (nu NULL) |
Mechanical | P5 | Gate-ul devine reviewed=0 clar, fara ambiguitate NULL vs 0 |
DEFAULT NULL |
| 8 | Eng | Gate commit derivat din DB reviewed pe AMBELE canale; API reviewed_rows pastrat dar seteaza reviewed=1 (contract stabil) |
Mechanical | P1/P5 | Evita divergenta web/API si pastreaza contractul /v1/import/.../commit |
A schimba doar web-ul (divergenta tacuta) |
| 9 | Eng | reset reviewed la schimbare valoare se implementeaza in calea editeaza/override |
Mechanical | P1 | E un AC US-007 fara loc de implementare numit; apply_row_override e locul |
A-l lasa nespecificat |
| 10 | Design | <input type=date> ne-ISO: gol + hint + valoare bruta in hidden, fara pierdere |
Taste->auto | P1 | Previne pierderea tacuta de date pe formate Excel; backend deja marcheaza needs_review | A goli pur si simplu inputul |
| 11 | Design | US-003 fisier fara randuri: mesaj explicit „antet fara randuri de date" + blocheaza Continua | Mechanical | P1 | Edge case altfel = esec tacut | Doar „fara crash" |
| 12 | US-004 | Bulk mapping: validare per-item, skip invalid + sumar, restul salvate, 1 re-render | Mechanical | P1 | PRD acopera „fara cod = ignorat" dar nu „cod invalid pe 1 din N" | All-or-nothing |
| 13 | US-008 | Modal full-screen mobil: VERIFICA, nu re-adauga (exista base.html:407) | Mechanical | P4 | Regula deja prezenta la @media max-width:767px |
A re-implementa |
| 14 | DX | Q3: actualizam fixture-urile via factory in conftest.py, FARA --allow-incomplete in prod |
Taste | P5/P4 | Escape-hatch lasa o veruca in codul de productie; factory centralizat e curat si mai bun pe termen lung | --allow-incomplete flag |
| T1 | Design | needs_review: banner persistent deasupra tabelului | Taste -> APROBAT user 2026-06-26 | P1/P5 | Gate-ul mutat in modal devine usor de ratat; bannerul il face explicit | Doar pill+tooltip |
| T2 | Design/Eng | „Confirma valorile" = buton explicit separat (nu implicit pe save) | Taste -> APROBAT user 2026-06-26 | P5 | Atestare explicita pe valori ambigue; evita confirmarea accidentala | Implicit pe orice save |
| T3 | DX | Mesaj CUI duplicat include canal de contact configurabil (fallback la actual) | Taste -> APROBAT user 2026-06-26 | P1 | Operatorul primeste un pas urmator real; detectie in handler signup, nu str(exc) |
Pastreaza mesajul ca in PRD |
NOT in scope (confirmat)
- Worker, reconciliere, idempotenta,
build_key, masina de stari de trimitere, contract RAR (Non-Goals). - Canal API
POST /v1/prezentari//valideazasimapping.resolve_prestatii— neschimbate. - Multi-utilizatori per firma (flux invitatie/alaturare) -> TODOS.
- First-run E2E smoke ca poarta de release -> TODOS (deja deferat din 5.11).
- Split 5.12a/5.12b -> respins (vezi D#1).
What already exists (de refolosit, nu reconstruit)
- Modal global
#detaliu-modal+inchideModal(HX-Trigger-After-Settle, routes.py:1235/1394) — US-006 il refoloseste. include_oobpentru OOB swap rand+contoare — US-006/007 il refolosesc.save_mapping/reresolve_account— US-004 le refoloseste (fara logica noua de mapare).- Macro
campexista INLINE in ambele forme (_preview_rand.html:51,_trimitere_detaliu.html:98) — US-005 il EXTRAGE (nu „extinde in_macros.html" cum spune lista de fisiere;_macros.htmlare azi doarautosend_togglegol). - Modal full-screen mobil + tinte touch 44px — deja in base.html (
@media max-width:767px, liniile 407-427). US-008 = tableta + verificare, nu rescriere. _migratedefensiv idempotent (tiparusers.is_admin3.3b) — US-001/007 il urmeaza.
Faza 1 — CEO (strategie & scope)
CEO DUAL VOICES — CONSENSUS:
Dimensiune Claude Codex Consens
------------------------------------- -------- ------- ---------
1. Premise valide? DA* N/A n/a (1 voce)
2. Problema corecta? DA N/A n/a
3. Calibrare scope? DISAGREE N/A -> taste (split?)
4. Alternative explorate suficient? PARTIAL N/A n/a
5. Riscuri competitive acoperite? DA N/A n/a
6. Traiectorie 6 luni sanatoasa? DA N/A n/a
* premise = decizii user deja luate (Q1/Q2 inchise, modal/calendar = „decizie utilizator")
Examinat, nimic blocant pe strategie. PRD-ul rezolva first-run friction confirmat E2E; scope-ul e calibrat prin valuri. CEO subagent a recomandat split-ul in 2 release-uri (D#1, respins) si a ridicat findings de „testeaza worker-ul" care cad in afara Non-Goals (D#2, respinse). Single-critical pastrat: Q3 (backward-compat CLI) — real, mutat la faza DX/Eng. Dream-state delta: 5.12 inchide first-run UX; ramane (separat) poarta E2E smoke + flux multi-user firma.
Faza 2 — Design (UI/UX)
Litmus (Claude design; Codex n/a):
Dimensiune Scor Nota
-------------------------------- ----- -------------------------------------------
Ierarhie informatie (mapcoloane) 7/10 US-003 ok; recomandat cap-tabel sticky pe fisiere cu 15+ coloane
Stari (load/empty/error/partial) 6/10 empty-file (US-003) si date ne-ISO (US-005) sub-specificate
Gate needs_review in modal 6/10 LANDMINE: gate HARD mutat dintr-o coloana vizibila intr-un modal
Responsive tableta (US-008) 7/10 breakpoint lipseste azi; spec sa fie pixel-exact, nu aspirational
Interactiune modal (US-006/007) 6/10 „Confirma valorile" = buton separat vs implicit-pe-save (ambiguu)
Issue-uri auto-decise: D#10 (date ne-ISO), D#11 (empty-file mesaj). Taste surfaced la gata: banner discoverability pe needs_review (T1) + buton „Confirma valorile" explicit (T2).
Faza 3 — Eng (arhitectura & teste)
ENG DUAL VOICES — CONSENSUS:
Dimensiune Claude Codex Consens
--------------------------- -------- ------- ---------
1. Arhitectura sanatoasa? DA(cond) N/A n/a — cond. pe partial corect parametrizat
2. Acoperire teste? PARTIAL N/A n/a — vezi test plan, 4 gap-uri
3. Riscuri performanta? DA N/A n/a — irelevant (UI/CRUD mic)
4. Securitate? DA N/A n/a — CSRF+scoped pastrate pe rute noi
5. Cai de eroare? PARTIAL N/A n/a — bulk mapping partial, date ne-ISO
6. Risc deployment? DA N/A n/a — 2 migrari nullable defensive
Diagrama arhitectura (componente noi vs existente):
US-001 create_account(+email) -> auth_routes.signup ──┐
accounts.email (migrare) -> tools/account.py CLI ┤ (3 call-sites de actualizat)
account_is_complete ┘
US-005 _form_editare.html (NOU) <── _trimitere_detaliu.html (cod_prestatie select + nemapate_inline RAMAN aici)
└──< _preview_rand.html (US-006: ramura inline ELIMINATA)
US-006 preview „Editeaza" -> #detaliu-modal-body (GET fragment) -> POST /editeaza -> inchideModal + OOB
US-007 import_rows.reviewed (migrare) -> _resolve_row_for_preview -> gate n_confirmat
├── web routes.py /confirma (citeste DB reviewed)
└── API import_router commit_import (reviewed_rows -> seteaza reviewed=1; contract stabil)
US-008 base.html @media (768-1024) NOU + verificare 767 existent
Findings auto-decise: D#5 (partial scope), D#6 (fix_map), D#7 (reviewed DEFAULT 0), D#8 (gate pe 2 canale),
D#9 (reset reviewed in apply_row_override), D#12 (bulk partial). Test plan scris pe disc (44 codepath-uri,
4 gap-uri marcate). Invariant critic confirmat (R2): editarea preview ramane override-only, NU re-queue —
test obligatoriu (#24/#25 in test plan): dupa editare preview, submissions neatins.
Faza 3.5 — DX (CLI + erori + contract API)
DX CONSENSUS (Claude; Codex n/a):
Dimensiune Nota
--------------------------------- -------------------------------------------
CLI create --email/--cui Q3 nerezolvat: a face flag-uri obligatorii rupe fixture-urile
Mesaj eroare CUI duplicat prietenos da, dar „cere accesul de la admin" nu spune CUM
Contract API commit reviewed_rows risc de divergenta tacuta -> rezolvat de D#8 + test #39
Migrare fixture-uri recomandat factory in conftest.py (DX gap real)
Auto-decis: D#14 (Q3 -> factory, fara --allow-incomplete). Taste surfaced: mesaj CUI duplicat sa includa
un canal de contact concret (T3).
Cross-Phase Themes
- Tema A — Gate-ul needs_review (Design + Eng). Design: mutarea in modal ascunde gate-ul (discoverability). Eng: gate-ul trebuie sa fie DB-backed pe ambele canale + reset la editare. Semnal high-confidence: tratati needs_review ca feature de sine statator in US-007, nu ca „stergere de coloana". -> T1 + D#8/D#9.
- Tema B — Sub-specificarea „Confirma valorile" (Design + Eng). Ambii: cand se seteaza
reviewed=1? Buton separat vs implicit pe save. -> T2. - Tema C — Q3 backward-compat (CEO + DX + Eng). Toate trei: a face email/CUI obligatorii rupe fixture-uri. -> D#14 (factory).
Implementation Tasks (aggregate)
Niciun fisier tasks-*.jsonl per faza (autoplan ruleaza review-urile inline, nu skill-urile standalone).
Task-urile concrete = AC-urile din US-001..008 + cele 14 decizii din audit trail + 4 gap-urile din test plan.
Status: DONE_WITH_CONCERNS
Concerns (de inchis inainte de executie, niciunul blocant): cele 3 taste decisions de la gate (T1 banner, T2 buton confirma, T3 contact CUI) + integrarea celor 14 decizii in AC-urile US. Codex indisponibil -> review single-voice; re-ruleaza dupa 2026-07-18 daca vrei al doilea unghi adversarial.