feat(unified-mobile-material-design): Complete US-101a - Creare MobileTopBar.vue component

Implemented by Ralph autonomous loop.
Iteration: 1

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-01-12 09:46:30 +00:00
parent 7b3541403f
commit dfbaca2477
5 changed files with 1570 additions and 1056 deletions

View File

@@ -1,19 +1,23 @@
{
"projectName": "mobile-ux-improvements",
"branchName": "ralph/bulk-receipt-upload",
"description": "Corectarea comportamentului de refresh pentru bulk upload, implementarea selecției multiple pe mobil cu interfață Android nativă, afișarea numelui fișierului pentru toate bonurile, și rezolvarea bug-urilor de UX raportate.",
"projectName": "unified-mobile-material-design",
"branchName": "ralph/unified-mobile-md",
"description": "Extinderea interfeței Material Design la toate paginile aplicației în modul mobil pentru consistență UI/UX",
"cssRules": {
"documentation": [
"docs/ONBOARDING_CSS.md",
"docs/DESIGN_TOKENS.md",
"docs/CSS_PATTERNS.md"
"docs/CSS_PATTERNS.md",
"docs/MOBILE_PATTERNS.md"
],
"goldenRules": [
"Folosește DOAR design tokens - NICIODATĂ valori hardcodate",
"Verifică CSS_PATTERNS.md înainte de a scrie CSS nou",
"Testează în AMBELE teme (light + dark mode)",
"NICIODATĂ :deep() în componente (PrimeVue → vendor/)",
"NICIODATĂ duplicate CSS (write once, use everywhere)"
"NICIODATĂ duplicate CSS (write once, use everywhere)",
"Mobile: toate paginile folosesc MobileTopBar + MobileBottomNav",
"Mobile: filtrele se pun în BottomSheet, NU inline",
"Mobile: selecția afișează acțiuni în footer, NU în header"
],
"mobileLayoutTokens": {
"topBarHeight": "56px",
@@ -21,589 +25,316 @@
"fabSize": "56px",
"fabBottomOffset": "72px",
"touchTargetMin": "48px"
},
"selectionModeColors": {
"selected": {
"background": "var(--blue-50)",
"border": "var(--blue-500)"
},
"selectedDark": {
"background": "var(--blue-900)",
"border": "var(--blue-400)"
}
}
},
"userStories": [
{
"id": "US-001",
"title": "Backend - Stocare Batch și Processing Status",
"description": "Ca developer, vreau să extind schema Receipt pentru a stoca informații de batch, pentru că am nevoie de persistență pentru tracking.",
"id": "US-101a",
"title": "Creare MobileTopBar.vue component",
"description": "Ca developer vreau componentă MobileTopBar.vue extrasă din ReceiptsListView pentru reutilizare",
"priority": 1,
"acceptanceCriteria": [
"Câmpuri noi în tabelul receipts: batch_id, processing_status, processing_error, file_hash, processing_started_at, processing_completed_at",
"Index pe batch_id, file_hash, processing_status",
"Migration reversibilă cu Alembic"
"Componentă src/shared/components/mobile/MobileTopBar.vue creată",
"Props: title (string), showBack (boolean), showMenu (boolean), actions (array)",
"Emit events: menu-click, back-click, action-click",
"Stiluri CSS extrase din ReceiptsListView (.mobile-top-bar)",
"Dark mode support cu [data-theme='dark']",
"npm run build passes"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-002",
"title": "Backend - Endpoint List cu Batch Info",
"description": "Ca developer, vreau să extind endpoint-ul GET /receipts pentru a include info de batch.",
"priority": 2,
"acceptanceCriteria": [
"Response include câmpurile de batch și processing pentru fiecare receipt",
"Filtrare pe processing_status și batch_id funcționează",
"Response include processing_stats"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-003",
"title": "Backend - Reject Automat pentru Duplicate (File Hash)",
"description": "Ca sistem, vreau să detectez și să reject fișierele duplicate la upload.",
"priority": 3,
"acceptanceCriteria": [
"SHA-256 hash pentru duplicate detection",
"Response include existing_receipt_id pentru duplicates"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-004",
"title": "Frontend - Drag Anywhere pentru Upload",
"description": "Ca utilizator, vreau să pot trage fișiere oriunde pe pagina de bonuri.",
"priority": 4,
"acceptanceCriteria": [
"DragDropOverlay.vue componentă",
"Global listeners cleanup în onUnmounted"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-005",
"title": "Frontend - Row Grouping per Batch în DataTable",
"description": "Ca utilizator, vreau să văd bonurile din același batch grupate vizual.",
"priority": 5,
"acceptanceCriteria": [
"BatchGroupHeader.vue componentă",
"Grupuri sortate după processing_started_at descending"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-006",
"title": "Frontend - Coloană Status Procesare în Tabel",
"description": "Ca utilizator, vreau să văd statusul fiecărui bon din batch într-o coloană dedicată.",
"priority": 6,
"acceptanceCriteria": [
"ProcessingStatusCell.vue componentă",
"Status updates în real-time via polling"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-007",
"title": "Frontend - Mesaj Eroare Vizibil în Listă",
"description": "Ca utilizator, vreau să văd mesajul de eroare direct în listă.",
"priority": 7,
"acceptanceCriteria": [
"Truncated error message cu tooltip pentru full text"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-008",
"title": "Frontend - Quick Filter Chips pentru Statusuri Procesare",
"description": "Ca utilizator, vreau filtre rapide pentru bonurile cu erori sau în procesare.",
"priority": 8,
"acceptanceCriteria": [
"Chips 'În procesare (N)' și 'Cu erori (N)' în status bar"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-009",
"title": "Frontend - Lock Row în Procesare (Read-Only)",
"description": "Ca utilizator, vreau ca bonurile în procesare să fie read-only.",
"priority": 9,
"acceptanceCriteria": [
"Butoane și checkbox disabled pentru pending/processing",
"Tooltip pe butoane dezactivate"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-010",
"title": "Frontend - Retry Individual și Retry All Failed",
"description": "Ca utilizator, vreau să pot re-procesa bonurile cu erori.",
"priority": 10,
"acceptanceCriteria": [
"Buton Reîncercă per rând failed",
"Buton Reîncercă toate erorile în BatchGroupHeader"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-011",
"title": "Frontend - Auto-Resume Polling la Refresh/Revenire",
"description": "Ca utilizator, vreau ca procesarea să continue când revin.",
"priority": 11,
"acceptanceCriteria": [
"localStorage pentru active batch IDs",
"Auto-resume polling la onMounted"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-012",
"title": "Backend - Auto-Cleanup Erori După 7 Zile",
"description": "Ca sistem, vreau să șterg automat bonurile cu erori după 7 zile.",
"priority": 12,
"acceptanceCriteria": [
"Background job pentru cleanup",
"Șterge receipts și attachments"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-013",
"title": "Cleanup - Eliminare Pagină Separată Bulk Upload",
"description": "Ca developer, vreau să elimin pagina separată de bulk upload.",
"priority": 13,
"acceptanceCriteria": [
"Route redirect pentru backwards compatibility",
"Șterge componente nefolosite"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-014",
"title": "Backend - Endpoint Cancel Job Individual",
"description": "Ca sistem, vreau un endpoint API pentru anularea unui job specific de procesare.",
"priority": 14,
"acceptanceCriteria": [
"POST /api/data-entry/bulk/cancel/{job_id} endpoint creat",
"Job-uri cu status completed/failed returnează 400 Bad Request",
"Job-uri pending/processing sunt marcate cancelled",
"Response include: {success, job_id, cancelled_at, message}"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-015",
"title": "Backend - Endpoint Cancel Batch Complet",
"description": "Ca sistem, vreau un endpoint API pentru anularea tuturor job-urilor dintr-un batch.",
"priority": 15,
"acceptanceCriteria": [
"POST /api/data-entry/bulk/cancel-batch/{batch_id} endpoint creat",
"Response include: {success, batch_id, cancelled_count, skipped_count, message}"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-016",
"title": "Frontend - Store Actions pentru Cancel",
"description": "Ca dezvoltator, vreau acțiuni în batchProgressStore pentru cancel.",
"priority": 16,
"acceptanceCriteria": [
"batchProgressStore.cancelJob(jobId) implementat",
"batchProgressStore.cancelBatch(batchId) implementat"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-017",
"title": "Frontend - Afișare Jobs Pending în Tabel",
"description": "Ca utilizator, vreau să văd fișierele încărcate imediat în tabel.",
"priority": 17,
"acceptanceCriteria": [
"După upload success, rândurile pentru jobs apar instant în tabel",
"Tabelul poate randa atât Receipt-uri cât și BatchJob-uri"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-018",
"title": "Frontend - Tranziție Job → Receipt când OCR Termină",
"description": "Ca sistem, vreau ca rândul de job să se transforme în receipt când OCR termină.",
"priority": 18,
"acceptanceCriteria": [
"Când polling detectează job completed cu receipt_id, rândul se actualizează",
"Tranziția e smooth - rândul NU dispare și reapare"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-019",
"title": "Frontend - Animație Status Change",
"description": "Ca utilizator, vreau o indicație vizuală când un fișier își schimbă statusul.",
"priority": 19,
"acceptanceCriteria": [
"Badge-ul de status se schimbă cu CSS transition opacity 300ms",
"Highlight verde/roșu subtil pentru completed/failed"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-020",
"title": "Frontend - Buton Cancel Individual pe Row",
"description": "Ca utilizator, vreau un buton Cancel pe fiecare fișier pending/processing.",
"priority": 20,
"acceptanceCriteria": [
"Fișierele pending/processing au icon Cancel (×)",
"După cancel success, rândul dispare cu fade-out 300ms"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-021",
"title": "Frontend - Buton Cancel All în BatchGroupHeader",
"description": "Ca utilizator, vreau un buton pentru a anula toate fișierele dintr-un batch.",
"priority": 21,
"acceptanceCriteria": [
"BatchGroupHeader are buton 'Anulează tot' pentru batch-uri cu pending/processing jobs",
"Job-urile completed/failed rămân vizibile"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-022",
"title": "Frontend - Checkbox Disabled pentru Jobs în Procesare",
"description": "Ca utilizator, vreau ca checkbox-urile să fie disabled pentru fișiere în procesare.",
"priority": 22,
"acceptanceCriteria": [
"Checkbox-ul este disabled pentru rânduri de tip job",
"Select All NU include job-urile în procesare"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-023",
"title": "Frontend - Restore Jobs la Refresh/Revenire",
"description": "Ca utilizator, vreau să văd job-urile pending când revin pe pagină.",
"priority": 23,
"acceptanceCriteria": [
"La onMounted, verifică localStorage pentru active batch IDs",
"Job-urile pending/processing apar în tabel"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-024",
"title": "Backend - Endpoint Bulk Delete",
"description": "Ca frontend, vreau să pot trimite o listă de ID-uri pentru ștergere.",
"priority": 24,
"acceptanceCriteria": [
"DELETE /api/data-entry/receipts/bulk acceptă body: { ids: [...] }",
"Returnează partial success response"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-025",
"title": "Frontend - Buton Șterge în Bulk Actions Bar",
"description": "Ca utilizator, vreau să văd un buton 'Șterge' când am selecții.",
"priority": 25,
"acceptanceCriteria": [
"Butonul 'Șterge' apare în bulk actions bar când selectedReceipts.length > 0",
"Butonul are icon pi-trash și severity danger"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-026",
"title": "Frontend - Dialog Confirmare Ștergere Bulk",
"description": "Ca utilizator, vreau o confirmare înainte de ștergere.",
"priority": 26,
"acceptanceCriteria": [
"La click pe 'Șterge', apare dialog cu mesaj confirmare",
"Dialog-ul folosește PrimeVue ConfirmDialog"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-027",
"title": "Frontend - Bulk Delete cu Partial Success Toast",
"description": "Ca utilizator, vreau să văd rezultatul ștergerii.",
"priority": 27,
"acceptanceCriteria": [
"Toast arată rezultatul: 'X bonuri șterse'",
"Bonurile șterse dispar instant din listă",
"Selecția se golește după ștergere"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-028",
"title": "Frontend - Navigare la Pagina Anterioară când Lista Devine Goală",
"description": "Ca utilizator, vreau să fiu redirecționat când șterg toate bonurile de pe pagină.",
"priority": 28,
"acceptanceCriteria": [
"După bulk delete, dacă lista devine goală și currentPage > 1, navigare la pagina anterioară",
"Dacă eram pe pagina 1, afișează empty state"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-029",
"title": "Frontend - Metodă updateReceiptInPlace în Store",
"description": "Ca frontend, vreau să actualizez un singur rând fără să re-renderez toată lista.",
"priority": 29,
"acceptanceCriteria": [
"Metoda updateReceiptInPlace(receiptId, updates) în receiptsStore",
"Object.assign pentru updates, nu înlocuire array"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-030",
"title": "Backend - SSE Endpoint pentru Status Updates",
"description": "Ca frontend, vreau să primesc notificări real-time despre schimbări de status.",
"priority": 30,
"acceptanceCriteria": [
"GET /api/data-entry/receipts/sse/status returnează SSE stream",
"Format eveniment: {receipt_id, status, processing_status}"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-031",
"title": "Frontend - SSE Client Service",
"description": "Ca frontend, vreau să mă conectez la SSE și să actualizez rândurile individual.",
"priority": 31,
"acceptanceCriteria": [
"sseService.js cu connect(), disconnect(), onStatusChange()",
"Folosește native EventSource API"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-032",
"title": "Frontend - Înlocuire Polling cu SSE",
"description": "Ca frontend, vreau să folosesc SSE în loc de polling.",
"priority": 32,
"acceptanceCriteria": [
"SSE în loc de setInterval pentru auto-refresh",
"La primire eveniment SSE, apelează updateReceiptInPlace()"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-033",
"title": "Frontend - Graceful Degradation la SSE Failure",
"description": "Ca utilizator, vreau ca aplicația să funcționeze și fără SSE.",
"priority": 33,
"acceptanceCriteria": [
"Dacă SSE fail, activează fallback la polling clasic",
"Retry SSE periodic (la 30s)"
],
"passes": true,
"notes": "Completed previously"
},
{
"id": "US-034",
"title": "Fix - Refresh Individual vs Refresh Total",
"description": "Ca utilizator, vreau ca bonurile să se actualizeze individual fără să se reîncarce toată lista, pentru că vreau să văd progresul în timp real fără să pierd poziția.",
"priority": 34,
"acceptanceCriteria": [
"SSE handler NU apelează store.fetchReceipts() când un receipt nu este în pagina curentă",
"Verifică dacă receipt-ul aparține unui batch activ și îl adaugă local dacă nu există",
"Ordinea bonurilor din batch rămâne stabilă (nu se reordonează)",
"npm run typecheck passes",
"Verify in browser: uploadează 5 bonuri, nu se reîncarcă pagina între procesări"
],
"technicalNotes": "Modificare în handleSSEStatusChange din ReceiptsListView.vue:2397. Când receipt nu e găsit: verifică dacă batch_id e în batchProgressStore, apoi fetch individual receipt și inserează local în poziția corectă.",
"passes": true,
"notes": "Completed in iteration 1"
},
{
"id": "US-035",
"title": "Fix - Bonuri cu Eroare Rămân în Listă",
"description": "Ca utilizator, vreau ca bonurile cu eroare de procesare să rămână vizibile în listă, pentru că vreau să le pot edita manual sau să le șterg.",
"priority": 35,
"id": "US-101b",
"title": "Creare MobileBottomNav.vue component",
"description": "Ca developer vreau componentă MobileBottomNav.vue pentru navigarea de jos pe mobil",
"priority": 2,
"acceptanceCriteria": [
"Bonurile cu processing_status='failed' NU sunt eliminate din view după refresh",
"Eroarea de extragere dată/sumă afișează bonul cu status 'Eroare' și buton 'Editează'",
"Link rapid 'Editează manual' duce la formularul de editare cu datele disponibile pre-populate",
"Bonurile failed au highlight vizual (roșu subtil) pentru identificare ușoară",
"npm run typecheck passes",
"Verify in browser: bon cu eroare OCR rămâne în listă și poate fi editat"
"Componentă src/shared/components/mobile/MobileBottomNav.vue creată",
"Props: items (array of {to, icon, label, active})",
"4 linkuri: Bonuri, Upload, Rapoarte, Setări",
"Stiluri CSS extrase din ReceiptsListView (.mobile-bottom-nav)",
"router-link pentru navigare",
"npm run build passes"
],
"technicalNotes": "Bonurile cu eroare ar trebui să aibă status='draft' și processing_status='failed'. Nu se șterg la refresh, doar se actualizează in-place.",
"passes": true,
"notes": "Completed in iteration 2"
"passes": false,
"notes": ""
},
{
"id": "US-036",
"title": "Afișare Nume Fișier pentru Toate Bonurile",
"description": "Ca utilizator, vreau să văd numele fișierului original pentru bonurile procesate, pentru că vreau să identific care bon corespunde cărui fișier uploadat.",
"priority": 36,
"id": "US-101c",
"title": "Creare MobileSelectionFooter.vue component",
"description": "Ca developer vreau componentă pentru acțiuni batch când sunt selectate elemente",
"priority": 3,
"acceptanceCriteria": [
"Coloana 'Fișier' afișează original_filename și pentru receipt-uri completate, nu doar pentru job-uri",
"Pe mobil, numele fișierului apare sub partener (font mic, gri)",
"Tooltip pe desktop arată numele complet dacă e trunchiat",
"Dacă receipt nu are original_filename, afișează '-'",
"npm run typecheck passes",
"Verify in browser: bon procesat afișează numele fișierului original"
"Componentă src/shared/components/mobile/MobileSelectionFooter.vue creată",
"Props: visible (boolean), actions (array of {label, icon, severity, handler})",
"Animație slide-up cu Transition",
"Stiluri din .mobile-selection-bottom-bar",
"npm run build passes"
],
"technicalNotes": "Backend trebuie să populeze original_filename pe receipt din job-ul OCR. Verifică că auto-save service copiază filename din job la receipt.",
"passes": true,
"notes": "Completed in iteration 3"
"passes": false,
"notes": ""
},
{
"id": "US-037",
"title": "Fix - Upload Nu Mai Face Refresh Automat",
"description": "Ca utilizator, vreau ca selectarea fișierelor să nu reîncarce pagina, pentru că pierd fișierele selectate înainte să apăs 'Procesează'.",
"priority": 37,
"id": "US-102",
"title": "Definire MD3 color tokens în CSS",
"description": "Ca developer vreau variabile CSS pentru Material Design 3 color system",
"priority": 4,
"acceptanceCriteria": [
"Selectarea fișierelor via input NU declanșează refresh/reload pagină",
"Fișierele selectate rămân în listă până la submit explicit",
"Comportament identic pe desktop și mobil (Chrome, Safari)",
"Nu există race condition între clonarea fișierelor și resetarea input-ului",
"npm run typecheck passes",
"Verify on mobile: selectează 5 fișiere, toate rămân în listă"
"Fișier src/assets/css/core/md3-tokens.css creat",
"Tokens: --md-sys-color-primary, --md-sys-color-on-primary, --md-sys-color-surface, --md-sys-color-on-surface, --md-sys-color-outline",
"Dark mode variants cu [data-theme='dark']",
"Auto dark mode cu @media (prefers-color-scheme: dark)",
"Import adăugat în main.css",
"npm run build passes"
],
"technicalNotes": "Bug probabil în onBulkFileInputChange sau în interacțiunea cu store. Verifică că nu există watchers care declanșează refresh la schimbări de state. Test specific pe Chrome Android - vezi gotcha din CLAUDE.md despre File API.",
"passes": true,
"notes": "Completed in iteration 4"
"passes": false,
"notes": ""
},
{
"id": "US-038",
"title": "Mobile - Selecție Multiplă prin Long-Press",
"description": "Ca utilizator mobil, vreau să selectez bonuri prin apăsare lungă, pentru că vreau să pot șterge sau acționa asupra mai multor bonuri simultan.",
"priority": 38,
"id": "US-103",
"title": "Refactor ReceiptsListView să folosească componente comune",
"description": "Ca developer vreau ReceiptsListView să folosească noile componente pentru validare API",
"priority": 5,
"acceptanceCriteria": [
"Long-press (500ms) pe un card activează modul de selecție",
"Card-ul selectat primește checkmark și background diferit (var(--blue-50))",
"După activare, tap simplu pe alte carduri le adaugă/elimină din selecție",
"Tap în afara cardurilor dezactivează modul selecție",
"Header contextual arată numărul de elemente selectate",
"npm run typecheck passes",
"Verify on mobile: long-press selectează, tap adaugă la selecție"
"Import MobileTopBar, MobileBottomNav, MobileSelectionFooter în ReceiptsListView",
"Înlocuire template HTML cu componente",
"Ștergere CSS duplicat din ReceiptsListView (>200 linii)",
"Funcționalitate identică cu implementarea actuală",
"Verify in browser că lista bonuri funcționează identic pe mobil",
"npm run build passes"
],
"technicalNotes": "Implementare cu setTimeout(500ms) pe touchstart, clear pe touchend/touchmove. CSS: .receipt-card.selected { background: var(--blue-50); border-color: var(--blue-500); }",
"passes": true,
"notes": "Completed in iteration 5"
"passes": false,
"notes": ""
},
{
"id": "US-039",
"title": "Mobile - Select All și Buton Ștergere",
"description": "Ca utilizator mobil, vreau să am butoane 'Selectează tot' și 'Șterge' în modul selecție, pentru că vreau să pot șterge rapid mai multe bonuri.",
"priority": 39,
"id": "US-104",
"title": "Eliminare buton delete duplicat din header tabel",
"description": "Ca utilizator vreau butonul delete doar în footer, nu și în header când e selecție activă",
"priority": 6,
"acceptanceCriteria": [
"În modul selecție, apare top bar contextual cu: număr selectate, buton 'Selectează tot', buton X pentru ieșire",
"Apare bottom bar cu buton 'Șterge' (roșu, icon coș)",
"'Selectează tot' selectează toate bonurile din pagina curentă",
"Buton 'Șterge' afișează confirmare înainte de ștergere",
"După ștergere, modul selecție se dezactivează",
"npm run typecheck passes",
"Verify on mobile: select all + delete funcționează"
"În ReceiptsListView, bulk-actions-bar afișat DOAR când !isMobile",
"Pe mobil, delete apare DOAR în mobile-selection-bottom-bar (footer)",
"Pe desktop, delete rămâne în bulk-actions-bar (header tabel)",
"Verify in browser: pe mobil delete e doar în footer",
"Verify in browser: pe desktop delete e în header",
"npm run build passes"
],
"technicalNotes": "Top bar: position: sticky; top: 0. Bottom bar: position: fixed; bottom: 0. Animație slide-in pentru bars.",
"passes": true,
"notes": "Completed in iteration 6"
"passes": false,
"notes": ""
},
{
"id": "US-040",
"title": "Mobile - Layout Android Nativ pentru Lista Bonuri",
"description": "Ca utilizator mobil, vreau o interfață similară cu aplicațiile Android native, pentru că vreau experiență familiară și intuitivă.",
"priority": 40,
"id": "US-105",
"title": "Adăugare buton Înapoi în editare bon",
"description": "Ca utilizator vreau să pot reveni la lista de bonuri din pagina de editare cu buton ←",
"priority": 7,
"acceptanceCriteria": [
"Top Bar fixă cu: hamburger/back (stânga), titlu centrat, search+filter+more (dreapta)",
"Filter Chips sub top bar (orizontal scrollabil): Toate, Ciorne, În așteptare, Validate, Respinse",
"Bottom Navigation fixă cu 4 tab-uri: Bonuri (activ), Upload, Rapoarte, Setări",
"FAB (56x56px) în colț dreapta jos, 16px de la margine, 72px de la bottom",
"FAB se ascunde când scroll în jos, apare când scroll în sus",
"Lista ocupă spațiul dintre top bar și bottom nav",
"npm run typecheck passes",
"Verify on mobile: layout similar cu Gmail/WhatsApp"
"MobileTopBar adăugat în ReceiptCreateUnifiedView.vue",
"showBack=true pentru a afișa săgeata ←",
"Click pe ← navighează la /data-entry",
"Title dinamic: 'Bon nou' pentru create, 'Editare Bon' pentru edit",
"Verify in browser că butonul funcționează",
"npm run build passes"
],
"technicalNotes": "CSS: .mobile-top-bar { position: fixed; top: 0; height: 56px; z-index: 1000 }. .mobile-bottom-nav { position: fixed; bottom: 0; height: 56px }. .mobile-fab { position: fixed; bottom: 72px; right: 16px; width: 56px; border-radius: 16px }",
"passes": true,
"notes": "Completed in iteration 7"
"passes": false,
"notes": ""
},
{
"id": "US-041",
"title": "Mobile - Layout Android pentru Editare/Creare Bon",
"description": "Ca utilizator mobil, vreau interfață de editare bon similară cu compose email în Gmail, pentru că vreau butoane de acțiune accesibile și layout familiar.",
"priority": 41,
"id": "US-112",
"title": "Creare BottomSheet.vue component pentru filtre",
"description": "Ca utilizator vreau să pot accesa filtrele într-un bottom sheet pe mobil",
"priority": 8,
"acceptanceCriteria": [
"Top Bar cu: X/back (stânga), titlu centrat, attach+save icons (dreapta)",
"Content Area scrollabilă cu form fields 100% width",
"Bottom Action Bar fixă cu: 'Salvează Ciornă' (secondary), 'Trimite pentru Validare' (primary)",
"Keyboard-aware: bottom bar se mută deasupra tastaturii",
"npm run typecheck passes",
"Verify on mobile: layout similar cu Gmail compose"
"Componentă src/shared/components/mobile/BottomSheet.vue creată",
"v-model:visible pentru control",
"Animație slide-up smooth",
"Drag handle în partea de sus",
"Close pe tap outside overlay",
"Slot default pentru conținut",
"npm run build passes"
],
"technicalNotes": "CSS: .mobile-receipt-form { padding-bottom: 80px }. .mobile-form-bottom-bar { position: fixed; bottom: 0; display: flex; gap: var(--space-sm) }. Butoane cu flex: 1 pentru width egal.",
"passes": true,
"notes": "Completed in iteration 8"
"passes": false,
"notes": ""
},
{
"id": "US-042",
"title": "Mobile - Layout Android pentru Vizualizare Bon",
"description": "Ca utilizator mobil, vreau interfață de vizualizare bon similară cu view email în Gmail, pentru că vreau acțiuni rapide și navigare ușoară.",
"priority": 42,
"id": "US-106",
"title": "Dashboard Mobile cu Swipeable KPI Cards",
"description": "Ca utilizator vreau KPI-uri ca carousel swipeable pe mobil cu dots indicator",
"priority": 9,
"acceptanceCriteria": [
"Top Bar cu: back arrow (stânga), edit+delete+more icons (dreapta)",
"Content Area cu detalii bon (read-only)",
"Bottom Action Bar cu butoane contextuale: DRAFT→Editează/Trimite, PENDING→Validează/Respinge, APPROVED→Anulează",
"npm run typecheck passes",
"Verify on mobile: acțiuni accesibile din bottom bar"
"Componentă src/shared/components/mobile/SwipeableCards.vue creată",
"Touch swipe left/right funcțional (touchstart, touchmove, touchend)",
"Dots indicator pentru poziție curentă",
"DashboardView.vue folosește componenta când isMobile",
"Fallback la layout grid normal pe desktop",
"npm run build passes"
],
"technicalNotes": "Refolosește stilurile din US-041. Butoanele din bottom bar se schimbă dinamic bazat pe receipt.status.",
"passes": true,
"notes": "Completed in iteration 9"
"passes": false,
"notes": ""
},
{
"id": "US-043",
"title": "Păstrare Ordine Bonuri la Refresh",
"description": "Ca utilizator, vreau ca bonurilerămână în ordinea în care au fost uploadate, pentru că vreau să urmăresc progresul fiecărui bon uploadat.",
"priority": 43,
"id": "US-107",
"title": "InvoicesView Mobile Material Design",
"description": "Ca utilizator vreau pagina de facturi să aibă același header Material ca lista bonuri",
"priority": 10,
"acceptanceCriteria": [
"Bonurile din același batch păstrează ordinea de upload (nu se reordonează)",
"Un bon finalizat rămâne în aceeași poziție vizuală, nu sare la sfârșit",
"SSE updates modifică status-ul in-place, fără a muta rândul",
"Refresh manual poate reordona, dar SSE updates nu",
"npm run typecheck passes",
"Verify in browser: bon procesat nu își schimbă poziția în listă"
"MobileTopBar adăugat cu title 'Facturi'",
"Actions în header: refresh (pi-refresh), export (pi-download)",
"MobileBottomNav adăugat",
"Filtre existente mutate în BottomSheet pe mobil",
"Verify in browser că arată consistent cu bonuri",
"npm run build passes"
],
"technicalNotes": "Lista trebuie sortată by created_at DESC sau by batch order, nu by last_modified. updateReceiptInPlace din store NU trebuie să reordoneze array-ul.",
"passes": true,
"notes": "Completed in iteration 10"
"passes": false,
"notes": ""
},
{
"id": "US-108",
"title": "TrialBalanceView Mobile Material Design",
"description": "Ca utilizator vreau pagina de balanță să aibă același header Material",
"priority": 11,
"acceptanceCriteria": [
"MobileTopBar adăugat cu title 'Balanță de Verificare'",
"Actions în header: export (pi-download)",
"MobileBottomNav adăugat",
"Filtre existente mutate în BottomSheet pe mobil",
"npm run build passes"
],
"passes": false,
"notes": ""
},
{
"id": "US-109",
"title": "BankCashRegisterView Mobile Material Design",
"description": "Ca utilizator vreau pagina de trezorerie să aibă același header Material",
"priority": 12,
"acceptanceCriteria": [
"MobileTopBar adăugat cu title 'Trezorerie'",
"Actions în header: refresh, export",
"MobileBottomNav adăugat",
"Filtre în BottomSheet pe mobil",
"npm run build passes"
],
"passes": false,
"notes": ""
},
{
"id": "US-110",
"title": "ServerLogsView Mobile Material Design",
"description": "Ca admin vreau pagina de loguri să aibă interfață Material",
"priority": 13,
"acceptanceCriteria": [
"MobileTopBar adăugat cu title 'Loguri Server'",
"Actions în header: refresh, export",
"MobileBottomNav adăugat",
"npm run build passes"
],
"passes": false,
"notes": ""
},
{
"id": "US-111",
"title": "CacheStatsView Mobile Material Design",
"description": "Ca admin vreau pagina de statistici cache să aibă interfață Material",
"priority": 14,
"acceptanceCriteria": [
"MobileTopBar adăugat cu title 'Statistici Cache'",
"Actions în header: refresh",
"MobileBottomNav adăugat",
"npm run build passes"
],
"passes": false,
"notes": ""
},
{
"id": "US-113",
"title": "Batch Actions per Module contextuale",
"description": "Ca utilizator vreau acțiuni batch diferite în funcție de modulul curent",
"priority": 15,
"acceptanceCriteria": [
"MobileSelectionFooter acceptă prop actions ca array",
"Bonuri: Delete + Export",
"Facturi (dacă se adaugă selecție): Export + Print",
"Fiecare acțiune are icon, label, severity, handler",
"npm run build passes"
],
"passes": false,
"notes": ""
},
{
"id": "US-118",
"title": "Creare MOBILE_PATTERNS.md documentație",
"description": "Ca developer vreau documentație dedicată pentru pattern-urile mobile",
"priority": 16,
"acceptanceCriteria": [
"Fișier docs/MOBILE_PATTERNS.md creat",
"Table of Contents cu linkuri rapide",
"Secțiuni: MobileTopBar, MobileBottomNav, MobileSelectionFooter, BottomSheet, SwipeableCards",
"Exemple de cod copy-paste pentru fiecare componentă",
"Secțiune Quick Start pentru devs noi",
"Diagrame ASCII pentru layout mobile"
],
"passes": false,
"notes": ""
},
{
"id": "US-115",
"title": "Actualizare CSS_PATTERNS.md cu Mobile Material Design",
"description": "Ca developer viitor vreau referință la MOBILE_PATTERNS.md în CSS_PATTERNS.md",
"priority": 17,
"acceptanceCriteria": [
"Secțiune nouă 'Mobile Material Design' în CSS_PATTERNS.md",
"Link către docs/MOBILE_PATTERNS.md pentru detalii",
"Rezumat scurt al componentelor disponibile",
"Breakpoint-uri mobile documentate"
],
"passes": false,
"notes": ""
},
{
"id": "US-116",
"title": "Actualizare DESIGN_TOKENS.md cu MD3 Tokens",
"description": "Ca developer vreau documentație pentru noile MD3 color tokens",
"priority": 18,
"acceptanceCriteria": [
"Secțiune nouă 'Material Design 3 Tokens' în DESIGN_TOKENS.md",
"Tabel cu toate variabilele --md-sys-color-*",
"Mapping la tokens existenți",
"Exemple pentru dark mode"
],
"passes": false,
"notes": ""
},
{
"id": "US-117",
"title": "Actualizare CLAUDE.md cu reguli Mobile Development",
"description": "Ca Claude Code vreau reguli clare pentru dezvoltare mobilă",
"priority": 19,
"acceptanceCriteria": [
"Secțiune nouă '### Mobile Development Rules' în CLAUDE.md",
"Regulă: Toate paginile mobile folosesc MobileTopBar",
"Regulă: Filtrele pe mobil în BottomSheet",
"Regulă: Selecția afișează acțiuni în footer",
"Regulă: Touch targets minim 44x44px",
"Checklist rapid pentru code review mobile"
],
"passes": false,
"notes": ""
},
{
"id": "US-119",
"title": "Actualizare claude-learn-frontend.md cu pattern-uri noi",
"description": "Ca Claude Code vreau pattern-urile salvate în memoria pentru sesiuni viitoare",
"priority": 20,
"acceptanceCriteria": [
"Pattern: Mobile Material Design Component Architecture",
"Pattern: Bottom Sheet Filter Pattern",
"Pattern: Mobile Selection Mode Flow",
"Gotcha: Nu duplica delete button în header și footer",
"Format corect cu @date și tags #mobile #material-design"
],
"passes": false,
"notes": ""
}
]
}