# PRD: Bulk Actions & SSE Real-time Status Updates ## Branch de lucru > **IMPORTANT**: Toate implementările din acest PRD trebuie făcute în branch-ul existent: > `ralph/bulk-receipt-upload` --- ## 1. Introducere Pagina de listă bonuri fiscale (`ReceiptsListView.vue`) are deja funcționalitate de selecție multiplă (select all și individual), dar lipsesc acțiunile bulk (în special ștergerea). De asemenea, actualizarea statusului bonurilor la bulk import se face prin polling periodic care reîncarcă toată lista, ceea ce creează o experiență vizuală neplăcută (flicker, pierdere scroll, etc.). Acest PRD adresează: 1. **Bulk Delete** - posibilitatea de a șterge mai multe bonuri odată 2. **SSE Real-time Updates** - actualizări incrementale ale statusului în loc de refresh total --- ## 2. Obiective ### Obiectiv Principal - Permiterea ștergerii în masă a bonurilor selectate cu UX fluid ### Obiective Secundare - Eliminarea refresh-ului total al paginii la actualizarea statusului bonurilor - Actualizări real-time via SSE pentru starea bonurilor în procesare - Experiență vizuală smooth fără flicker ### Metrici de Succes - Ștergerea a 10+ bonuri să dureze <2s - Nicio reîncărcare completă a listei la update status - SSE să reducă request-urile de polling cu 80%+ --- ## 3. User Stories ### US-001: Buton Șterge în Bulk Actions Bar **Ca** utilizator **Vreau** să văd un buton "Șterge" când am selecții **Pentru că** vreau să pot șterge mai multe bonuri odată **Acceptance Criteria:** - [ ] Butonul "Șterge" apare în bulk actions bar când `selectedReceipts.length > 0` - [ ] Butonul e vizibil lângă "Validează selectate" și "Deselectează" în header inline - [ ] Butonul are icon `pi-trash` și severity `danger` - [ ] Bonurile în procesare (pending/processing) NU sunt selectabile (deja implementat - verifică că funcționează) - [ ] npm run typecheck passes - [ ] Verify in browser: butonul apare doar când există selecții --- ### US-002: Dialog Confirmare Ștergere Bulk **Ca** utilizator **Vreau** o confirmare simplă înainte de ștergere **Pentru că** vreau să evit ștergeri accidentale **Acceptance Criteria:** - [ ] La click pe "Șterge", apare dialog cu mesaj: "Ești sigur că vrei să ștergi {N} bonuri?" - [ ] Dialog-ul are 2 butoane: "Anulează" (secondary) și "Șterge" (danger) - [ ] Dialog-ul folosește componenta PrimeVue `ConfirmDialog` sau `Dialog` - [ ] npm run typecheck passes - [ ] Verify in browser: dialogul apare cu numărul corect de bonuri --- ### US-003: Backend Endpoint Bulk Delete **Ca** frontend **Vreau** să pot trimite o listă de ID-uri pentru ștergere **Pentru că** e mai eficient decât request-uri individuale **Acceptance Criteria:** - [ ] Endpoint `DELETE /api/data-entry/receipts/bulk` acceptă body: `{ "ids": [1, 2, 3] }` - [ ] Validează că fiecare bon e în status DRAFT și creat de user-ul curent - [ ] Returnează partial success: `{ "deleted": [1, 2], "failed": [{"id": 3, "error": "..."}] }` - [ ] Șterge atașamentele și înregistrările contabile asociate - [ ] Bonurile în procesare nu pot fi șterse - returnează eroare specifică - [ ] npm run typecheck passes (pentru Python: ruff check) --- ### US-004: Frontend Bulk Delete cu Partial Success **Ca** utilizator **Vreau** să văd rezultatul ștergerii **Pentru că** vreau să știu dacă toate bonurile au fost șterse **Acceptance Criteria:** - [ ] După ștergere, toast arată rezultatul: "X bonuri șterse" sau "X din Y șterse, Z au eșuat" - [ ] Bonurile șterse dispar instant din listă (fără animație per specificații) - [ ] Lista se actualizează local (nu re-fetch complet dacă nu e necesar) - [ ] Stats se actualizează după ștergere - [ ] Selecția se golește după ștergere - [ ] npm run typecheck passes - [ ] Verify in browser: bonurile dispar instant, toast corect --- ### US-005: Navigare la Pagina Anterioară când Lista Devine Goală **Ca** utilizator **Vreau** să fiu redirecționat automat la pagina anterioară **Pentru că** nu vreau să văd o pagină goală **Acceptance Criteria:** - [ ] După bulk delete, dacă pagina curentă devine goală și există pagini anterioare, navighează la pagina anterioară - [ ] Dacă eram pe pagina 1 și devine goală, afișează empty state - [ ] npm run typecheck passes - [ ] Verify in browser: navigare automată funcționează --- ### US-006: Backend SSE Endpoint pentru Status Updates **Ca** frontend **Vreau** să primesc notificări real-time despre schimbări de status **Pentru că** vreau să actualizez UI-ul fără polling **Acceptance Criteria:** - [ ] Endpoint `GET /api/data-entry/receipts/sse/status` returnează Server-Sent Events - [ ] Conexiunea e persistent și trimite evenimente la schimbări de status - [ ] Format eveniment: `data: {"receipt_id": 123, "status": "completed", "processing_status": "completed"}` - [ ] Suport pentru filtrare pe batch_id (query param) - [ ] Include timeout handling și reconnect hints - [ ] ruff check passes --- ### US-007: Frontend SSE Client pentru Status Updates **Ca** frontend **Vreau** să mă conectez la SSE și să actualizez rândurile individual **Pentru că** vreau actualizări smooth fără reload **Acceptance Criteria:** - [ ] Creează serviciu SSE în `src/modules/data-entry/services/sseService.js` - [ ] Conectare automată când există bonuri în procesare în listă - [ ] La primire eveniment, actualizează doar rândul afectat (nu toată lista) - [ ] Deconectare automată când nu mai sunt bonuri în procesare - [ ] Retry logic cu exponential backoff la disconnect - [ ] npm run typecheck passes - [ ] Verify in browser: statusul se actualizează în timp real fără flicker --- ### US-008: Înlocuire Polling cu SSE **Ca** frontend **Vreau** să folosesc SSE în loc de polling interval **Pentru că** e mai eficient și mai smooth **Acceptance Criteria:** - [ ] Elimină `setInterval` pentru auto-refresh când SSE e activ - [ ] Fallback la polling dacă SSE nu e disponibil (WebSocket blocked, etc.) - [ ] Când toate bonurile din batch sunt procesate, închide conexiunea SSE - [ ] Logging pentru debug (console.log la conectare/deconectare/evenimente) - [ ] npm run typecheck passes - [ ] Verify in browser: consola arată evenimente SSE, nu polling requests --- ### US-009: Update Row Individual fără Re-render Lista **Ca** frontend **Vreau** să actualizez un singur rând fără să re-renderez toată lista **Pentru că** vreau performanță și stabilitate vizuală **Acceptance Criteria:** - [ ] Creează metodă `updateReceiptInPlace(receiptId, updates)` în store - [ ] Metoda modifică doar obiectul specific din array, nu înlocuiește array-ul - [ ] Vue reactivity detectează schimbarea și actualizează doar rândul afectat - [ ] Stats se actualizează separat (poate necesita re-fetch stats) - [ ] npm run typecheck passes - [ ] Verify in browser: DevTools Performance - doar un rând se re-renderează --- ### US-010: Graceful Degradation la SSE Failure **Ca** utilizator **Vreau** ca aplicația să funcționeze și fără SSE **Pentru că** pot avea probleme de rețea sau proxy **Acceptance Criteria:** - [ ] Dacă SSE fail la connect, fallback la polling clasic (interval 5s) - [ ] Mesaj discret în consolă (nu toast pentru user) - [ ] Retry SSE periodic (la 30s) pentru a vedea dacă funcționează din nou - [ ] npm run typecheck passes - [ ] Verify in browser: deconectează SSE, verifică că polling-ul preia --- ## 4. Cerințe Funcționale 1. [REQ-001] Butonul "Șterge" trebuie să fie roșu (severity danger) cu icon trash 2. [REQ-002] Bonurile cu `processing_status` în `pending` sau `processing` nu pot fi șterse 3. [REQ-003] Ștergerea bulk trebuie să șteargă și atașamentele și înregistrările contabile asociate 4. [REQ-004] SSE trebuie să trimită evenimente pentru: status change, processing_status change 5. [REQ-005] Frontend trebuie să mențină conexiunea SSE doar când există bonuri în procesare 6. [REQ-006] La ștergerea ultimului bon de pe pagină, navighează automat la pagina anterioară --- ## 5. Non-Goals (Ce NU facem) - ❌ Bulk edit (modificare date pe mai multe bonuri) - poate fi un alt PRD - ❌ Animații fade/slide la ștergere - utilizatorul a cerut instant remove - ❌ Undo pentru ștergere - nu e în scope - ❌ WebSocket în loc de SSE - SSE e mai simplu și suficient - ❌ Notificări push/toast pentru statusuri - doar actualizare în tabel --- ## 6. Considerații Tehnice ### Stack/Tehnologii - **Backend**: FastAPI cu SSE support (StreamingResponse) - **Frontend**: Vue 3 Composition API, Pinia store - **SSE**: Native EventSource API (bun suport browser) ### Patterns de Urmat - Service pattern în backend (nu logică în router) - Store actions în frontend pentru mutații - `@cached` decorator DOAR pentru reads, nu pentru deletes ### Dependențe - PrimeVue ConfirmDialog sau Dialog pentru confirmare - FastAPI `StreamingResponse` pentru SSE - Deja există: bulk actions bar, selection logic, checkbox disable pentru processing ### Riscuri Tehnice - SSE poate fi blocat de proxy/firewall - mitigat cu fallback la polling - Concurrency la delete bulk - poate conflicta cu procesare - mitigat cu check status ### Structură Fișiere Noi ``` backend/modules/data_entry/ ├── routers/ │ └── receipts.py # Adaugă DELETE /bulk și GET /sse/status ├── services/ │ └── sse_service.py # NOU: SSE broadcaster └── schemas/ └── receipt.py # Adaugă BulkDeleteRequest, BulkDeleteResponse src/modules/data-entry/ ├── services/ │ └── sseService.js # NOU: SSE client ├── stores/ │ └── receiptsStore.js # Adaugă updateReceiptInPlace └── views/receipts/ └── ReceiptsListView.vue # Adaugă buton Șterge, integrare SSE ``` --- ## 7. Considerații UI/UX ### Layout - Butonul Șterge: în bulk actions bar, header inline, lângă celelalte butoane - Dialog confirmare: centered modal, compact ### Stări - **Loading delete**: butonul Șterge arată spinner - **Success**: toast verde "X bonuri șterse" - **Partial fail**: toast warning "X din Y șterse, Z au eșuat" - **SSE connected**: indicator vizual opțional (poate în devtools) ### Accesibilitate - Dialog accesibil (focus trap, escape to close) - Buton cu label clear --- ## 8. Success Metrics - Bulk delete 10 bonuri: < 2 secunde end-to-end - Zero full page refreshes la status updates - SSE latency: < 500ms de la schimbare status la UI update - Fallback polling activat în < 5s dacă SSE eșuează --- ## 9. Open Questions - [x] Confirmare: simplă cu count ✓ - [x] Bonuri în procesare: exclude automat ✓ - [x] Polling: SSE ✓ - [x] Error handling: partial success ✓ - [x] Bara acțiuni: header inline ✓ - [x] Animații: instant remove ✓ - [x] Pagină goală: du la pagina anterioară ✓ --- ## 10. Ordine Implementare Recomandată 1. **US-003** - Backend bulk delete (fundație) 2. **US-001** - Buton Șterge în UI 3. **US-002** - Dialog confirmare 4. **US-004** - Frontend bulk delete cu toast 5. **US-005** - Navigare pagină anterioară 6. **US-009** - Update row in place (pregătire pentru SSE) 7. **US-006** - Backend SSE endpoint 8. **US-007** - Frontend SSE client 9. **US-008** - Înlocuire polling cu SSE 10. **US-010** - Graceful degradation