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

@@ -0,0 +1,609 @@
{
"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.",
"cssRules": {
"documentation": [
"docs/ONBOARDING_CSS.md",
"docs/DESIGN_TOKENS.md",
"docs/CSS_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)"
],
"mobileLayoutTokens": {
"topBarHeight": "56px",
"bottomNavHeight": "56px",
"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.",
"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"
],
"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,
"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"
],
"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"
},
{
"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,
"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"
],
"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"
},
{
"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,
"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ă"
],
"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"
},
{
"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,
"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"
],
"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"
},
{
"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,
"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ă"
],
"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"
},
{
"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,
"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"
],
"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"
},
{
"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,
"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"
],
"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"
},
{
"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,
"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"
],
"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"
},
{
"id": "US-043",
"title": "Păstrare Ordine Bonuri la Refresh",
"description": "Ca utilizator, vreau ca bonurile să rămână în ordinea în care au fost uploadate, pentru că vreau să urmăresc progresul fiecărui bon uploadat.",
"priority": 43,
"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ă"
],
"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"
}
]
}

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": ""
}
]
}

View File

@@ -1,516 +1,15 @@
# Ralph Progress Log
Started: Sat Jan 11 02:30:00 PM UTC 2026
Project: bulk-upload-list-integration (continuation of bulk-receipt-upload)
Branch: ralph/bulk-receipt-upload (existing)
PRD: tasks/prd-bulk-upload-list-integration.md
Note: Continuing on same branch - bulk upload base already implemented
Started: $(date)
Project: unified-mobile-material-design
Branch: ralph/unified-mobile-md
Total Stories: 20
---
[2026-01-11 14:05:11] Starting Ralph for project: bulk-upload-list-integration
[2026-01-11 14:05:11] Max iterations: 50
[2026-01-11 14:05:11] === Iteration 1/50 ===
[2026-01-11 14:05:11] Working on story: US-011
[2026-01-11 14:05:11] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_1_US-011.log)
[2026-01-11 15:02:40] SUCCESS: Story US-011 passed!
[2026-01-11 15:02:40] Changes committed
[2026-01-11 15:02:40] Progress: 1/13 stories completed
[2026-01-11 15:02:42] === Iteration 2/50 ===
[2026-01-11 15:02:42] Working on story: US-012
[2026-01-11 15:02:42] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_2_US-012.log)
[2026-01-11 15:08:26] SUCCESS: Story US-012 passed!
[2026-01-11 15:08:26] Changes committed
[2026-01-11 15:08:26] Progress: 2/13 stories completed
[2026-01-11 15:08:28] === Iteration 3/50 ===
[2026-01-11 15:08:28] Working on story: US-007
[2026-01-11 15:08:28] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_3_US-007.log)
[2026-01-11 15:16:09] SUCCESS: Story US-007 passed!
[2026-01-11 15:16:09] Changes committed
[2026-01-11 15:16:09] Progress: 3/13 stories completed
[2026-01-11 15:16:11] === Iteration 4/50 ===
[2026-01-11 15:16:11] Working on story: US-001
[2026-01-11 15:16:11] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_4_US-001.log)
[2026-01-11 15:20:19] SUCCESS: Story US-001 passed!
[2026-01-11 15:20:19] Changes committed
[2026-01-11 15:20:19] Progress: 4/13 stories completed
[2026-01-11 15:20:21] === Iteration 5/50 ===
[2026-01-11 15:20:21] Working on story: US-002
[2026-01-11 15:20:21] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_5_US-002.log)
[2026-01-11 15:25:52] Story US-002 not yet complete, continuing...
[2026-01-11 15:25:52] Progress: 4/13 stories completed
[2026-01-11 15:25:54] === Iteration 6/50 ===
[2026-01-11 15:25:54] Working on story: US-002
[2026-01-11 15:25:54] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_6_US-002.log)
[2026-01-11 15:28:49] SUCCESS: Story US-002 passed!
[2026-01-11 15:28:49] Changes committed
[2026-01-11 15:28:49] Progress: 5/13 stories completed
[2026-01-11 15:28:51] === Iteration 7/50 ===
[2026-01-11 15:28:51] Working on story: US-003
[2026-01-11 15:28:51] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_7_US-003.log)
[2026-01-11 15:31:48] SUCCESS: Story US-003 passed!
[2026-01-11 15:31:48] Changes committed
[2026-01-11 15:31:48] Progress: 6/13 stories completed
[2026-01-11 15:31:50] === Iteration 8/50 ===
[2026-01-11 15:31:50] Working on story: US-004
[2026-01-11 15:31:50] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_8_US-004.log)
[2026-01-11 15:36:36] SUCCESS: Story US-004 passed!
[2026-01-11 15:36:37] Changes committed
[2026-01-11 15:36:37] Progress: 7/13 stories completed
[2026-01-11 15:36:39] === Iteration 9/50 ===
[2026-01-11 15:36:39] Working on story: US-005
[2026-01-11 15:36:39] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_9_US-005.log)
[2026-01-11 15:41:56] SUCCESS: Story US-005 passed!
[2026-01-11 15:41:56] Changes committed
[2026-01-11 15:41:56] Progress: 8/13 stories completed
[2026-01-11 15:41:58] === Iteration 10/50 ===
[2026-01-11 15:41:58] Working on story: US-010
[2026-01-11 15:41:58] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_10_US-010.log)
[2026-01-11 15:48:38] SUCCESS: Story US-010 passed!
[2026-01-11 15:48:38] Changes committed
[2026-01-11 15:48:38] Progress: 9/13 stories completed
[2026-01-11 15:48:40] === Iteration 11/50 ===
[2026-01-11 15:48:40] Working on story: US-006
[2026-01-11 15:48:40] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_11_US-006.log)
[2026-01-11 15:48:44] Story US-006 not yet complete, continuing...
[2026-01-11 15:48:44] Progress: 9/13 stories completed
[2026-01-11 15:48:46] === Iteration 12/50 ===
[2026-01-11 15:48:46] Working on story: US-006
[2026-01-11 15:48:46] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_12_US-006.log)
[2026-01-11 15:57:30] SUCCESS: Story US-006 passed!
[2026-01-11 15:57:30] Changes committed
[2026-01-11 15:57:30] Progress: 10/13 stories completed
[2026-01-11 15:57:32] === Iteration 13/50 ===
[2026-01-11 15:57:32] Working on story: US-009
[2026-01-11 15:57:32] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_13_US-009.log)
[2026-01-11 15:58:11] Story US-009 not yet complete, continuing...
[2026-01-11 15:58:11] Progress: 10/13 stories completed
[2026-01-11 15:58:13] === Iteration 14/50 ===
[2026-01-11 15:58:13] Working on story: US-009
[2026-01-11 15:58:13] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_14_US-009.log)
[2026-01-11 15:58:16] Story US-009 not yet complete, continuing...
[2026-01-11 15:58:16] Progress: 10/13 stories completed
[2026-01-11 15:58:18] === Iteration 15/50 ===
[2026-01-11 15:58:18] Working on story: US-009
[2026-01-11 15:58:18] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_15_US-009.log)
[2026-01-11 15:58:22] Story US-009 not yet complete, continuing...
[2026-01-11 15:58:22] Progress: 10/13 stories completed
[2026-01-11 15:58:24] === Iteration 16/50 ===
[2026-01-11 15:58:24] Working on story: US-009
[2026-01-11 15:58:24] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_16_US-009.log)
[2026-01-11 15:58:27] Story US-009 not yet complete, continuing...
[2026-01-11 15:58:27] Progress: 10/13 stories completed
[2026-01-11 15:58:29] === Iteration 17/50 ===
[2026-01-11 15:58:29] Working on story: US-009
[2026-01-11 15:58:29] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_17_US-009.log)
[2026-01-11 15:58:33] Story US-009 not yet complete, continuing...
[2026-01-11 15:58:33] Progress: 10/13 stories completed
[2026-01-11 15:58:35] === Iteration 18/50 ===
[2026-01-11 15:58:35] Working on story: US-009
[2026-01-11 15:58:35] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_18_US-009.log)
[2026-01-11 15:58:39] Story US-009 not yet complete, continuing...
[2026-01-11 15:58:39] Progress: 10/13 stories completed
[2026-01-11 15:58:41] === Iteration 19/50 ===
[2026-01-11 15:58:41] Working on story: US-009
[2026-01-11 15:58:41] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_19_US-009.log)
[2026-01-11 15:58:44] Story US-009 not yet complete, continuing...
[2026-01-11 15:58:44] Progress: 10/13 stories completed
[2026-01-11 15:58:46] === Iteration 20/50 ===
[2026-01-11 15:58:46] Working on story: US-009
[2026-01-11 15:58:46] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_20_US-009.log)
[2026-01-11 15:58:50] Story US-009 not yet complete, continuing...
[2026-01-11 15:58:50] Progress: 10/13 stories completed
[2026-01-11 15:58:52] === Iteration 21/50 ===
[2026-01-11 15:58:52] Working on story: US-009
[2026-01-11 15:58:52] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_21_US-009.log)
[2026-01-11 15:58:56] Story US-009 not yet complete, continuing...
[2026-01-11 15:58:56] Progress: 10/13 stories completed
[2026-01-11 15:58:58] === Iteration 22/50 ===
[2026-01-11 15:58:58] Working on story: US-009
[2026-01-11 15:58:58] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_22_US-009.log)
[2026-01-11 15:59:01] Story US-009 not yet complete, continuing...
[2026-01-11 15:59:01] Progress: 10/13 stories completed
[2026-01-11 15:59:03] === Iteration 23/50 ===
[2026-01-11 15:59:03] Working on story: US-009
[2026-01-11 15:59:03] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_23_US-009.log)
[2026-01-11 15:59:07] Story US-009 not yet complete, continuing...
[2026-01-11 15:59:07] Progress: 10/13 stories completed
[2026-01-11 15:59:09] === Iteration 24/50 ===
[2026-01-11 15:59:09] Working on story: US-009
[2026-01-11 15:59:09] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_24_US-009.log)
[2026-01-11 15:59:12] Story US-009 not yet complete, continuing...
[2026-01-11 15:59:12] Progress: 10/13 stories completed
[2026-01-11 15:59:14] === Iteration 25/50 ===
[2026-01-11 15:59:14] Working on story: US-009
[2026-01-11 15:59:14] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_25_US-009.log)
[2026-01-11 15:59:18] Story US-009 not yet complete, continuing...
[2026-01-11 15:59:18] Progress: 10/13 stories completed
[2026-01-11 15:59:20] === Iteration 26/50 ===
[2026-01-11 15:59:20] Working on story: US-009
[2026-01-11 15:59:20] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_26_US-009.log)
[2026-01-11 15:59:23] Story US-009 not yet complete, continuing...
[2026-01-11 15:59:23] Progress: 10/13 stories completed
[2026-01-11 15:59:25] === Iteration 27/50 ===
[2026-01-11 15:59:25] Working on story: US-009
[2026-01-11 15:59:25] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_27_US-009.log)
[2026-01-11 15:59:29] Story US-009 not yet complete, continuing...
[2026-01-11 15:59:29] Progress: 10/13 stories completed
[2026-01-11 15:59:31] === Iteration 28/50 ===
[2026-01-11 15:59:31] Working on story: US-009
[2026-01-11 15:59:31] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_28_US-009.log)
[2026-01-11 15:59:35] Story US-009 not yet complete, continuing...
[2026-01-11 15:59:35] Progress: 10/13 stories completed
[2026-01-11 15:59:37] === Iteration 29/50 ===
[2026-01-11 15:59:37] Working on story: US-009
[2026-01-11 15:59:37] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_29_US-009.log)
[2026-01-11 15:59:40] Story US-009 not yet complete, continuing...
[2026-01-11 15:59:40] Progress: 10/13 stories completed
[2026-01-11 15:59:42] === Iteration 30/50 ===
[2026-01-11 15:59:42] Working on story: US-009
[2026-01-11 15:59:42] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_30_US-009.log)
[2026-01-11 15:59:46] Story US-009 not yet complete, continuing...
[2026-01-11 15:59:46] Progress: 10/13 stories completed
[2026-01-11 15:59:48] === Iteration 31/50 ===
[2026-01-11 15:59:48] Working on story: US-009
[2026-01-11 15:59:48] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_31_US-009.log)
[2026-01-11 15:59:52] Story US-009 not yet complete, continuing...
[2026-01-11 15:59:52] Progress: 10/13 stories completed
[2026-01-11 15:59:54] === Iteration 32/50 ===
[2026-01-11 15:59:54] Working on story: US-009
[2026-01-11 15:59:54] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_32_US-009.log)
[2026-01-11 15:59:57] Story US-009 not yet complete, continuing...
[2026-01-11 15:59:57] Progress: 10/13 stories completed
[2026-01-11 15:59:59] === Iteration 33/50 ===
[2026-01-11 15:59:59] Working on story: US-009
[2026-01-11 15:59:59] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_33_US-009.log)
[2026-01-11 16:04:24] SUCCESS: Story US-009 passed!
[2026-01-11 16:04:24] Changes committed
[2026-01-11 16:04:24] Progress: 11/13 stories completed
[2026-01-11 16:04:26] === Iteration 34/50 ===
[2026-01-11 16:04:26] Working on story: US-008
[2026-01-11 16:04:26] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_34_US-008.log)
[2026-01-11 16:08:14] SUCCESS: Story US-008 passed!
[2026-01-11 16:08:14] Changes committed
[2026-01-11 16:08:14] Progress: 12/13 stories completed
[2026-01-11 16:08:16] === Iteration 35/50 ===
[2026-01-11 16:08:16] Working on story: US-013
[2026-01-11 16:08:16] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_35_US-013.log)
[2026-01-11 16:12:10] Story US-013 not yet complete, continuing...
[2026-01-11 16:12:10] Progress: 12/13 stories completed
[2026-01-11 16:12:12] === Iteration 36/50 ===
[2026-01-11 16:12:12] Working on story: US-013
[2026-01-11 16:12:12] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_36_US-013.log)
[2026-01-11 16:14:52] SUCCESS: Story US-013 passed!
[2026-01-11 16:14:52] Changes committed
[2026-01-11 16:14:52] Progress: 13/13 stories completed
[2026-01-11 16:14:54] === Iteration 37/50 ===
[2026-01-11 16:14:54] SUCCESS: All stories completed! 🎉
[2026-01-11 16:14:54] === Ralph Session Complete ===
[2026-01-11 16:14:54] Final progress: 13/13 stories completed
[2026-01-11 16:14:54] Branch: ralph/bulk-receipt-upload
[2026-01-11 16:14:54] Logs: /workspace/roa2web/scripts/ralph/logs
---
## Phase 2: Real-Time Progress în Listă
Started: 2026-01-11 19:40
Project: bulk-upload-realtime-progress
Branch: ralph/bulk-receipt-upload (CONTINUĂ - NU crea branch nou!)
PRD: tasks/prd-bulk-upload-realtime-progress.md
Această fază extinde bulk upload-ul din Phase 1 (US-001→US-013 COMPLETE) cu:
- Afișare fișiere imediat în tabel după upload
- Progres real-time pe măsură ce sunt procesate
- Cancel individual și batch
- Animații pentru status changes
### Noile User Stories (US-014 → US-023):
| ID | Title | Priority | Status |
|----|-------|----------|--------|
| US-014 | Backend - Cancel Job Individual | 14 | pending |
| US-015 | Backend - Cancel Batch Complet | 15 | pending |
| US-016 | Frontend - Store Actions Cancel | 16 | pending |
| US-017 | Frontend - Afișare Jobs în Tabel | 17 | pending |
| US-018 | Frontend - Tranziție Job→Receipt | 18 | pending |
| US-019 | Frontend - Animație Status Change | 19 | pending |
| US-020 | Frontend - Cancel Individual Row | 20 | pending |
| US-021 | Frontend - Cancel All în Header | 21 | pending |
| US-022 | Frontend - Checkbox Disabled Jobs | 22 | pending |
| US-023 | Frontend - Restore Jobs Refresh | 23 | pending |
---
(Ralph va adăuga aici progresul per iterație)
[2026-01-11 19:42:53] Starting Ralph for project: bulk-upload-realtime-progress
[2026-01-11 19:42:53] Max iterations: 50
[2026-01-11 19:42:53] === Iteration 1/50 ===
[2026-01-11 19:42:53] Working on story: US-014
[2026-01-11 19:42:53] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_1_US-014.log)
[2026-01-11 19:47:06] SUCCESS: Story US-014 passed!
[2026-01-11 19:47:07] Changes committed
[2026-01-11 19:47:07] Progress: 14/23 stories completed
[2026-01-11 19:47:09] === Iteration 2/50 ===
[2026-01-11 19:47:09] Working on story: US-015
[2026-01-11 19:47:09] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_2_US-015.log)
[2026-01-11 19:53:13] Story US-015 not yet complete, continuing...
[2026-01-11 19:53:13] Progress: 14/23 stories completed
[2026-01-11 19:53:15] === Iteration 3/50 ===
[2026-01-11 19:53:15] Working on story: US-015
[2026-01-11 19:53:15] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_3_US-015.log)
[2026-01-11 19:57:53] Starting Ralph for project: bulk-upload-realtime-progress
[2026-01-11 19:57:53] Max iterations: 50
[2026-01-11 19:57:53] === Iteration 1/50 ===
[2026-01-11 19:57:53] Working on story: US-015
[2026-01-11 19:57:53] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_1_US-015.log)
[2026-01-11 20:00:23] SUCCESS: Story US-015 passed!
[2026-01-11 20:00:23] Changes committed
[2026-01-11 20:00:23] Progress: 15/23 stories completed
[2026-01-11 20:00:25] === Iteration 2/50 ===
[2026-01-11 20:00:25] Working on story: US-016
[2026-01-11 20:00:25] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_2_US-016.log)
[2026-01-11 20:06:04] SUCCESS: Story US-016 passed!
[2026-01-11 20:06:04] Changes committed
[2026-01-11 20:06:04] Progress: 16/23 stories completed
[2026-01-11 20:06:06] === Iteration 3/50 ===
[2026-01-11 20:06:06] Working on story: US-017
[2026-01-11 20:06:06] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_3_US-017.log)
[2026-01-11 20:12:04] Story US-017 not yet complete, continuing...
[2026-01-11 20:12:04] Progress: 16/23 stories completed
[2026-01-11 20:12:06] === Iteration 4/50 ===
[2026-01-11 20:12:06] Working on story: US-017
[2026-01-11 20:12:06] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_4_US-017.log)
[2026-01-11 20:16:17] SUCCESS: Story US-017 passed!
[2026-01-11 20:16:17] Changes committed
[2026-01-11 20:16:17] Progress: 17/23 stories completed
[2026-01-11 20:16:19] === Iteration 5/50 ===
[2026-01-11 20:16:19] Working on story: US-018
[2026-01-11 20:16:19] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_5_US-018.log)
[2026-01-11 20:22:23] SUCCESS: Story US-018 passed!
[2026-01-11 20:22:23] Changes committed
[2026-01-11 20:22:23] Progress: 18/23 stories completed
[2026-01-11 20:22:25] === Iteration 6/50 ===
[2026-01-11 20:22:25] Working on story: US-019
[2026-01-11 20:22:25] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_6_US-019.log)
[2026-01-11 20:27:28] SUCCESS: Story US-019 passed!
[2026-01-11 20:27:28] Changes committed
[2026-01-11 20:27:28] Progress: 19/23 stories completed
[2026-01-11 20:27:30] === Iteration 7/50 ===
[2026-01-11 20:27:30] Working on story: US-020
[2026-01-11 20:27:30] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_7_US-020.log)
[2026-01-11 20:32:44] SUCCESS: Story US-020 passed!
[2026-01-11 20:32:44] Changes committed
[2026-01-11 20:32:44] Progress: 20/23 stories completed
[2026-01-11 20:32:46] === Iteration 8/50 ===
[2026-01-11 20:32:46] Working on story: US-021
[2026-01-11 20:32:46] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_8_US-021.log)
[2026-01-11 20:35:59] SUCCESS: Story US-021 passed!
[2026-01-11 20:35:59] Changes committed
[2026-01-11 20:35:59] Progress: 21/23 stories completed
[2026-01-11 20:36:01] === Iteration 9/50 ===
[2026-01-11 20:36:01] Working on story: US-022
[2026-01-11 20:36:01] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_9_US-022.log)
[2026-01-11 20:38:17] SUCCESS: Story US-022 passed!
[2026-01-11 20:38:17] Changes committed
[2026-01-11 20:38:17] Progress: 22/23 stories completed
[2026-01-11 20:38:19] === Iteration 10/50 ===
[2026-01-11 20:38:19] Working on story: US-023
[2026-01-11 20:38:19] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_10_US-023.log)
[2026-01-11 20:44:18] Story US-023 not yet complete, continuing...
[2026-01-11 20:44:18] Progress: 22/23 stories completed
[2026-01-11 20:44:20] === Iteration 11/50 ===
[2026-01-11 20:44:20] Working on story: US-023
[2026-01-11 20:44:20] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_11_US-023.log)
[2026-01-11 20:46:15] SUCCESS: Story US-023 passed!
[2026-01-11 20:46:15] Changes committed
[2026-01-11 20:46:15] Progress: 23/23 stories completed
[2026-01-11 20:46:17] === Iteration 12/50 ===
[2026-01-11 20:46:17] SUCCESS: All stories completed! 🎉
[2026-01-11 20:46:17] === Ralph Session Complete ===
[2026-01-11 20:46:17] Final progress: 23/23 stories completed
[2026-01-11 20:46:17] Branch: ralph/bulk-receipt-upload
[2026-01-11 20:46:17] Logs: /workspace/roa2web/scripts/ralph/logs
---
## Phase 3: Bulk Actions & SSE Real-time Updates
Started: 2026-01-11 21:50
Project: bulk-actions-sse-refresh
Branch: ralph/bulk-receipt-upload (CONTINUĂ - NU crea branch nou!)
PRD: tasks/prd-bulk-actions-sse-refresh.md
Această fază adaugă:
- Bulk Delete pentru bonuri selectate
- SSE (Server-Sent Events) pentru actualizări real-time
- Eliminare polling în favoarea SSE
- Graceful degradation cu fallback la polling
### Noile User Stories (US-024 → US-033):
| ID | Title | Priority | Status |
|----|-------|----------|--------|
| US-024 | Backend - Endpoint Bulk Delete | 24 | pending |
| US-025 | Frontend - Buton Șterge Bulk Actions | 25 | pending |
| US-026 | Frontend - Dialog Confirmare Ștergere | 26 | pending |
| US-027 | Frontend - Bulk Delete Partial Success | 27 | pending |
| US-028 | Frontend - Navigare Pagină Anterioară | 28 | pending |
| US-029 | Frontend - updateReceiptInPlace Store | 29 | pending |
| US-030 | Backend - SSE Endpoint Status Updates | 30 | pending |
| US-031 | Frontend - SSE Client Service | 31 | pending |
| US-032 | Frontend - Înlocuire Polling cu SSE | 32 | pending |
| US-033 | Frontend - Graceful Degradation SSE | 33 | pending |
---
(Ralph va adăuga aici progresul per iterație)
[2026-01-11 22:12:45] Starting Ralph for project: bulk-actions-sse-refresh
[2026-01-11 22:12:45] Max iterations: 50
[2026-01-11 22:12:45] === Iteration 1/50 ===
[2026-01-11 22:12:45] Working on story: US-024
[2026-01-11 22:12:45] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_1_US-024.log)
[2026-01-11 22:48:00] MANUAL: US-024 marked as passed (code complete, Claude timed out after 30+ minutes)
[2026-01-11 22:48:04] Starting Ralph for project: bulk-actions-sse-refresh
[2026-01-11 22:48:04] Max iterations: 50
[2026-01-11 22:48:04] === Iteration 1/50 ===
[2026-01-11 22:48:04] Working on story: US-025
[2026-01-11 22:48:04] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_1_US-025.log)
[2026-01-11 22:51:34] SUCCESS: Story US-025 passed!
[2026-01-11 22:51:34] Changes committed
[2026-01-11 22:51:34] Progress: 25/33 stories completed
[2026-01-11 22:51:36] === Iteration 2/50 ===
[2026-01-11 22:51:36] Working on story: US-026
[2026-01-11 22:51:36] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_2_US-026.log)
[2026-01-11 22:55:07] SUCCESS: Story US-026 passed!
[2026-01-11 22:55:07] Changes committed
[2026-01-11 22:55:07] Progress: 26/33 stories completed
[2026-01-11 22:55:09] === Iteration 3/50 ===
[2026-01-11 22:55:09] Working on story: US-027
[2026-01-11 22:55:09] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_3_US-027.log)
[2026-01-11 22:59:03] SUCCESS: Story US-027 passed!
[2026-01-11 22:59:03] Changes committed
[2026-01-11 22:59:03] Progress: 27/33 stories completed
[2026-01-11 22:59:05] === Iteration 4/50 ===
[2026-01-11 22:59:05] Working on story: US-028
[2026-01-11 22:59:05] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_4_US-028.log)
[2026-01-11 23:03:22] SUCCESS: Story US-028 passed!
[2026-01-11 23:03:22] Changes committed
[2026-01-11 23:03:22] Progress: 28/33 stories completed
[2026-01-11 23:03:24] === Iteration 5/50 ===
[2026-01-11 23:03:24] Working on story: US-029
[2026-01-11 23:03:24] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_5_US-029.log)
[2026-01-11 23:04:38] SUCCESS: Story US-029 passed!
[2026-01-11 23:04:38] Changes committed
[2026-01-11 23:04:38] Progress: 29/33 stories completed
[2026-01-11 23:04:40] === Iteration 6/50 ===
[2026-01-11 23:04:40] Working on story: US-030
[2026-01-11 23:04:40] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_6_US-030.log)
[2026-01-11 23:35:52] MANUAL: US-030 marked as passed (SSE endpoint complete, Claude timed out after 30+ minutes)
[2026-01-11 23:35:57] Starting Ralph for project: bulk-actions-sse-refresh
[2026-01-11 23:35:57] Max iterations: 50
[2026-01-11 23:35:57] === Iteration 1/50 ===
[2026-01-11 23:35:57] Working on story: US-031
[2026-01-11 23:35:57] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_1_US-031.log)
[2026-01-11 23:37:36] SUCCESS: Story US-031 passed!
[2026-01-11 23:37:36] Changes committed
[2026-01-11 23:37:36] Progress: 31/33 stories completed
[2026-01-11 23:37:38] === Iteration 2/50 ===
[2026-01-11 23:37:38] Working on story: US-032
[2026-01-11 23:37:38] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_2_US-032.log)
[2026-01-11 23:41:53] SUCCESS: Story US-032 passed!
[2026-01-11 23:41:53] Changes committed
[2026-01-11 23:41:53] Progress: 32/33 stories completed
[2026-01-11 23:41:55] === Iteration 3/50 ===
[2026-01-11 23:41:55] Working on story: US-033
[2026-01-11 23:41:55] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_3_US-033.log)
[2026-01-11 23:44:52] SUCCESS: Story US-033 passed!
[2026-01-11 23:44:53] Changes committed
[2026-01-11 23:44:53] Progress: 33/33 stories completed
[2026-01-11 23:44:55] === Iteration 4/50 ===
[2026-01-11 23:44:55] SUCCESS: All stories completed! 🎉
[2026-01-11 23:44:55] === Ralph Session Complete ===
[2026-01-11 23:44:55] Final progress: 33/33 stories completed
[2026-01-11 23:44:55] Branch: ralph/bulk-receipt-upload
[2026-01-11 23:44:55] Logs: /workspace/roa2web/scripts/ralph/logs
---
## Phase 2: Mobile UX Improvements
Started: 2026-01-12
### New User Stories Added:
- US-034: Fix - Refresh Individual vs Refresh Total
- US-035: Fix - Bonuri cu Eroare Rămân în Listă
- US-036: Afișare Nume Fișier pentru Toate Bonurile
- US-037: Fix - Upload Nu Mai Face Refresh Automat
- US-038: Mobile - Selecție Multiplă prin Long-Press
- US-039: Mobile - Select All și Buton Ștergere
- US-040: Mobile - Layout Android Nativ pentru Lista Bonuri
- US-041: Mobile - Layout Android pentru Editare/Creare Bon
- US-042: Mobile - Layout Android pentru Vizualizare Bon
- US-043: Păstrare Ordine Bonuri la Refresh
### Prioritization:
Faza 1 (Bug Fixes): US-034, US-035, US-037, US-043
Faza 2 (Mobile Enhancements): US-036, US-038, US-039
Faza 3 (Mobile Native Layout): US-040, US-041, US-042
[2026-01-12 07:34:34] Starting Ralph for project: mobile-ux-improvements
[2026-01-12 07:34:34] Max iterations: 50
[2026-01-12 07:34:34] === Iteration 1/50 ===
[2026-01-12 07:34:34] Working on story: US-034
[2026-01-12 07:34:34] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_1_US-034.log)
[2026-01-12 07:38:22] SUCCESS: Story US-034 passed!
[2026-01-12 07:38:22] Changes committed
[2026-01-12 07:38:22] Progress: 34/43 stories completed
[2026-01-12 07:38:24] === Iteration 2/50 ===
[2026-01-12 07:38:24] Working on story: US-035
[2026-01-12 07:38:24] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_2_US-035.log)
[2026-01-12 07:41:39] SUCCESS: Story US-035 passed!
[2026-01-12 07:41:39] Changes committed
[2026-01-12 07:41:39] Progress: 35/43 stories completed
[2026-01-12 07:41:41] === Iteration 3/50 ===
[2026-01-12 07:41:41] Working on story: US-036
[2026-01-12 07:41:41] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_3_US-036.log)
[2026-01-12 07:46:55] SUCCESS: Story US-036 passed!
[2026-01-12 07:46:56] Changes committed
[2026-01-12 07:46:56] Progress: 36/43 stories completed
[2026-01-12 07:46:58] === Iteration 4/50 ===
[2026-01-12 07:46:58] Working on story: US-037
[2026-01-12 07:46:58] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_4_US-037.log)
[2026-01-12 07:51:38] SUCCESS: Story US-037 passed!
[2026-01-12 07:51:38] Changes committed
[2026-01-12 07:51:38] Progress: 37/43 stories completed
[2026-01-12 07:51:40] === Iteration 5/50 ===
[2026-01-12 07:51:40] Working on story: US-038
[2026-01-12 07:51:40] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_5_US-038.log)
[2026-01-12 07:56:08] SUCCESS: Story US-038 passed!
[2026-01-12 07:56:08] Changes committed
[2026-01-12 07:56:08] Progress: 38/43 stories completed
[2026-01-12 07:56:10] === Iteration 6/50 ===
[2026-01-12 07:56:10] Working on story: US-039
[2026-01-12 07:56:10] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_6_US-039.log)
[2026-01-12 08:00:37] SUCCESS: Story US-039 passed!
[2026-01-12 08:00:37] Changes committed
[2026-01-12 08:00:38] Progress: 39/43 stories completed
[2026-01-12 08:00:40] === Iteration 7/50 ===
[2026-01-12 08:00:40] Working on story: US-040
[2026-01-12 08:00:40] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_7_US-040.log)
[2026-01-12 08:06:20] SUCCESS: Story US-040 passed!
[2026-01-12 08:06:20] Changes committed
[2026-01-12 08:06:20] Progress: 40/43 stories completed
[2026-01-12 08:06:22] === Iteration 8/50 ===
[2026-01-12 08:06:22] Working on story: US-041
[2026-01-12 08:06:22] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_8_US-041.log)
[2026-01-12 08:11:12] SUCCESS: Story US-041 passed!
[2026-01-12 08:11:12] Changes committed
[2026-01-12 08:11:12] Progress: 41/43 stories completed
[2026-01-12 08:11:14] === Iteration 9/50 ===
[2026-01-12 08:11:14] Working on story: US-042
[2026-01-12 08:11:14] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_9_US-042.log)
[2026-01-12 08:14:53] SUCCESS: Story US-042 passed!
[2026-01-12 08:14:53] Changes committed
[2026-01-12 08:14:53] Progress: 42/43 stories completed
[2026-01-12 08:14:55] === Iteration 10/50 ===
[2026-01-12 08:14:55] Working on story: US-043
[2026-01-12 08:14:55] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_10_US-043.log)
[2026-01-12 08:20:33] SUCCESS: Story US-043 passed!
[2026-01-12 08:20:33] Changes committed
[2026-01-12 08:20:33] Progress: 43/43 stories completed
[2026-01-12 08:20:35] === Iteration 11/50 ===
[2026-01-12 08:20:35] SUCCESS: All stories completed! 🎉
[2026-01-12 08:20:35] === Ralph Session Complete ===
[2026-01-12 08:20:35] Final progress: 43/43 stories completed
[2026-01-12 08:20:35] Branch: ralph/bulk-receipt-upload
[2026-01-12 08:20:35] Logs: /workspace/roa2web/scripts/ralph/logs
Mon Jan 12 09:44:54 AM UTC 2026
[2026-01-12 09:45:00] Starting Ralph for project: unified-mobile-material-design
[2026-01-12 09:45:00] Max iterations: 100
[2026-01-12 09:45:00] Creating new branch: ralph/unified-mobile-md
[2026-01-12 09:45:00] === Iteration 1/100 ===
[2026-01-12 09:45:00] Working on story: US-101a
[2026-01-12 09:45:00] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_1_US-101a.log)
[2026-01-12 09:46:30] SUCCESS: Story US-101a passed!

