feat(web): dashboard ergonomic cu tab-uri, stepper import si microcopy uman (3.4)
Reorganizeaza interfata web pe trei principii, fara a atinge backend-ul de trimitere (worker, mapping, idempotency, masina de stari neatinse): - US-001 app/web/labels.py: modul pur stari tehnice -> text uman + clasa CSS - US-002 bara status /_fragments/status: microcopy uman, defalcare blocate, scoped cont - US-003 shell 6 tab-uri (Acasa/Import/Coada/Mapari/Cont/Nomenclator): deep-link ?tab=, panou activ randat server-side, fragmente inactive lazy, ARIA real - US-004 stepper import 4 pasi (pur vizual; hx-target + csrf pastrate) - US-005 Acasa onboarding checklist auto-bifat + colaps + empty states prietenoase Reparat in cursul VERIFY/CLOSE: izolare teste (reset ratelimit._hits in fixturi), regresie avertisment "cont in asteptare de activare" (re-introdus in bara status), culori hardcodate -> variabile paleta. 434 teste pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# PRD 3.4 — Interfata web ergonomica (tab-uri + wizard + microcopy uman)
|
||||
|
||||
**Stare**: aprobat
|
||||
**Stare**: inchis
|
||||
|
||||
> Proces complet: `docs/ROADMAP.md` §5. Contractul RAR (sursa de adevar): `docs/api-rar-contract.md`.
|
||||
> Starea trece: `draft → aprobat → in-executie → verify-pass → inchis` (actualizata de lead).
|
||||
@@ -75,9 +75,9 @@ de "pagini ca un wizard, intuitive" si "caption-uri utile, relevante, simple, pe
|
||||
| `error` | **Eroare la trimitere** | Vezi detaliul randului; se reincearca automat sau necesita corectie. |
|
||||
|
||||
- **Acceptance criteria**:
|
||||
- [ ] `labels.py` expune o functie/dict care, pentru fiecare stare din `schema.sql`, da `(text, css_class)`.
|
||||
- [ ] Nicio stare de submission existenta nu ramane fara eticheta (test parametrizat care iese rosu daca se adauga o stare noua nemapata).
|
||||
- [ ] Functiile sunt pure (fara DB, fara request) — usor de testat unitar.
|
||||
- [x] `labels.py` expune o functie/dict care, pentru fiecare stare din `schema.sql`, da `(text, css_class)`.
|
||||
- [x] Nicio stare de submission existenta nu ramane fara eticheta (test parametrizat care iese rosu daca se adauga o stare noua nemapata).
|
||||
- [x] Functiile sunt pure (fara DB, fara request) — usor de testat unitar.
|
||||
- **Verificare E2E**: indirect, prin US-002/US-003 (etichetele apar in UI).
|
||||
|
||||
### US-002: Bara de status persistenta cu etichete umane (fragment)
|
||||
@@ -94,9 +94,9 @@ de "pagini ca un wizard, intuitive" si "caption-uri utile, relevante, simple, pe
|
||||
`labels.py`. Ramane sticky/vizibil sus indiferent de tab-ul activ. Defalca "Necesita atentia ta"
|
||||
pe motive. Pastreaza poll-ul HTMX (`every 15s`) deja existent pentru banner.
|
||||
- **Acceptance criteria**:
|
||||
- [ ] `/_fragments/status` randeaza bara cu etichetele din US-001 (scoped pe cont, ca restul UI).
|
||||
- [ ] Bara ramane vizibila sus cand se schimba tab-ul (nu e inghitita de panoul activ).
|
||||
- [ ] Cand exista submissions blocate, bara arata defalcarea pe motiv, nu doar un numar.
|
||||
- [x] `/_fragments/status` randeaza bara cu etichetele din US-001 (scoped pe cont, ca restul UI).
|
||||
- [x] Bara ramane vizibila sus cand se schimba tab-ul (nu e inghitita de panoul activ).
|
||||
- [x] Cand exista submissions blocate, bara arata defalcarea pe motiv, nu doar un numar.
|
||||
- **Verificare E2E**: browser — incarca `/`, bara de status arata text uman; opreste workerul →
|
||||
"Trimitere automata: oprita".
|
||||
|
||||
@@ -122,12 +122,12 @@ de "pagini ca un wizard, intuitive" si "caption-uri utile, relevante, simple, pe
|
||||
`role="tabpanel"` pe panou, navigare cu sageti intre tab-uri (JS vanilla minim). Mobil: tab-bar se
|
||||
ruleaza orizontal / se sparge curat (fara meniu hamburger — pastram simplu).
|
||||
- **Acceptance criteria**:
|
||||
- [ ] Tab-bar cu cele 6 tab-uri (Acasa · Import · Coada · Mapari · Cont · Nomenclator); "Acasa" implicit la prima incarcare.
|
||||
- [ ] Un singur panou randat la un moment dat; celelalte fragmente NU se cer pana la activarea tab-ului.
|
||||
- [ ] Panoul activ (inclusiv din `?tab=`) e randat **server-side** la full load — fara palpaire la refresh, vizibil si fara JS.
|
||||
- [ ] Accesibilitate: `role=tablist/tab/tabpanel`, `aria-selected` pe tab-ul activ, navigare cu sageti (nu doar focus vizibil).
|
||||
- [ ] Refresh pe un tab non-implicit revine pe acelasi tab (deep-link prin query string `?tab=`).
|
||||
- [ ] Toate functiile existente raman accesibile dintr-un tab (nimic pierdut fata de pagina veche).
|
||||
- [x] Tab-bar cu cele 6 tab-uri (Acasa · Import · Coada · Mapari · Cont · Nomenclator); "Acasa" implicit la prima incarcare.
|
||||
- [x] Un singur panou randat la un moment dat; celelalte fragmente NU se cer pana la activarea tab-ului.
|
||||
- [x] Panoul activ (inclusiv din `?tab=`) e randat **server-side** la full load — fara palpaire la refresh, vizibil si fara JS.
|
||||
- [x] Accesibilitate: `role=tablist/tab/tabpanel`, `aria-selected` pe tab-ul activ, navigare cu sageti (nu doar focus vizibil).
|
||||
- [x] Refresh pe un tab non-implicit revine pe acelasi tab (deep-link prin query string `?tab=`).
|
||||
- [x] Toate functiile existente raman accesibile dintr-un tab (nimic pierdut fata de pagina veche).
|
||||
- **Verificare E2E**: browser — click pe fiecare tab incarca panoul corect; refresh pe `?tab=import`
|
||||
ramane pe Import; navigare cu sageti intre tab-uri functioneaza (citior de ecran anunta tab-ul activ).
|
||||
|
||||
@@ -154,11 +154,11 @@ de "pagini ca un wizard, intuitive" si "caption-uri utile, relevante, simple, pe
|
||||
panoului de tab (nu vechiul container de la radacina paginii), iar `csrf_token` din formularele de
|
||||
import trebuie sa ramana corect. Verificat prin testele de mai sus + regula de aur.
|
||||
- **Acceptance criteria**:
|
||||
- [ ] Acelasi stepper apare in upload, mapare-coloane si preview, cu pasul corect evidentiat.
|
||||
- [ ] Pasii deja parcursi sunt marcati ca facuti; cei viitori sunt estompati.
|
||||
- [ ] Fiecare pas are un titlu-actiune + o fraza scurta de ajutor (microcopy din US-001 unde se aplica).
|
||||
- [ ] `hx-target` din fragmentele de import se rezolva in panoul de tab; `csrf_token` pastrat in formulare.
|
||||
- [ ] Fluxul de import functioneaza identic (upload → mapare → preview → confirma) — fara regresie.
|
||||
- [x] Acelasi stepper apare in upload, mapare-coloane si preview, cu pasul corect evidentiat.
|
||||
- [x] Pasii deja parcursi sunt marcati ca facuti; cei viitori sunt estompati.
|
||||
- [x] Fiecare pas are un titlu-actiune + o fraza scurta de ajutor (microcopy din US-001 unde se aplica).
|
||||
- [x] `hx-target` din fragmentele de import se rezolva in panoul de tab; `csrf_token` pastrat in formulare.
|
||||
- [x] Fluxul de import functioneaza identic (upload → mapare → preview → confirma) — fara regresie.
|
||||
- **Verificare E2E**: browser — urca `test_data.csv`, parcurge cei 4 pasi; stepper-ul avanseaza corect;
|
||||
commit → randuri in coada → worker → FINALIZATA la RAR test (regula de aur).
|
||||
|
||||
@@ -182,11 +182,11 @@ de "pagini ca un wizard, intuitive" si "caption-uri utile, relevante, simple, pe
|
||||
discreta ("Totul e configurat — vezi coada"), ca sa nu deranjeze utilizatorul experimentat. Sub ghid,
|
||||
pe acelasi tab, un rezumat scurt + scurtaturi (coada recenta / actiuni rapide).
|
||||
- **Acceptance criteria**:
|
||||
- [ ] Pasul "Conecteaza contul RAR" e nebifat fara creds, bifat cand `are_creds` e adevarat.
|
||||
- [ ] Pasul "Importa primul fisier" se bifeaza cand contul are cel putin un submission.
|
||||
- [ ] Cand toti pasii esentiali sunt gata, ghidul e colapsat/discret (nu ocupa tot ecranul).
|
||||
- [ ] Link-urile din ghid duc la tab-ul corect (Cont / Import).
|
||||
- [ ] **Empty states prietenoase**: tab Coada gol → "Nicio trimitere inca — incepe cu Import" (link la
|
||||
- [x] Pasul "Conecteaza contul RAR" e nebifat fara creds, bifat cand `are_creds` e adevarat.
|
||||
- [x] Pasul "Importa primul fisier" se bifeaza cand contul are cel putin un submission.
|
||||
- [x] Cand toti pasii esentiali sunt gata, ghidul e colapsat/discret (nu ocupa tot ecranul).
|
||||
- [x] Link-urile din ghid duc la tab-ul corect (Cont / Import).
|
||||
- [x] **Empty states prietenoase**: tab Coada gol → "Nicio trimitere inca — incepe cu Import" (link la
|
||||
tab Import); tab Mapari gol → mesaj scurt + indemn, nu un tabel/lista goala fara context.
|
||||
- **Verificare E2E**: browser — cont nou (fara creds): ghid vizibil cu pasi nebifati + tab Coada arata
|
||||
empty state cu indemn la Import; dupa setarea credentialelor si un import, pasii se bifeaza si ghidul se restrange.
|
||||
@@ -268,6 +268,46 @@ Val 4: [US-004] [US-005] ← ambele depind de shell-ul de tab-uri (US-003
|
||||
|
||||
## 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 browser pe `http://localhost:8000/`,
|
||||
> plus regula de aur: import → worker → FINALIZATA la RAR test). Lipseste pana la VERIFY.
|
||||
> Verificare condusa de lead (utilizatorul a respins E2E cu browser/server). Acoperire: suita
|
||||
> pytest completa + verificare ACs prin FastAPI TestClient + spot-check de integrare. E2E cu browser
|
||||
> live si regula de aur LIVE (FINALIZATA la RAR test) NU au fost rulate in aceasta sesiune — vezi nota.
|
||||
|
||||
**Suita**: `python3 -m pytest -q` → **434 passed** (de la 400 baseline: +34 teste noi 3.4). Fara regresie.
|
||||
|
||||
### PASS/FAIL per story
|
||||
|
||||
- **US-001 (labels.py)** — PASS. `tests/test_web_labels.py` (11 teste). `test_toate_starile_au_eticheta`
|
||||
parseaza CHECK-ul din `schema.sql` → iese rosu la stare noua nemapata. Functii pure (fara DB/request).
|
||||
- **US-002 (bara status)** — PASS. `tests/test_web_status_fragment.py`. `/_fragments/status` randeaza
|
||||
"Trimitere automata" (nu "worker viu"), defalcare blocate pe motiv, poll `every 15s`, scoped pe cont.
|
||||
- **US-003 (tab-uri)** — PASS. `tests/test_web_tabs.py` (6 teste). TestClient: `role="tablist"` + 6 tab-uri,
|
||||
Acasa implicit (`aria-selected="true"`), `/?tab=import` randeaza `#import-section` server-side, panou
|
||||
activ in HTML initial, role=tab/tabpanel + aria-selected, navigare cu sageti (JS vanilla). Fragmentele
|
||||
inactive NU se cer la load (swap pe click). Deep-link `?tab=` supravietuieste refresh-ului.
|
||||
- **US-004 (stepper)** — PASS. `tests/test_web_import_stepper.py` (8 teste). Stepper 4 pasi in
|
||||
upload(1)/mapcoloane(2)/preview(3), pasii facuti marcati, `aria-current="step"` pe activ, `hx-target="#import-section"`
|
||||
si `csrf_token` pastrate. Fluxul import (upload→mapare→preview→confirma) neatins — endpointuri intacte.
|
||||
- **US-005 (Acasa onboarding)** — PASS. `tests/test_web_onboarding.py` (6 teste). Checklist auto-bifat
|
||||
(are_creds/are_trimiteri), ghid colapsat cand totul gata, linkuri `?tab=cont`/`?tab=import`, empty states
|
||||
prietenoase pe Coada (indemn Import) si Mapari, scoped pe cont.
|
||||
|
||||
### Regula de aur (regresie)
|
||||
Backend de trimitere (worker, mapping, idempotency, import_router, masina de stari) **neatins** — confirmat
|
||||
prin diff. Fluxul de import pana la enqueue (`queued`) ramane verde prin `tests/test_import_ui.py` +
|
||||
`tests/test_import_e2e.py`. **Trimiterea LIVE la RAR test (FINALIZATA) NU a fost probata in aceasta sesiune**
|
||||
(fara browser/creds RAR test) — recomandata o probare manuala `./start.sh test both --send` inainte de prod.
|
||||
|
||||
### Defecte gasite si reparate in cursul VERIFY/CLOSE
|
||||
1. **Izolare teste (429)**: fixturile noi nu reseteau rate-limiterul de login in-proces (`ratelimit._hits`);
|
||||
rulate impreuna, login-urile depaseau pragul → 429 → 5 teste rosii la rulare subset. Reparat: `ratelimit._hits.clear()`
|
||||
in fixturile celor 4 fisiere noi (pattern din `test_web_login.py`). Suita completa trecea din noroc de ordine.
|
||||
2. **Regresie UX** (code-review): avertismentul "Cont in asteptare de activare" (vechiul `_banner.html`) nu
|
||||
mai era afisat dupa scoaterea `/_fragments/banner`. Re-introdus in bara de status (`account_active`).
|
||||
3. **Consistenta tema**: culori hardcodate in `_acasa.html` → variabile paletei (`--muted`/`--accent`/`--ok`).
|
||||
|
||||
### Cleanup notat, neremediat (non-blocant)
|
||||
- Duplicare intre `_render_panel_{mapari,cont,nomenclator}` si endpointurile fragment existente.
|
||||
- `/_fragments/banner` + `_banner.html` raman dead code dupa mutarea avertismentelor in bara de status.
|
||||
- Ramura moarta in `_render_panel_for_tab` (fallback acasa fara conn) — inaccesibila (tab pre-validat).
|
||||
- Bara de status e lazy (HTMX `load`), nu server-side: fara JS arata "se incarca…". AC formale indeplinite
|
||||
(panoul activ e server-side); de reconsiderat la o iteratie viitoare daca conteaza no-JS pe status.
|
||||
|
||||
Reference in New Issue
Block a user