Dashboard compact (carduri-contor + lista slim), formular editare slim cu VIN unic, Observatii (text liber = operatii service) si prestatii ca chips multi-select. Propaga sistemul de design al landing-ului (teme/culori/carduri) in controalele aplicatiei. Decizii confirmate cu userul: D1 contoarele inlocuiesc bara de status; D2 teme aditive (light/dark/petrol/Auto + grafit/cobalt/cupru/hartie); D3 chips reale multi-cod; D4 contor Trimise all-time + luna + azi; D5 obs = denumirea operatiilor de service (in payload, fara coloana noua; concatenat la import). 8 stories / 5 valuri, backend separat de UI. Draft, asteapta poarta de aprobare. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
19 KiB
PRD 5.15 — Propagare design landing in aplicatie (dashboard compact + editare slim, VIN unic, prestatii multi-select)
Stare: draft
Proces complet:
docs/ROADMAP.md§5. Contract RAR (sursa de adevar):docs/api-rar-contract.md. Sistemul de design al landing-ului:app/web/templates/landing.html(commit41aa385),DESIGN.md. Starea trece:draft -> aprobat -> in-executie -> verify-pass -> inchis.
1. Obiectiv
Propagam sistemul de design al landing-ului comercial (carduri/liste/formulare compacte, slim, si cele 4 teme grafit/cobalt/cupru/hartie) in aplicatia reala. Concret: dashboard-ul Acasa primeste cardurile-contor + lista de trimiteri slim din mockup-ul hero, iar formularul de editare trimitere primeste designul compact din mockup-ul "prestatie noua", cu un singur camp VIN, Observatii ca text liber pentru operatiile de service si prestatii ca chips multi-select de coduri RAR. Userul a cerut explicit replicarea acestor doua mockup-uri pentru ca ii place cat de compacte/slim sunt.
Decizii de produs confirmate cu userul (poarta de aprobare a acestui PRD):
- D1: cardurile-contor INLOCUIESC bara de status actuala (
_status.html); pastram doar indicatorii de sanatate worker/RAR intr-o forma compacta. - D2: temele sunt ADITIVE — pastram light/dark/petrol + Auto SI adaugam cele 4 din landing (grafit/cobalt/cupru/hartie). Selectorul ciclic le parcurge pe toate. (grafit ~ dark si hartie ~ light raman optiuni separate, la cererea userului.)
- D3: prestatiile sunt chips reale multi-select — utilizatorul poate adauga mai multe coduri
din nomenclatorul RAR si poate sterge oricare; se trimite lista
prestatiicompleta (RAR accepta lista{codPrestatie, idPrezentare:null}—docs/api-rar-contract.md§payload). - D4 (contor Trimise): cardul "Trimise" arata trei valori temporale — all-time (principal)
- luna asta + azi (secundar). Necesita extinderea numaratorilor cu
sent_today/sent_month.
- luna asta + azi (secundar). Necesita extinderea numaratorilor cu
- D5 (Observatii = operatii service): in API-ul RAR, campul
obse DE FAPT denumirea operatiilor din service. Deciobs= text liber cu operatiile efectuate; la import, daca fisierul nu are coloana Observatii, concatenam denumirea operatiei de service inobs.obsramane inpayload_json(camp din contractul RAR), fara coloana noua.
Fapte verificate care fundamenteaza scope-ul (nu presupuneri):
vinla RAR e un singur camp (17 car., MAJUSCULE, fara O/I/Q) — cerinta "fara 2 campuri VIN" e deja respectata azi (_form_editare.htmlare un singurvin); ramane sa NU regresam.prestatiie deja lista in modelul intern (mapping.resolve_prestatii(prestatii: list[dict])) si in contractul RAR — multi-select nu cere model nou, ci editor nou.obsexista deja ca alias de coloana la import (import_router.py:71— Observatii/Obs/Mentiuni/Note) si ca text liber optional in contractul RAR (obs); azi NU e editabil in formular.
2. Non-Goals (anti scope-creep)
- Fara modificari pe backend-ul de trimitere: worker, masina de stari, idempotenta-logica
(
build_key), reconciliere, contract RAR. Recalcularea idempotentei la editare foloseste mecanismul EXISTENT (ca la 3.5/5.10), nu unul nou. - Fara migrare de schema decat daca strict necesar.
obssiprestatiitraiesc insubmissions.payload_json(de confirmat la US-005) — fara coloane noi daca payload-ul le poarta. - Fara stergerea functionalitatii listei de trimiteri: filtre (data/vehicul/stare), paginare, bulk-delete pe randuri blocate, click->detaliu raman; se schimba DOAR aspectul randului (slim).
- Fara schimbarea regulilor de mapare operatie->cod sau a validarii nomenclatorului RAR
(
mapping.py,validation.pyraman ca atare; doar callsite-urile de editare le folosesc cu lista). - Fara redesign al landing-ului (deja livrat in 5.x); aici doar IMPORTAM stilul lui in app.
3. Stories atomice
Backend + UI pentru acelasi comportament = 2 stories.
base.htmle fisier FIERBINTE (serializat intre valuri — un singur autor pe val). Toate UI verificate pe un esantion de teme (o tema luminoasa + una intunecata).
US-001: Teme aditive (light/dark/petrol + grafit/cobalt/cupru/hartie) + tokeni --card2/--line2
Ca operator de service vreau aceleasi teme ca pe landing pentru ca aplicatia sa para acelasi produs, coerent vizual.
- Depinde de: —
- Fisiere:
app/web/templates/base.html,DESIGN.md,tests/test_tema.py(~3 fisiere) - Test intai (RED):
tests/test_tema.py—test_cele_4_teme_definite,test_tokeni_card2_line2_in_toate_temele,test_anti_fouc_4_stari,test_migrare_localStorage_legacy - Acceptance criteria:
- Pastram temele EXISTENTE light/dark/petrol si ADAUGAM 4 teme noi grafit/cobalt/cupru/hartie,
definite prin token-urile EXISTENTE (
--bg/--card/--ink/--muted/--line/--ok/--warn/--err/--accent) + DOUA noi--card2(fundal input/contor) si--line2(separator subtire).--card2/--line2primesc valori si in light/dark/petrol (fallback rezonabil). Maparea landing->app pentru cele 4 noi:--text->--ink,--sub->--muted,--okt->--ok,--errt->--err,--infot->--accent. - Selectorul ciclic parcurge TOATE: light -> dark -> petrol -> grafit -> cobalt -> cupru ->
hartie -> Auto, afiseaza eticheta temei curente, persistenta
localStorage(D2). - "Auto" pastrat: urmeaza
prefers-color-scheme, rezolva la dark/grafit sau light/hartie (decizie minora: Auto -> dark + hartie pentru light, sau dark/grafit — aliniaza cu I2). - Script anti-FOUC in
<head>seteazadata-themesincron pre-paint pentru toate starile; valoare necunoscuta -> Auto, fara blink. Valorile vechi raman valide (nu se mapeaza fortat). - Contrast AA pentru text principal in toate temele (light + hartie sunt cele luminoase).
DESIGN.mdactualizat: sectiunea cromatica + selector tema reflecta toate temele.
- Pastram temele EXISTENTE light/dark/petrol si ADAUGAM 4 teme noi grafit/cobalt/cupru/hartie,
definite prin token-urile EXISTENTE (
- Verificare E2E: browser pe
/(dashboard logat) — ciclare prin toate temele, persistenta la refresh, fara FOUC; toate temele selectabile.
US-002: Componente de design slim in base.html (CSS, fara consumatori inca)
Ca dezvoltator vreau clase reutilizabile pentru carduri-contor, lista slim, campuri slim si chips pentru ca dashboard-ul si formularul sa le consume DRY, identic cu mockup-ul.
- Depinde de: US-001 (foloseste
--card2/--line2) - Fisiere:
app/web/templates/base.html,DESIGN.md,tests/test_web_responsive.py(~3 fisiere) - Test intai (RED):
tests/test_web_responsive.py—test_clasa_contor_card,test_clasa_lista_slim,test_clasa_camp_slim,test_clasa_chips - Acceptance criteria:
.contor-card(sau nume aliniat conventiei): cifra mare bold + eticheta mica muted, fundal--card2, bordura--line, radius 8px, padding 10-12px; variante de culoare a cifrei prin.s-*existente (verde/accent/rosu)..lista-trimiteri-slimcu rand.trimitere-slim: stanga = VIN mono (linia 1) + operatie·ora muted (linia 2, 11px); dreapta = pill de stare; separator--line2; padding 10-14px. Randul ramane clickabil (rol button) si pastreaza tinta 44px pe mobil.- Varianta slim de camp formular: label 11px muted deasupra, input ~30px inaltime, fundal
--card2, mono pentru VIN/odometru/nr; integrata in macro-ulcampdin_macros.htmlprintr-un flag (slim=True), fara a rupe randarea actuala (default neschimbat). .chips+.chip(cu buton×de stergere) pentru prestatii multi-select; accesibil (buton real cuaria-label), stilat ca in mockup (accent 18%, font 10-11px).- Zero regresie vizuala pe componentele existente (
.card/.pill/.act/.tabel-trimiteri).
- Verificare E2E: pagina de proba/sandbox sau direct in US-003/004/007; vizual pe un esantion de teme + 390/1280.
US-003: Dashboard Acasa — carduri-contor inlocuiesc bara de status
Ca operator vreau cele 3 carduri-contor compacte (Trimise / In coada / De corectat) pentru ca sa vad starea dintr-o privire, ca in mockup.
- Depinde de: US-002
- Fisiere:
app/web/templates/_status.html,app/web/templates/_acasa.html,app/web/routes.py(_status_countsextins cusent_today/sent_month),tests/test_web_status.py,tests/test_web_dashboard.py(~5 fisiere) - Test intai (RED):
tests/test_web_status.py—test_trei_contoare_card,test_trimise_all_time_luna_azi,test_sanatate_compacta_worker_rar,test_fara_bara_veche - Acceptance criteria:
- Antetul Acasa = card "Trimiteri RAR AUTOPASS" cu 3 contoare slim: In coada (queued, accent), Trimise (sent, verde), De corectat (blocate = needs_data + needs_mapping + error, rosu).
- Cardul Trimise afiseaza trei valori temporale (D4): all-time (cifra principala) + "luna asta"
+ "azi" (sub-linie secundara).
_status_countsextins cusent_today/sent_month(filtru peupdated_at/data trimitere; scoped pe cont), restul contoarelor din numaratoarea existenta. - Indicatorii de sanatate worker/RAR + ultima autentificare RAR raman, intr-o forma compacta (pill/glif), nu bara cu 2 randuri ca azi; pastreaza glifele accesibile ✓/✗ (nu doar culoare).
- Navigarea existenta (Trimiteri/Mapari + badge needs_mapping) se pastreaza.
- Scoped pe cont; poll-ul existent (
/_fragments/status) randeaza noul antet fara a pierde tab-ul. - Responsive: cele 3 contoare pe un rand pe desktop, stivuite/2-pe-rand pe mobil, fara overflow.
- Verificare E2E: browser pe
/— contoare corecte vs date din DB, sanatate worker mort/viu, poll pastreaza starea.
US-004: Lista de trimiteri — rand slim (VIN + operatie·ora + pill)
Ca operator vreau lista de trimiteri in stil slim ca in mockup pentru ca e mai compacta si mai usor de scanat, pastrand filtrele si actiunile.
- Depinde de: US-002
- Fisiere:
app/web/templates/_submissions.html,app/web/templates/_coada.html(filtre raman),tests/test_web_submissions.py,tests/test_web_responsive.py(~4 fisiere) - Test intai (RED):
tests/test_web_submissions.py—test_rand_slim_vin_operatie_pill,test_filtre_paginare_pastrate,test_bulk_doar_blocate,test_click_deschide_detaliu - Acceptance criteria:
- Fiecare rand: stanga VIN mono scurt (
vin_scurt) linia 1 + operatie + ora/data muted linia 2; dreapta pill de stare (stare_css/stare_scurt). Nr. inmatriculare, data completa si nr. prezentare RAR raman accesibile (linie meta discreta si/sau in modalul de detaliu). - Filtre (data/vehicul/stare —
_coada.html), paginarea numerotata si bulk-delete pe randuri blocate (checkbox doar pegestionabil) raman FUNCTIONALE. - Click pe rand deschide
/_fragments/trimitere/{id}in modal (neschimbat). - Slim layout consistent desktop si <=1024px (cardurile responsive existente nu regreseaza).
- Pill-urile de stare folosesc maparea din
labels.py(zero etichete noi).
- Fiecare rand: stanga VIN mono scurt (
- Verificare E2E: browser — filtrare + paginare + click detaliu + bulk pe blocate, pe 4 teme, pe 390/820/1280.
US-005: Backend — obs (Observatii) editabil si persistat
Ca operator vreau sa editez Observatiile (operatiile de service in text liber) pentru ca sa corectez/completez ce s-a facut, separat de codurile RAR.
- Depinde de: —
- Fisiere:
app/web/routes.py(/trimitere/{id}/corecteaza),app/api/v1/import_router.py(/_import/{id}/rand/{row}/editeaza,EDIT_FIELDS),app/validation.py(obs optional),app/payload_view.py(echo obs),tests/test_web_corectie*.py,tests/test_import_review.py(~6 fisiere) - Test intai (RED):
tests/test_web_corectie_obs.py—test_obs_editabil_persistat_corecteaza,test_obs_persistat_preview_editeaza,test_obs_optional_gol_ok,test_import_concateneaza_operatie_in_obs - Acceptance criteria:
obstraieste inpayload_json(campobsdin contractul RAR); fara coloana noua / migrare (D5).obsadaugat inEDIT_FIELDS;corecteazasiediteaza(preview) accepta si persistaobs.obsoptional (text liber, fara validare de continut, doar trim); apare inpayload_view.obsse include in payload-ul trimis la RAR (campobs) — fara a schimba celelalte campuri; idempotenta se recalculeaza ca la orice editare (mecanism existent).- La import, daca fisierul NU are coloana Observatii, denumirea operatiei de service se
CONCATENEAZA in
obs(D5:obs= operatiile efectuate); daca are coloana Observatii, se pastreaza textul ei. Format de concatenare definit (ex. denumiri separate prin "; ").
- Verificare E2E:
POST /trimitere/{id}/corecteazacuobs-> persistat -> vizibil in detaliu; optional proba live RAR caobsapare in FINALIZATA.
US-006: Backend — prestatii multi-cod (lista) la editare/corectie
Ca operator vreau sa adaug/sterg mai multe coduri RAR pe o trimitere pentru ca o comanda poate avea mai multe prestatii, asa cum accepta RAR.
- Depinde de: —
- Fisiere:
app/web/routes.py(/corecteaza,/repune),app/api/v1/import_router.py(/editeaza),app/mapping.py(folosit cu lista — fara schimbare de logica),app/validation.py(fiecare cod in nomenclator),tests/test_web_corectie*.py,tests/test_mapping*.py(~6 fisiere) - Test intai (RED):
tests/test_web_corectie_prestatii.py—test_mai_multe_coduri_acceptate,test_cod_invalid_respins,test_lista_goala_needs_mapping,test_idempotency_recalculat,test_odometru_initial_conditionat_R_ODO - Acceptance criteria:
- Handler-ele de editare accepta o LISTA de
cod_prestatie(mai multe valori), inlocuind selectul unic; reconstruiescprestatiica[{cod_prestatie, idPrezentare:null}, ...]. - Fiecare cod e validat fata de nomenclator (
valid_codes); cod necunoscut -> respins cu mesaj (NU se trimite raw — invariant ORA-12899 din CLAUDE.md/contract). - Lista goala de coduri -> ramane
needs_mapping(nu se trimite fara cod). - Recalcul idempotenta dupa editare (mecanism existent), cu prinderea coliziunii ca azi.
- Se pastreaza regula
odometruInitialobligatoriu cand lista contineR-ODO/I-ODO(contract §payload) — validare existenta, doar verificata pe lista.
- Handler-ele de editare accepta o LISTA de
- Verificare E2E:
POST /corecteazacu 2 coduri valide ->queuedcuprestatiide lungime 2; cu un cod invalid -> respins; optional live RAR cu 2 prestatii -> FINALIZATA.
US-007: UI — formular editare slim (VIN unic, Observatii, chips prestatii)
Ca operator vreau formularul de editare in design slim cu chips de prestatii pentru ca e compact si imi arata clar codurile RAR si observatiile, ca in mockup.
- Depinde de: US-002, US-005, US-006
- Fisiere:
app/web/templates/_form_editare.html,app/web/templates/_macros.html,app/web/templates/_trimitere_detaliu.html,app/web/templates/_editare_preview_modal.html,tests/test_web_preview_edit.py,tests/test_web_detaliu*.py(~6 fisiere) - Test intai (RED):
tests/test_web_form_editare_slim.py—test_un_singur_vin,test_camp_observatii_prezent,test_chips_multi_select_prestatii,test_adauga_sterge_chip,test_form_slim_in_ambele_modale - Acceptance criteria:
- Formularul foloseste varianta slim de camp (US-002): VIN, Data prestatiei, Nr. inmatriculare, Observatii (textarea), prestatii (chips), Odometru — un SINGUR camp VIN (fara "Confirma VIN").
- Observatii = textarea liber, legat de
obs(US-005). - Prestatii = chips multi-select: fiecare cod ca chip cu
×; un picker (dropdown din nomenclator) adauga un cod nou; lista se trimite cacod_prestatiemultiplu (US-006). - Acelasi
_form_editare.htmlslujeste ambele modale (detaliu/corecteazasi preview/editeaza), fara duplicare; degradare fara JS rezonabila (chips ca lista, picker = select). - Stilizare fidela mockup-ului pe cele 4 teme; tinte 44px pe mobil; a11y (label-uri, aria).
- Verificare E2E: browser — editare trimitere needs_data: schimb VIN + scriu Observatii + adaug 2 coduri RAR (chips) + sterg unul -> salvare -> persistat; identic in preview import.
US-008: Teste de regresie + E2E final pe cele 4 teme
Ca dezvoltator vreau acoperire si o trecere E2E completa pentru ca redesign-ul atinge fisiere fierbinti (base.html) si nu vreau regresii pe teme/liste/formular.
- Depinde de: US-003, US-004, US-007
- Fisiere:
tests/test_web_responsive.py,tests/test_tema.py,tests/test_web_submissions.py(~3 fisiere) - Test intai (RED): completare scenarii lipsa (4 teme x componente noi; slim list desktop+mobil)
- Acceptance criteria:
pytest -q -m "not live"verde (fara regresii fata de baseline).- E2E Playwright pe 390/820/1280, pe light/dark/petrol + cele 4 noi (esantion: grafit + hartie): dashboard contoare, lista slim cu filtre/paginare/bulk, formular slim cu chips, fara overflow orizontal.
- Verificare E2E: rulare completa documentata in Raportul VERIFY.
4. Riscuri
- base.html fisier fierbinte: US-001/US-002 il ating amandoua + US-003/004/007 il citesc. Serializeaza pe valuri (un singur autor pe val pe base.html), ca la 5.12/5.13.
- Migrare teme legacy: useri cu
localStorage.theme= light/dark/petrol. Mitigare: maparea grafioasa din US-001 (light->hartie, dark->grafit, petrol->grafit) + test dedicat. - Restyle lista = pierdere de functii: filtre/paginare/bulk pot fi sparte de schimbarea de markup. Mitigare: US-004 are AC explicite pentru pastrarea lor + teste lock.
- Idempotenta la prestatii multiple: schimbarea listei schimba cheia canonica. Mitigare: refolosim mecanismul existent de recalcul + prindere coliziune (3.5/5.10), zero logica noua.
- Densitate vizuala pe mobil: randul slim cu 2 linii + pill poate aglomera. Mitigare: tinte 44px + verificare 390px in US-004/008.
5. Intrebari deschise
Toate intrebarile au fost REZOLVATE cu userul (vezi D1-D5 §1). Pastrate aici ca istoric al deciziei.
- I1 — contor Trimise [REZOLVAT]: arata all-time + luna asta + azi (D4).
_status_countsextins. - I2 — teme [REZOLVAT]: aditiv — light/dark/petrol + Auto + grafit/cobalt/cupru/hartie (D2).
- I3 — stocare obs [REZOLVAT]: in
payload_json, fara coloana noua (D5). - I4 — operatii la import -> obs [REZOLVAT]: concatenam denumirea operatiei in
obscand fisierul nu are coloana Observatii (D5). - Reziduale minore (de decis la executie, non-blocante): formatul exact de concatenare a denumirilor
in
obs; rezolvarea "Auto" la light vs hartie; eventuala deduplicare grafitdark / hartielight in eticheta selectorului.
6. Valuri de executie (graful de dependente)
Val 1: [US-001] base.html teme + tokeni (autor unic pe base.html)
Val 2: [US-002] base.html componente (dupa US-001, autor unic pe base.html)
Val 3: [US-003] [US-004] dashboard + lista (consuma US-002; fisiere disjuncte) ||
[US-005] [US-006] backend obs + prestatii (fisiere backend; pot rula in paralel cu UI)
Val 4: [US-007] formular slim (dupa US-002+US-005+US-006)
Val 5: [US-008] regresie + E2E final
Raport VERIFY
Completat de subagentul verificator (context curat) in faza VERIFY — vezi ROADMAP §5.6. PASS/FAIL per criteriu, cu dovezi (output pytest citat, E2E pe RAR test). Lipseste pana la VERIFY.