View File

@@ -0,0 +1,218 @@
<template>
<header class="mobile-top-bar" :class="{ 'selection-active': selectionActive }">
<!-- Left section -->
<div class="top-bar-left">
<Button
v-if="showBack"
icon="pi pi-arrow-left"
text
rounded
class="top-bar-btn"
@click="$emit('back-click')"
aria-label="Înapoi"
/>
<Button
v-else-if="showMenu"
icon="pi pi-bars"
text
rounded
class="top-bar-btn"
@click="$emit('menu-click')"
aria-label="Meniu"
/>
</div>
<!-- Title section -->
<h1 class="top-bar-title">{{ title }}</h1>
<!-- Right section - actions -->
<div class="top-bar-right">
<Button
v-for="(action, index) in actions"
:key="index"
:icon="action.icon"
text
rounded
class="top-bar-btn"
:class="{ 'active': action.active }"
@click="$emit('action-click', action)"
:aria-label="action.label || action.icon"
v-tooltip.bottom="action.tooltip"
/>
</div>
</header>
</template>
<script setup>
import Button from 'primevue/button'
/**
* MobileTopBar - Material Design 3 inspired top navigation bar for mobile views
*
* Props:
* - title: The text displayed in the center of the bar
* - showBack: Shows back arrow button on the left
* - showMenu: Shows hamburger menu button on the left (ignored if showBack is true)
* - actions: Array of action buttons for the right side
* - selectionActive: Whether selection mode is active (changes background)
*
* Events:
* - menu-click: Emitted when menu button is clicked
* - back-click: Emitted when back button is clicked
* - action-click: Emitted when any action button is clicked, passes action object
*/
defineProps({
/**
* Title displayed in the center of the top bar
*/
title: {
type: String,
default: ''
},
/**
* Whether to show the back arrow button
*/
showBack: {
type: Boolean,
default: false
},
/**
* Whether to show the hamburger menu button
* (ignored if showBack is true)
*/
showMenu: {
type: Boolean,
default: false
},
/**
* Array of action buttons for the right side
* Each action should have: { icon: string, label?: string, tooltip?: string, active?: boolean }
*/
actions: {
type: Array,
default: () => []
},
/**
* Whether selection mode is active (changes background color)
*/
selectionActive: {
type: Boolean,
default: false
}
})
defineEmits(['menu-click', 'back-click', 'action-click'])
</script>
<style scoped>
/* ================================================
MobileTopBar Component Styles
Material Design 3 inspired mobile navigation
================================================ */
.mobile-top-bar {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 56px;
background: var(--surface-card);
border-bottom: 1px solid var(--surface-border);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 var(--space-xs);
z-index: 1000;
box-shadow: var(--shadow-sm);
}
/* Selection mode active state */
.mobile-top-bar.selection-active {
background: var(--blue-50);
border-bottom-color: var(--blue-200);
}
/* Left and right button containers */
.top-bar-left,
.top-bar-right {
display: flex;
align-items: center;
gap: var(--space-xs);
/* Ensure minimum width for consistent layout */
min-width: 48px;
}
/* Individual action buttons */
.top-bar-btn {
width: 48px;
height: 48px;
border-radius: var(--radius-full);
color: var(--text-color);
}
/* Active state for buttons (e.g., filter active) */
.top-bar-btn.active {
color: var(--color-primary);
background: var(--blue-50);
}
/* Title in the center */
.top-bar-title {
font-size: var(--text-lg);
font-weight: var(--font-semibold);
color: var(--text-color);
margin: 0;
flex: 1;
text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* ================================================
Dark Mode Support
================================================ */
/* Manual dark mode via data-theme attribute */
[data-theme="dark"] .mobile-top-bar {
background: var(--surface-card);
border-bottom-color: var(--surface-border);
}
[data-theme="dark"] .mobile-top-bar.selection-active {
background: var(--blue-900);
border-bottom-color: var(--blue-700);
}
[data-theme="dark"] .top-bar-btn {
color: var(--text-color);
}
[data-theme="dark"] .top-bar-btn.active {
color: var(--blue-400);
background: var(--blue-900);
}
/* Auto dark mode (when no manual theme is set) */
@media (prefers-color-scheme: dark) {
:root:not([data-theme]) .mobile-top-bar {
background: var(--surface-card);
border-bottom-color: var(--surface-border);
}
:root:not([data-theme]) .mobile-top-bar.selection-active {
background: var(--blue-900);
border-bottom-color: var(--blue-700);
}
:root:not([data-theme]) .top-bar-btn {
color: var(--text-color);
}
:root:not([data-theme]) .top-bar-btn.active {
color: var(--blue-400);
background: var(--blue-900);
}
}
</style>

View File

@@ -0,0 +1,457 @@
# PRD: Unified Mobile Material Design Interface
## 1. Introducere
Aplicația ROA2WEB are deja implementată o interfață Material Design nativă Android pentru modulul de bonuri (ReceiptsListView). Acest PRD definește extinderea aceleiași interfețe unitare la toate paginile aplicației în modul mobil: Dashboard, Facturi, Balanță de Verificare, Trezorerie, Statistici, Loguri etc.
Scopul este o experiență consistentă cross-module pe dispozitive mobile, cu header/footer unitar și pattern-uri de interacțiune identice.
## 2. Obiective
### Obiectiv Principal
- **Consistență UI/UX**: Toate paginile mobile să aibă aceeași structură vizuală și pattern-uri de interacțiune ca lista de bonuri
### Obiective Secundare
- Eliminare cod duplicat (buton delete în header când există în footer)
- Adăugare navigare back (← Înapoi) în editare bonuri
- Dashboard cu carousel swipeable pentru KPI cards
- MD3 color tokens pentru theming consistent
### Metrici de Succes
- 100% consistență vizuală între pagini pe mobil
- Reducere CSS duplicat cu minim 30%
- Zero regresii vizuale în modul desktop
- Funcționare pe Android Chrome, iOS Safari, Samsung Internet
## 3. User Stories
### US-101: Creare sistem de componente Material Design reutilizabile
**Ca** developer
**Vreau** componente Vue reutilizabile pentru elementele MD comune
**Pentru că** să evit duplicarea codului și să asigur consistența
**Acceptance Criteria:**
- [ ] Componentă `MobileTopBar.vue` extrasă din ReceiptsListView
- [ ] Componentă `MobileBottomNav.vue` extrasă din ReceiptsListView
- [ ] Componentă `MobileSelectionFooter.vue` pentru batch actions
- [ ] Componentă `MobileFilterChips.vue` pentru filtre orizontale scrollable
- [ ] Componentă `BottomSheet.vue` pentru filtre avansate
- [ ] Props pentru customizare (title, actions, items)
- [ ] npm run build passes
### US-102: Definire MD3 color tokens în CSS
**Ca** developer
**Vreau** variabile CSS pentru Material Design 3 color system
**Pentru că** să am culori consistente și dark mode automatic
**Acceptance Criteria:**
- [ ] Fișier `src/assets/css/core/md3-tokens.css` creat
- [ ] Tokens pentru: primary, secondary, surface, on-surface, outline
- [ ] Dark mode variants cu `[data-theme="dark"]`
- [ ] Mapping la tokens existenți unde posibil
- [ ] Import în main.css
- [ ] npm run build passes
### US-103: Refactor ReceiptsListView să folosească componente comune
**Ca** developer
**Vreau** ReceiptsListView să folosească noile componente
**Pentru că** să validez API-ul componentelor și să reduc duplicarea
**Acceptance Criteria:**
- [ ] Import MobileTopBar, MobileBottomNav, MobileSelectionFooter
- [ ] Ștergere CSS duplicat din ReceiptsListView
- [ ] Funcționalitate identică cu implementarea actuală
- [ ] Verify in browser că lista bonuri funcționează identic pe mobil
- [ ] npm run build passes
### US-104: Eliminare buton delete duplicat din header tabel bonuri
**Ca** utilizator
**Vreau** să văd butonul de ștergere doar în footer când am selecție activă
**Pentru că** afișarea în ambele locuri e confuză și ocupă spațiu inutil
**Acceptance Criteria:**
- [ ] Desktop: bulk-actions-bar NU mai afișează buton Șterge când e pe mobil
- [ ] Desktop: bulk-actions-bar afișează doar când `!isMobile`
- [ ] Mobile: delete apare DOAR în `mobile-selection-bottom-bar`
- [ ] Verify in browser: pe mobil, delete e doar în footer
- [ ] Verify in browser: pe desktop, delete e în header table
- [ ] npm run build passes
### US-105: Adăugare buton Înapoi în editare bon (mobile)
**Ca** utilizator
**Vreau** să pot reveni la lista de bonuri din pagina de editare
**Pentru că** navigarea back prin browser nu e intuitivă pe mobil
**Acceptance Criteria:**
- [ ] MobileTopBar în ReceiptCreateView/ReceiptCreateUnifiedView
- [ ] Iconiță ← în stânga header-ului
- [ ] Click pe ← navighează la `/data-entry`
- [ ] Title: "Editare Bon" sau "Bon nou" în funcție de context
- [ ] Verify in browser: butonul funcționează corect
- [ ] npm run build passes
### US-106: Dashboard Mobile cu Swipeable KPI Cards
**Ca** utilizator
**Vreau** să văd KPI-urile ca un carousel swipeable pe mobil
**Pentru că** cardurile stacked ocupă prea mult spațiu vertical
**Acceptance Criteria:**
- [ ] Componentă `SwipeableCards.vue` creată
- [ ] Dots indicator pentru poziție
- [ ] Touch swipe left/right funcțional
- [ ] DashboardView.vue folosește componenta pe mobil
- [ ] Fallback la layout normal pe desktop
- [ ] Verify in browser: swipe funcționează pe touch devices
- [ ] npm run build passes
### US-107: InvoicesView Mobile Material Design Header
**Ca** utilizator
**Vreau** pagina de facturi să aibă același header Material ca lista bonuri
**Pentru că** interfața trebuie să fie unitară
**Acceptance Criteria:**
- [ ] MobileTopBar cu title "Facturi"
- [ ] App Bar Actions: refresh, export în header (iconițe dreapta)
- [ ] MobileBottomNav identic cu cel din bonuri
- [ ] Filtre în Bottom Sheet (nu inline pe mobil)
- [ ] Verify in browser: header arată ca la bonuri
- [ ] npm run build passes
### US-108: TrialBalanceView Mobile Material Design Header
**Ca** utilizator
**Vreau** pagina de balanță să aibă același header Material
**Pentru că** interfața trebuie să fie unitară
**Acceptance Criteria:**
- [ ] MobileTopBar cu title "Balanță de Verificare"
- [ ] App Bar Actions: export în header
- [ ] MobileBottomNav identic
- [ ] Bottom Sheet pentru filtre complexe (perioadă, conturi)
- [ ] Verify in browser că arată consistent
- [ ] npm run build passes
### US-109: BankCashRegisterView (Trezorerie) Mobile Material Design
**Ca** utilizator
**Vreau** pagina de trezorerie să aibă același header Material
**Pentru că** interfața trebuie să fie unitară
**Acceptance Criteria:**
- [ ] MobileTopBar cu title "Trezorerie"
- [ ] App Bar Actions: refresh, export
- [ ] MobileBottomNav identic
- [ ] Bottom Sheet pentru filtre
- [ ] Verify in browser
- [ ] npm run build passes
### US-110: ServerLogsView Mobile Material Design
**Ca** admin
**Vreau** pagina de loguri să aibă interfață Material
**Pentru că** și paginile admin trebuie să fie unitare
**Acceptance Criteria:**
- [ ] MobileTopBar cu title "Loguri Server"
- [ ] App Bar Actions: refresh, export
- [ ] MobileBottomNav (fără link-ul de setări OCR pentru non-admin)
- [ ] Verify in browser
- [ ] npm run build passes
### US-111: CacheStatsView Mobile Material Design
**Ca** admin
**Vreau** pagina de statistici cache să aibă interfață Material
**Pentru că** și paginile admin trebuie să fie unitare
**Acceptance Criteria:**
- [ ] MobileTopBar cu title "Statistici Cache"
- [ ] App Bar Actions: refresh
- [ ] MobileBottomNav
- [ ] Verify in browser
- [ ] npm run build passes
### US-112: Bottom Sheet Component pentru Filtre
**Ca** utilizator
**Vreau** să pot accesa filtrele într-un bottom sheet pe mobil
**Pentru că** ocupă mai puțin spațiu și e pattern MD familiar
**Acceptance Criteria:**
- [ ] Componentă `BottomSheet.vue` cu animație slide-up
- [ ] Drag handle în partea de sus
- [ ] Close pe tap outside sau swipe down
- [ ] Slot pentru conținut
- [ ] Suport pentru visible v-model
- [ ] Verify in browser: animația e smooth
- [ ] npm run build passes
### US-113: Batch Actions per Module (contextuale)
**Ca** utilizator
**Vreau** acțiuni batch diferite în funcție de modulul curent
**Pentru că** bonurile au ștergere, facturile au export+print, balanța doar export
**Acceptance Criteria:**
- [ ] MobileSelectionFooter acceptă prop `actions` array
- [ ] Bonuri: Delete button (existent) + Export
- [ ] Facturi: Export + Print
- [ ] Balanță: Export only
- [ ] Verify in browser pentru fiecare modul
- [ ] npm run build passes
### US-114: Integrare și testare completă
**Ca** QA
**Vreau** toate paginile testate pe multiple device-uri
**Pentru că** trebuie să fim siguri că nu sunt regresii
**Acceptance Criteria:**
- [ ] Test pe Chrome Android (real device sau emulator)
- [ ] Test pe Safari iOS (simulator sau real)
- [ ] Test desktop Chrome/Firefox/Safari
- [ ] Verify dark mode funcționează peste tot
- [ ] Verify landscape orientation
- [ ] npm run build passes
- [ ] Zero erori în console
---
## User Stories: Documentație și Reguli (Faza 7)
### US-115: Actualizare CSS_PATTERNS.md cu Mobile Material Design
**Ca** developer viitor
**Vreau** documentație completă pentru pattern-urile Mobile Material Design
**Pentru că** să pot implementa pagini noi consistent fără a citi codul existent
**Acceptance Criteria:**
- [ ] Secțiune nouă "Mobile Material Design Components" în CSS_PATTERNS.md
- [ ] Documentat MobileTopBar: structură HTML, clase CSS, props
- [ ] Documentat MobileBottomNav: linkuri, iconițe, structură
- [ ] Documentat MobileSelectionFooter: când se afișează, acțiuni
- [ ] Documentat BottomSheet: usage, v-model, slots
- [ ] Documentat SwipeableCards: props, dots indicator
- [ ] Exemple de cod copy-paste ready pentru fiecare componentă
- [ ] Screenshots/ASCII diagrams pentru layout-uri
### US-116: Actualizare DESIGN_TOKENS.md cu MD3 Tokens
**Ca** developer viitor
**Vreau** documentație pentru noile MD3 color tokens
**Pentru că** să știu ce variabile CSS să folosesc pentru Material Design
**Acceptance Criteria:**
- [ ] Secțiune nouă "Material Design 3 Tokens" în DESIGN_TOKENS.md
- [ ] Tabel cu toate variabilele MD3: `--md-sys-color-*`
- [ ] Mapping la tokens existenți (ex: `--md-sys-color-primary``--primary-color`)
- [ ] Exemple dark mode: cum se schimbă valorile
- [ ] Reguli când să folosești MD3 tokens vs tokens existenți
- [ ] Color swatches vizuale (ASCII sau referință la Figma)
### US-117: Actualizare CLAUDE.md cu Reguli Mobile Development
**Ca** Claude Code (AI)
**Vreau** reguli clare în CLAUDE.md pentru dezvoltare mobilă
**Pentru că** să respect pattern-urile stabilite la generarea automată de cod
**Acceptance Criteria:**
- [ ] Secțiune nouă "### Mobile Development Rules" în CLAUDE.md
- [ ] Regulă: "Toate paginile mobile TREBUIE să folosească MobileTopBar"
- [ ] Regulă: "Filtrele pe mobil se pun în BottomSheet, NU inline"
- [ ] Regulă: "Selecția multiplă afișează acțiuni în footer, NU în header"
- [ ] Regulă: "Touch targets minim 44x44px"
- [ ] Regulă: "Folosește isMobile composable pentru condiționare"
- [ ] Checklist rapid pentru code review mobile
### US-118: Creare MOBILE_PATTERNS.md dedicat
**Ca** developer
**Vreau** un fișier dedicat pentru documentația mobilă
**Pentru că** e prea mult pentru CSS_PATTERNS.md și merită propriul fișier
**Acceptance Criteria:**
- [ ] Fișier `docs/MOBILE_PATTERNS.md` creat
- [ ] Table of Contents cu linkuri rapide
- [ ] Secțiuni pentru fiecare componentă
- [ ] Secțiune "Quick Start" pentru devs noi
- [ ] Secțiune "Do's and Don'ts" cu exemple concrete
- [ ] Diagrame ASCII pentru layout mobile
- [ ] Linkuri către fișierele sursă componente
- [ ] Referință adăugată în CSS_PATTERNS.md și CLAUDE.md
### US-119: Actualizare claude-learn-frontend.md cu Pattern-uri Noi
**Ca** Claude Code (AI)
**Vreau** pattern-urile noi salvate în memoria Claude
**Pentru că** să le pot aplica automat în sesiuni viitoare
**Acceptance Criteria:**
- [ ] Pattern nou: "Mobile Material Design Component Architecture"
- [ ] Pattern nou: "Bottom Sheet Filter Pattern"
- [ ] Pattern nou: "Mobile Selection Mode Flow"
- [ ] Gotcha: "Nu duplica delete button în header și footer"
- [ ] Gotcha: "Filtrele mobile trebuie în BottomSheet"
- [ ] Format corect cu @date și tags
### US-120: Validare Documentație cu Fresh Developer Test
**Ca** lead developer
**Vreau** să validez că documentația e suficientă
**Pentru că** să mă asigur că un dev nou poate implementa o pagină mobilă fără ajutor
**Acceptance Criteria:**
- [ ] Un developer (sau AI) poate crea o pagină mobilă nouă doar din docs
- [ ] Toate componentele sunt documentate cu exemple
- [ ] Nu sunt referințe circulare sau documente lipsă
- [ ] Documentația e consistentă între fișiere
- [ ] npm run build passes după ce dev-ul urmează docs
## 4. Cerințe Funcționale
1. [REQ-001] Toate paginile mobile trebuie să aibă MobileTopBar cu structură identică
2. [REQ-002] MobileBottomNav trebuie să fie consistent între pagini (aceleași linkuri)
3. [REQ-003] Selecția multiplă trebuie să folosească footer fix, nu header tabel
4. [REQ-004] Filtrele pe mobil trebuie să fie în Bottom Sheet, nu inline
5. [REQ-005] App Bar Actions (iconițe) trebuie să respecte ordinea: refresh, export, print, more
6. [REQ-006] Dashboard KPI cards trebuie să fie swipeable cu dots indicator
7. [REQ-007] Navigarea înapoi trebuie să fie vizibilă în app bar pentru pagini secundare
8. [REQ-008] MD3 tokens trebuie să suporte toate cele 3 theme modes (auto/light/dark)
9. [REQ-009] Documentația trebuie actualizată ÎNAINTE de merge în main
10. [REQ-010] CLAUDE.md trebuie să conțină reguli explicite pentru mobile development
11. [REQ-011] Orice developer nou trebuie să poată implementa o pagină mobilă doar din documentație
## 5. Non-Goals (Ce NU facem)
- ❌ Nu modificăm layout-ul desktop (doar mobile)
- ❌ Nu schimbăm structura tabelelor existente (sunt deja optimizate)
- ❌ Nu implementăm gesture navigation nativă (back swipe) - folosim buton explicit
- ❌ Nu adăugăm animații complexe (transform, spring physics)
- ❌ Nu diferențiem comportamentul iOS vs Android (identic cross-platform)
- ❌ Nu modificăm logica backend sau API calls
- ❌ Nu implementăm pull-to-refresh (complexitate excesivă)
## 6. Considerații Tehnice
### Stack/Tehnologii
- Vue 3 Composition API
- PrimeVue components (Button, Dialog, Menu, Sidebar)
- CSS custom cu design tokens
- Touch events native (touchstart, touchmove, touchend)
### Patterns de Urmat
- Pattern existent din `ReceiptsListView.vue` (US-038, US-039, US-040)
- CSS tokens din `src/assets/css/core/tokens.css`
- Mobile breakpoint: `max-width: 768px`
- Touch target minimum: 44px
### Dependențe
- `useMediaQuery` composable pentru `isMobile`
- Router pentru navigare
- Design tokens existenți
### Riscuri Tehnice
- **Risc mediu**: Bottom Sheet poate avea probleme cu keyboard pe iOS
- Mitigare: Testare extensivă, fallback la modal simplu
- **Risc scăzut**: Swipe carousel poate conflicta cu scroll lateral pe tabele
- Mitigare: Disable swipe când e în tabel
## 7. Considerații UI/UX
### Layout Structure (Mobile)
```
┌─────────────────────────────────────┐
│ [☰/←] Title [🔍][⚙][⋮]│ ← MobileTopBar
├─────────────────────────────────────┤
│ [Filter Chips scrollable...] │ ← Optional, per page
├─────────────────────────────────────┤
│ │
│ Page Content │
│ (scrollable) │
│ │
├─────────────────────────────────────┤
│ [🏠] [📤] [📊] [⚙] │ ← MobileBottomNav
└─────────────────────────────────────┘
OR (when selection active):
├─────────────────────────────────────┤
│ [🗑️ Șterge selectate] │ ← MobileSelectionFooter
└─────────────────────────────────────┘
```
### Stări (States)
- **Normal**: Header cu hamburger, content, bottom nav
- **Selection Mode**: Header cu X și count, content cu checkmarks, footer cu delete
- **Filters Open**: Bottom sheet overlay cu filtre
- **Loading**: Skeleton sau spinner în content area
- **Empty**: Empty state centrat în content area
- **Error**: Toast notification
### Accesibilitate
- Touch targets minim 44x44px
- Focus visible pentru keyboard navigation
- ARIA labels pe butoanele icon-only
- Color contrast minim 4.5:1
## 8. Success Metrics
- **Consistență vizuală**: 100% pagini cu header identic
- **Code reuse**: Min 70% CSS din componente comune
- **Performance**: First Contentful Paint < 1.5s pe 3G
- **Bundle size**: Creștere max 10KB gzipped
## 9. Open Questions
- [ ] TelegramView are nevoie de interfață Material? (e admin-only)
- [ ] Cum gestionăm paginile cu mai mult de 4 acțiuni în app bar?
- [ ] OCRMetricsView necesită bottom nav sau e pagină secundară?
---
## Implementare Sugerată (Ordinea User Stories)
### Faza 1: Fundație (US-101, US-102)
1. Extract componente comune
2. Definire MD3 tokens
### Faza 2: Refactor Bonuri (US-103, US-104, US-105)
3. Validare API componente pe bonuri
4. Fix buton delete duplicat
5. Adaugă navigare înapoi
### Faza 3: Dashboard (US-106)
6. Implement swipeable cards
### Faza 4: Reports Module (US-107, US-108, US-109, US-110, US-111)
7. Apply pattern la fiecare pagină Reports
### Faza 5: Shared Components (US-112, US-113)
8. Bottom Sheet pentru filtre
9. Batch actions contextuale
### Faza 6: Testing (US-114)
10. Cross-device testing
### Faza 7: Documentație (US-115, US-116, US-117, US-118, US-119, US-120)
11. Creare MOBILE_PATTERNS.md dedicat
12. Actualizare CSS_PATTERNS.md cu referințe
13. Actualizare DESIGN_TOKENS.md cu MD3
14. Actualizare CLAUDE.md cu reguli mobile
15. Actualizare claude-learn-frontend.md cu patterns
16. Validare documentație cu fresh developer test
---
## Anexa A: Fișiere de Documentație Afectate
| Fișier | Tip Modificare | User Story |
|--------|---------------|------------|
| `docs/MOBILE_PATTERNS.md` | NOU | US-118 |
| `docs/CSS_PATTERNS.md` | Update | US-115 |
| `docs/DESIGN_TOKENS.md` | Update | US-116 |
| `CLAUDE.md` | Update | US-117 |
| `.claude/rules/claude-learn-frontend.md` | Update | US-119 |
## Anexa B: Structura Documentație Finală
```
docs/
├── MOBILE_PATTERNS.md # NOU - ghid complet mobile MD
├── CSS_PATTERNS.md # + link la MOBILE_PATTERNS.md
├── DESIGN_TOKENS.md # + secțiune MD3 tokens
├── ONBOARDING_CSS.md # + menționare mobile patterns
└── ...
CLAUDE.md # + secțiune Mobile Development Rules
.claude/rules/
└── claude-learn-frontend.md # + patterns & gotchas noi
```