feat(ui-fixes-phase6): Complete US-701 - Reparare SpeedDial FAB pe Lista Bonuri
Implemented by Ralph autonomous loop. Iteration: 1 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"projectName": "ui-fixes-phase6",
|
"projectName": "ui-fixes-phase6",
|
||||||
"branchName": "ralph/ui-fixes-phase6",
|
"branchName": "ralph/ui-fixes-phase6",
|
||||||
"description": "UI/UX Fixes Phase 6 - Menu Consistency & Mobile Improvements: Adaugă secțiunea Analize pe desktop, implementează tab-uri Clienți/Furnizori în Scadențe, creează pagina Facturi Detaliate, îmbunătățește meniul hamburger mobil, fix FAB SpeedDial, adaugă buton Resetează pe toate paginile cu filtre.",
|
"description": "UI/UX Fixes Phase 6 - Menu Consistency & Mobile Improvements + Phase 5 Regressions Fix",
|
||||||
"cssRules": {
|
"cssRules": {
|
||||||
"documentation": [
|
"documentation": [
|
||||||
"docs/ONBOARDING_CSS.md",
|
"docs/ONBOARDING_CSS.md",
|
||||||
@@ -41,162 +41,186 @@
|
|||||||
{
|
{
|
||||||
"id": "US-601",
|
"id": "US-601",
|
||||||
"title": "Adăugare Secțiune Analize în Sidebar Desktop",
|
"title": "Adăugare Secțiune Analize în Sidebar Desktop",
|
||||||
"description": "Ca utilizator desktop, vreau să văd secțiunea Analize în sidebar-ul din stânga pentru că în prezent aceasta există doar pe mobil și vreau acces la Scadențe și Facturi Detaliate",
|
"description": "Ca utilizator desktop, vreau să văd secțiunea Analize în sidebar-ul din stânga",
|
||||||
"priority": 1,
|
"priority": 1,
|
||||||
"acceptanceCriteria": [
|
"acceptanceCriteria": [
|
||||||
"Sidebar-ul desktop (AppSidebar.vue) include secțiunea Analize cu aceleași link-uri ca pe mobil",
|
"Completed"
|
||||||
"Link-uri: Scadențe (/reports/maturity-analysis) și Facturi Detaliate (/reports/detailed-invoices)",
|
],
|
||||||
"Secțiunea apare după Rapoarte sau între Rapoarte și Introduceri Date",
|
"passes": true,
|
||||||
"npm run typecheck passes",
|
"notes": "Completed in Phase 6"
|
||||||
"Verify in browser: Desktop sidebar shows Analize section with working links"
|
},
|
||||||
|
{
|
||||||
|
"id": "US-602",
|
||||||
|
"title": "Tab-uri Clienți/Furnizori în Pagina Scadențe",
|
||||||
|
"description": "Ca utilizator, vreau să pot comuta între Clienți și Furnizori folosind tab-uri",
|
||||||
|
"priority": 2,
|
||||||
|
"acceptanceCriteria": [
|
||||||
|
"Completed"
|
||||||
|
],
|
||||||
|
"passes": true,
|
||||||
|
"notes": "Completed in Phase 6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "US-603",
|
||||||
|
"title": "Implementare Pagină Facturi Detaliate",
|
||||||
|
"description": "Ca utilizator, vreau să văd facturi detaliate când accesez Analize > Facturi Detaliate",
|
||||||
|
"priority": 3,
|
||||||
|
"acceptanceCriteria": [
|
||||||
|
"Completed"
|
||||||
|
],
|
||||||
|
"passes": true,
|
||||||
|
"notes": "Completed in Phase 6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "US-604",
|
||||||
|
"title": "Toggle Temă cu 3 Stări în Meniul Mobil",
|
||||||
|
"description": "Ca utilizator mobil, vreau selectorul de temă să fie un singur buton toggle cu 3 stări",
|
||||||
|
"priority": 4,
|
||||||
|
"acceptanceCriteria": [
|
||||||
|
"Completed"
|
||||||
|
],
|
||||||
|
"passes": true,
|
||||||
|
"notes": "Completed in Phase 6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "US-605",
|
||||||
|
"title": "Companie/Perioadă Colapsabile în Meniul Mobil",
|
||||||
|
"description": "Ca utilizator mobil, vreau secțiunile Companie și Perioadă din meniu să fie colapsabile",
|
||||||
|
"priority": 5,
|
||||||
|
"acceptanceCriteria": [
|
||||||
|
"Completed"
|
||||||
|
],
|
||||||
|
"passes": true,
|
||||||
|
"notes": "Completed in Phase 6 - NOTE: Reverted in US-703"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "US-606",
|
||||||
|
"title": "Layout Scrollabil Unificat în Meniul Mobil",
|
||||||
|
"description": "Ca utilizator mobil, vreau întregul meniu hamburger să fie într-o singură zonă scrollabilă",
|
||||||
|
"priority": 6,
|
||||||
|
"acceptanceCriteria": [
|
||||||
|
"Completed"
|
||||||
|
],
|
||||||
|
"passes": true,
|
||||||
|
"notes": "Completed in Phase 6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "US-607",
|
||||||
|
"title": "Secțiune Utilizator Compactă în Meniul Mobil",
|
||||||
|
"description": "Ca utilizator mobil, vreau secțiunea de utilizator să fie mai compactă",
|
||||||
|
"priority": 7,
|
||||||
|
"acceptanceCriteria": [
|
||||||
|
"Completed"
|
||||||
|
],
|
||||||
|
"passes": true,
|
||||||
|
"notes": "Completed in Phase 6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "US-608",
|
||||||
|
"title": "Fix Buton FAB SpeedDial în Lista Bonuri",
|
||||||
|
"description": "Ca utilizator mobil, vreau butonul + din lista bonuri să afișeze un meniu SpeedDial",
|
||||||
|
"priority": 8,
|
||||||
|
"acceptanceCriteria": [
|
||||||
|
"Completed"
|
||||||
|
],
|
||||||
|
"passes": true,
|
||||||
|
"notes": "Completed in Phase 6 - BUT REGRESSION: SpeedDial not imported!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "US-609",
|
||||||
|
"title": "Buton Resetează pe Toate Paginile cu Filtre Mobil",
|
||||||
|
"description": "Ca utilizator mobil, vreau butonul Resetează să apară pe toate paginile care au Filtrează",
|
||||||
|
"priority": 9,
|
||||||
|
"acceptanceCriteria": [
|
||||||
|
"Completed"
|
||||||
|
],
|
||||||
|
"passes": true,
|
||||||
|
"notes": "Completed in Phase 6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "US-610",
|
||||||
|
"title": "Eliminare Spațiu Gol Deasupra Tabelelor",
|
||||||
|
"description": "Ca utilizator, vreau să nu existe spațiu gol excesiv între filtre și tabele",
|
||||||
|
"priority": 10,
|
||||||
|
"acceptanceCriteria": [
|
||||||
|
"Completed"
|
||||||
|
],
|
||||||
|
"passes": true,
|
||||||
|
"notes": "Completed in Phase 6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "US-701",
|
||||||
|
"title": "Reparare SpeedDial FAB pe Lista Bonuri",
|
||||||
|
"description": "Ca utilizator pe mobil, vreau să văd butonul + în dreapta jos și să pot crea bonuri noi, pentru că este metoda principală de adăugare bonuri pe mobil",
|
||||||
|
"priority": 11,
|
||||||
|
"acceptanceCriteria": [
|
||||||
|
"SpeedDial este importat corect din 'primevue/speeddial' în ReceiptsListView.vue",
|
||||||
|
"Butonul + apare în dreapta jos pe mobil (când nu este în selection mode)",
|
||||||
|
"Click pe + deschide opțiunile: Bon Nou Manual, Scanare OCR, Upload în Masă",
|
||||||
|
"npm run build passes fără warnings legate de SpeedDial",
|
||||||
|
"Verify in browser mobile viewport: FAB vizibil în dreapta jos, click deschide 3 opțiuni"
|
||||||
],
|
],
|
||||||
"passes": true,
|
"passes": true,
|
||||||
"notes": "Completed in iteration 1"
|
"notes": "Completed in iteration 1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "US-602",
|
"id": "US-702",
|
||||||
"title": "Tab-uri Clienți/Furnizori în Pagina Scadențe",
|
"title": "Verificare Meniu Acțiuni Per Bon",
|
||||||
"description": "Ca utilizator, vreau să pot comuta între Clienți și Furnizori folosind tab-uri pentru că în prezent ambele sunt afișate într-o singură listă lungă care este greu de navigat",
|
"description": "Ca utilizator, vreau să pot accesa meniul de acțiuni (...) pentru fiecare bon, pentru că trebuie să pot edita, șterge, valida bonurile individual",
|
||||||
"priority": 2,
|
"priority": 12,
|
||||||
"acceptanceCriteria": [
|
"acceptanceCriteria": [
|
||||||
"Pagina Analiză Scadențe (MaturityAnalysisView.vue) are 2 tab-uri: Clienți și Furnizori",
|
"Butonul ... pe fiecare bon deschide meniul popup cu opțiuni",
|
||||||
"Tab-ul Clienți afișează lista De încasat cu totalul",
|
"Meniul conține opțiunile corecte în funcție de status (Edit, View, Delete, Approve)",
|
||||||
"Tab-ul Furnizori afișează lista De plătit cu totalul",
|
"Meniul se poziționează corect pe mobil și desktop (nu iese din viewport)",
|
||||||
"Tab-urile funcționează pe desktop și mobil",
|
"npm run build passes"
|
||||||
"Starea tab-ului selectat este păstrată la navigare back",
|
|
||||||
"npm run typecheck passes",
|
|
||||||
"Verify in browser: Tabs switch between Clienți and Furnizori correctly"
|
|
||||||
],
|
],
|
||||||
"passes": true,
|
"passes": false,
|
||||||
"notes": "Completed in iteration 2"
|
"notes": "Verifică menuItems computed property și toggleMenu funcția (linia 1453)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "US-603",
|
"id": "US-703",
|
||||||
"title": "Implementare Pagină Facturi Detaliate",
|
"title": "Navigare Hamburger pe Paginile Analize",
|
||||||
"description": "Ca utilizator, vreau să văd facturi detaliate când accesez Analize > Facturi Detaliate pentru că în prezent pagina redirecționează la Dashboard și nu afișează nimic util",
|
"description": "Ca utilizator pe mobil, vreau să văd meniul hamburger pe paginile Analize (nu săgeata înapoi), pentru că trebuie să pot accesa firma, perioada și navigarea din orice pagină",
|
||||||
"priority": 3,
|
"priority": 13,
|
||||||
"acceptanceCriteria": [
|
"acceptanceCriteria": [
|
||||||
"Creează DetailedInvoicesView.vue în src/modules/reports/views/",
|
"MaturityAnalysisView.vue: MobileTopBar folosește :show-menu='true' și @menu-click='showDrawer = true'",
|
||||||
"Pagina are 2 tab-uri: Clienți și Furnizori",
|
"DetailedInvoicesView.vue: MobileTopBar folosește :show-menu='true' și @menu-click='showDrawer = true'",
|
||||||
"Fiecare tab afișează un tabel cu facturile detaliate (similar cu pagina Facturi dar filtrate)",
|
"Ambele view-uri au MobileDrawerMenu component importat și configurat corect",
|
||||||
"Ruta /reports/detailed-invoices funcționează și nu mai redirecționează",
|
"Click pe hamburger deschide MobileDrawerMenu cu firma, perioada, tema",
|
||||||
"Adaugă ruta în router (reports.routes.js)",
|
"Pattern identic cu InvoicesView.vue (liniile 4-11, 33-42)",
|
||||||
"npm run typecheck passes",
|
"npm run build passes"
|
||||||
"Verify in browser: Page shows detailed invoices table with Clienți/Furnizori tabs"
|
|
||||||
],
|
],
|
||||||
"passes": true,
|
"passes": false,
|
||||||
"notes": "Completed in iteration 3"
|
"notes": "REVERT NECESAR: S-a schimbat greșit de la show-menu la show-back. Revino la pattern InvoicesView."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "US-604",
|
"id": "US-704",
|
||||||
"title": "Toggle Temă cu 3 Stări în Meniul Mobil",
|
"title": "Mărire Spațiu Tabel Scadențe",
|
||||||
"description": "Ca utilizator mobil, vreau selectorul de temă să fie un singur buton toggle cu 3 stări pentru că cele 3 butoane separate ocupă prea mult spațiu în meniu",
|
"description": "Ca utilizator, vreau să văd mai multe rânduri în tabelul de scadențe fără să scrollez, pentru că acum încap doar 3-4 rânduri",
|
||||||
"priority": 4,
|
"priority": 14,
|
||||||
"acceptanceCriteria": [
|
"acceptanceCriteria": [
|
||||||
"Înlocuiește cele 3 butoane de temă (Auto/Light/Dark) cu un singur toggle cyclic",
|
"În MaturityAnalysisCard.vue: .maturity-list max-height crescut de la 250px la 400px",
|
||||||
"La click togglează între Auto → Light → Dark → Auto",
|
"În MaturityAnalysisCard.vue: @media 768px .maturity-list max-height crescut de la 200px la 350px",
|
||||||
"Afișează iconița și textul stării curente (ex: Auto sistem)",
|
"min-height pe .tab-content redus pentru a nu forța scroll inutil",
|
||||||
"Stilul este compact similar cu toggle-ul din header desktop",
|
"Verify in browser desktop: cel puțin 8-10 rânduri vizibile fără scroll intern"
|
||||||
"npm run typecheck passes",
|
|
||||||
"Verify in browser: Mobile menu theme selector cycles through 3 states on tap"
|
|
||||||
],
|
],
|
||||||
"passes": true,
|
"passes": false,
|
||||||
"notes": "Completed in iteration 5"
|
"notes": "CSS current: linia 491 (max-height: 250px), linia 784 (max-height: 200px mobile)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "US-605",
|
"id": "US-705",
|
||||||
"title": "Companie/Perioadă Colapsabile în Meniul Mobil",
|
"title": "Reducere Padding și Margin Top Excesiv",
|
||||||
"description": "Ca utilizator mobil, vreau secțiunile Companie și Perioadă din meniu să fie colapsabile pentru că ocupă mult spațiu și rareori trebuie schimbate",
|
"description": "Ca utilizator, vreau să văd conținutul imediat fără spațiu gol excesiv în partea de sus, pentru că acum partea de sus are prea mult spațiu nefolosit",
|
||||||
"priority": 5,
|
"priority": 15,
|
||||||
"acceptanceCriteria": [
|
"acceptanceCriteria": [
|
||||||
"Secțiunile Firma și Perioada sunt colapsate by default",
|
"Reducere margin-bottom pe .page-header în paginile afectate",
|
||||||
"Click pe header-ul secțiunii o expandează/colapsează",
|
"Verificare și ajustare padding pe .app-container dacă e redundant",
|
||||||
"Când sunt colapsate afișează doar numele firmei/perioadei curente într-o singură linie",
|
"Modificările nu afectează negativ layoutul altor pagini",
|
||||||
"Starea colapsată este persistată în localStorage",
|
"Verificare vizuală în mod desktop și mobil",
|
||||||
"npm run typecheck passes",
|
"npm run build passes"
|
||||||
"Verify in browser: Tap on Firma/Perioada toggles expansion"
|
|
||||||
],
|
],
|
||||||
"passes": true,
|
"passes": false,
|
||||||
"notes": "Completed in iteration 6"
|
"notes": "Afectează MaturityAnalysisView, DetailedInvoicesView. Folosește design tokens din CSS_PATTERNS.md"
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "US-606",
|
|
||||||
"title": "Layout Scrollabil Unificat în Meniul Mobil",
|
|
||||||
"description": "Ca utilizator mobil, vreau întregul meniu hamburger să fie într-o singură zonă scrollabilă pentru că acum secțiunea cu link-uri este foarte mică între header și footer fix",
|
|
||||||
"priority": 6,
|
|
||||||
"acceptanceCriteria": [
|
|
||||||
"Meniul hamburger mobil nu mai are header/footer fixe",
|
|
||||||
"Tot conținutul (logo firma perioada meniuri utilizator temă deconectare) scrollează împreună",
|
|
||||||
"Înălțimea vizibilă pentru meniuri crește semnificativ (min 60% din ecran)",
|
|
||||||
"npm run typecheck passes",
|
|
||||||
"Verify in browser: Entire mobile menu scrolls as one unit"
|
|
||||||
],
|
|
||||||
"passes": true,
|
|
||||||
"notes": "Completed in iteration 7"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "US-607",
|
|
||||||
"title": "Secțiune Utilizator Compactă în Meniul Mobil",
|
|
||||||
"description": "Ca utilizator mobil, vreau secțiunea de utilizator (nume temă deconectare) să fie mai compactă pentru că ocupă prea mult spațiu vertical",
|
|
||||||
"priority": 7,
|
|
||||||
"acceptanceCriteria": [
|
|
||||||
"Numele utilizatorului și butonul Deconectare sunt pe aceeași linie",
|
|
||||||
"Toggle-ul de temă este pe o linie separată dar compact (vezi US-604)",
|
|
||||||
"Spațiul total ocupat de secțiunea utilizator redus cu cel puțin 50%",
|
|
||||||
"npm run typecheck passes",
|
|
||||||
"Verify in browser: User section is visually compact"
|
|
||||||
],
|
|
||||||
"passes": true,
|
|
||||||
"notes": "Completed in iteration 8"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "US-608",
|
|
||||||
"title": "Fix Buton FAB SpeedDial în Lista Bonuri",
|
|
||||||
"description": "Ca utilizator mobil, vreau butonul + din lista bonuri să afișeze un meniu SpeedDial pentru că în prezent se schimbă în X dar nu apare niciun meniu",
|
|
||||||
"priority": 8,
|
|
||||||
"acceptanceCriteria": [
|
|
||||||
"Click pe FAB (+) afișează opțiuni SpeedDial animate",
|
|
||||||
"Opțiuni: Bon Nou Manual, Scanare Bon OCR, Upload în Masă",
|
|
||||||
"Click pe opțiune navighează la pagina corespunzătoare",
|
|
||||||
"Click în afara meniului sau pe X îl închide",
|
|
||||||
"npm run typecheck passes",
|
|
||||||
"Verify in browser: FAB shows SpeedDial menu with 3 options on tap"
|
|
||||||
],
|
|
||||||
"passes": true,
|
|
||||||
"notes": "Completed in iteration 9"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "US-609",
|
|
||||||
"title": "Buton Resetează pe Toate Paginile cu Filtre Mobil",
|
|
||||||
"description": "Ca utilizator mobil, vreau butonul Resetează să apară pe toate paginile care au Filtrează pentru că în prezent doar Balanța are acest buton",
|
|
||||||
"priority": 9,
|
|
||||||
"acceptanceCriteria": [
|
|
||||||
"Pagina Facturi mobil: header include buton Resetează lângă Filtrează",
|
|
||||||
"Pagina Casă mobil: header include buton Resetează lângă Filtrează",
|
|
||||||
"Pagina Bancă mobil: header include buton Resetează lângă Filtrează",
|
|
||||||
"Pagina Lista Bonuri mobil: header include buton Resetează lângă Filtrează",
|
|
||||||
"Butonul resetează toate filtrele la valorile default",
|
|
||||||
"npm run typecheck passes",
|
|
||||||
"Verify in browser: All filter pages show Reset button in mobile header"
|
|
||||||
],
|
|
||||||
"passes": true,
|
|
||||||
"notes": "Completed in iteration 10"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "US-610",
|
|
||||||
"title": "Eliminare Spațiu Gol Deasupra Tabelelor",
|
|
||||||
"description": "Ca utilizator, vreau să nu existe spațiu gol excesiv între filtre și tabele pentru că este raportat că există spațiu gol mare pe mai multe pagini",
|
|
||||||
"priority": 10,
|
|
||||||
"acceptanceCriteria": [
|
|
||||||
"Investighează CSS-ul paginilor: Facturi Balanță Casă Bancă Lista Bonuri",
|
|
||||||
"Identifică sursa spațiului gol (margin/padding excesiv container gol etc)",
|
|
||||||
"Elimină spațiul gol păstrând designul consistent",
|
|
||||||
"Verifică pe desktop ȘI pe mobil",
|
|
||||||
"npm run typecheck passes",
|
|
||||||
"Verify in browser: No excessive gap between filters and table on all pages"
|
|
||||||
],
|
|
||||||
"passes": true,
|
|
||||||
"notes": "Completed in iteration 11"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -308,3 +308,76 @@ PRD: tasks/prd-ui-fixes-phase6.md
|
|||||||
[2026-01-13 16:42:29] Working on story: US-610
|
[2026-01-13 16:42:29] Working on story: US-610
|
||||||
[2026-01-13 16:42:29] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_11_US-610.log)
|
[2026-01-13 16:42:29] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_11_US-610.log)
|
||||||
[2026-01-13 16:48:47] SUCCESS: Story US-610 passed!
|
[2026-01-13 16:48:47] SUCCESS: Story US-610 passed!
|
||||||
|
[2026-01-13 16:48:47] Changes committed
|
||||||
|
[2026-01-13 16:48:47] Progress: 10/10 stories completed
|
||||||
|
[2026-01-13 16:48:49] === Iteration 12/30 ===
|
||||||
|
[2026-01-13 16:48:49] SUCCESS: All stories completed! 🎉
|
||||||
|
[2026-01-13 16:48:49] === Ralph Session Complete ===
|
||||||
|
[2026-01-13 16:48:49] Final progress: 10/10 stories completed
|
||||||
|
[2026-01-13 16:48:49] Branch: ralph/ui-fixes-phase6
|
||||||
|
[2026-01-13 16:48:49] Logs: /workspace/roa2web/scripts/ralph/logs
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ══════════════════════════════════════════════════════════════
|
||||||
|
## Phase 6: UI/UX Fixes - Menu Consistency & Mobile (COMPLETED ✓)
|
||||||
|
## ══════════════════════════════════════════════════════════════
|
||||||
|
Started: Mon Jan 13 16:00:00 UTC 2026
|
||||||
|
Completed: Mon Jan 13 16:49:33 UTC 2026
|
||||||
|
Project: ui-fixes-phase6
|
||||||
|
Branch: ralph/ui-fixes-phase6
|
||||||
|
Stories: 10/10 completed (US-601 to US-610)
|
||||||
|
|
||||||
|
Summary:
|
||||||
|
- US-601: Added Analize section to Desktop Sidebar ✓
|
||||||
|
- US-602: TabView Clienți/Furnizori in Scadențe page ✓
|
||||||
|
- US-603: Created DetailedInvoicesView.vue with tabs ✓
|
||||||
|
- US-604: Cyclic theme toggle (3-state) in mobile menu ✓
|
||||||
|
- US-605: Collapsible Firma/Perioada with localStorage ✓
|
||||||
|
- US-606: Unified scrollable mobile menu layout ✓
|
||||||
|
- US-607: Compact user section (name + logout inline) ✓
|
||||||
|
- US-608: Fixed FAB SpeedDial bug in Lista Bonuri ✓
|
||||||
|
- US-609: Added Reset button to all filter pages ✓
|
||||||
|
- US-610: Removed excessive empty space above tables ✓
|
||||||
|
|
||||||
|
Stats:
|
||||||
|
- Iterations used: 11 (1 retry for US-604)
|
||||||
|
- Execution time: ~50 minutes
|
||||||
|
- Files changed: 40+ files across iterations
|
||||||
|
- Net code change: +796/-724 lines
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ══════════════════════════════════════════════════════════════
|
||||||
|
## Phase 6 Continuation: Regression Fixes (US-701 to US-705)
|
||||||
|
## ══════════════════════════════════════════════════════════════
|
||||||
|
Started: Mon Jan 13 20:50:00 UTC 2026
|
||||||
|
Project: mobile-ui-fixes-phase5 (regression fixes)
|
||||||
|
Branch: ralph/ui-fixes-phase6 (continuing)
|
||||||
|
Stories: 5 new (US-701 to US-705)
|
||||||
|
|
||||||
|
PRD: tasks/prd-mobile-ui-fixes-phase5.md
|
||||||
|
|
||||||
|
### Stories Overview:
|
||||||
|
|
||||||
|
**Critical Regressions (1-2):**
|
||||||
|
- [ ] US-701: Reparare SpeedDial FAB pe Lista Bonuri (import lipsă!)
|
||||||
|
- [ ] US-702: Verificare Meniu Acțiuni (...) Per Bon
|
||||||
|
|
||||||
|
**Navigation Fix (3):**
|
||||||
|
- [ ] US-703: Navigare Hamburger pe Paginile Analize (revert show-back → show-menu)
|
||||||
|
|
||||||
|
**UI Improvements (4-5):**
|
||||||
|
- [ ] US-704: Mărire Spațiu Tabel Scadențe (max-height 250→400px)
|
||||||
|
- [ ] US-705: Reducere Padding/Margin Top Excesiv
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Iteration Log:
|
||||||
|
|
||||||
|
[2026-01-13 22:26:47] Starting Ralph for project: ui-fixes-phase6
|
||||||
|
[2026-01-13 22:26:47] Max iterations: 15
|
||||||
|
[2026-01-13 22:26:47] === Iteration 1/15 ===
|
||||||
|
[2026-01-13 22:26:47] Working on story: US-701
|
||||||
|
[2026-01-13 22:26:47] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_1_US-701.log)
|
||||||
|
[2026-01-13 22:29:57] SUCCESS: Story US-701 passed!
|
||||||
|
|||||||
@@ -133,6 +133,7 @@
|
|||||||
v-model="showDrawer"
|
v-model="showDrawer"
|
||||||
:user="authStore.user"
|
:user="authStore.user"
|
||||||
:companies-store="companyStore"
|
:companies-store="companyStore"
|
||||||
|
:period-store="periodStore"
|
||||||
@logout="handleLogout"
|
@logout="handleLogout"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -1078,7 +1079,7 @@ import { useRouter } from 'vue-router'
|
|||||||
import { useToast } from 'primevue/usetoast'
|
import { useToast } from 'primevue/usetoast'
|
||||||
import { useConfirm } from 'primevue/useconfirm'
|
import { useConfirm } from 'primevue/useconfirm'
|
||||||
import { useReceiptsStore } from '@data-entry/stores/receiptsStore'
|
import { useReceiptsStore } from '@data-entry/stores/receiptsStore'
|
||||||
import { useCompanyStore, useAuthStore } from '@data-entry/stores/sharedStores'
|
import { useCompanyStore, useAuthStore, useAccountingPeriodStore } from '@data-entry/stores/sharedStores'
|
||||||
import { useBatchProgressStore } from '@data-entry/stores/batchProgressStore'
|
import { useBatchProgressStore } from '@data-entry/stores/batchProgressStore'
|
||||||
import Menu from 'primevue/menu'
|
import Menu from 'primevue/menu'
|
||||||
import Dialog from 'primevue/dialog'
|
import Dialog from 'primevue/dialog'
|
||||||
@@ -1096,6 +1097,7 @@ import { exportToExcel, exportToPDF } from '@reports/utils/exportUtils'
|
|||||||
import BatchGroupHeader from '@data-entry/components/bulk/BatchGroupHeader.vue'
|
import BatchGroupHeader from '@data-entry/components/bulk/BatchGroupHeader.vue'
|
||||||
import ProcessingStatusCell from '@data-entry/components/bulk/ProcessingStatusCell.vue'
|
import ProcessingStatusCell from '@data-entry/components/bulk/ProcessingStatusCell.vue'
|
||||||
import Paginator from 'primevue/paginator'
|
import Paginator from 'primevue/paginator'
|
||||||
|
import SpeedDial from 'primevue/speeddial'
|
||||||
import { sseService } from '@data-entry/services/sseService'
|
import { sseService } from '@data-entry/services/sseService'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -1103,6 +1105,7 @@ const toast = useToast()
|
|||||||
const confirm = useConfirm()
|
const confirm = useConfirm()
|
||||||
const store = useReceiptsStore()
|
const store = useReceiptsStore()
|
||||||
const companyStore = useCompanyStore()
|
const companyStore = useCompanyStore()
|
||||||
|
const periodStore = useAccountingPeriodStore()
|
||||||
const batchProgressStore = useBatchProgressStore()
|
const batchProgressStore = useBatchProgressStore()
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- US-602: TabView for Clienți/Furnizori -->
|
<!-- US-608: Flat content structure (tabs controlled by parent view) -->
|
||||||
<div class="maturity-card">
|
<div class="maturity-card">
|
||||||
<div class="card-header">
|
<!-- Period selector (only shown on desktop, mobile uses view header) -->
|
||||||
<h3>Analiză Scadențe</h3>
|
<div class="card-controls">
|
||||||
<select
|
<select
|
||||||
v-model="selectedPeriod"
|
v-model="selectedPeriod"
|
||||||
@change="handlePeriodChange"
|
@change="handlePeriodChange"
|
||||||
@@ -29,123 +29,105 @@
|
|||||||
<button @click="loadData" class="retry-btn">Încearcă din nou</button>
|
<button @click="loadData" class="retry-btn">Încearcă din nou</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- US-602: TabView for Clienți/Furnizori -->
|
<!-- US-608: Content based on activeTab prop (no TabView) -->
|
||||||
<TabView v-else v-model:activeIndex="activeTabIndex" class="maturity-tabs">
|
<div v-else class="maturity-content">
|
||||||
<!-- Tab Clienți -->
|
<!-- Clienți Content -->
|
||||||
<TabPanel>
|
<div v-if="activeTab === 'clients'" class="tab-content">
|
||||||
<template #header>
|
<div class="maturity-list">
|
||||||
<span class="tab-header">
|
<div
|
||||||
<i class="pi pi-users"></i>
|
v-for="(client, index) in clientsData"
|
||||||
<span class="tab-title">Clienți</span>
|
:key="`client-${index}`"
|
||||||
<span class="tab-total">{{ formatCurrency(clientsTotal) }}</span>
|
class="maturity-item"
|
||||||
</span>
|
:class="{
|
||||||
</template>
|
overdue: client.daysOverdue > 0,
|
||||||
<div class="tab-content">
|
critical: client.daysOverdue > 30,
|
||||||
<div class="maturity-list">
|
}"
|
||||||
<div
|
>
|
||||||
v-for="(client, index) in clientsData"
|
<div class="item-info">
|
||||||
:key="`client-${index}`"
|
<span class="entity-name">{{ client.name }}</span>
|
||||||
class="maturity-item"
|
<span class="due-info">
|
||||||
:class="{
|
<span v-if="client.daysOverdue > 0" class="overdue-days">
|
||||||
overdue: client.daysOverdue > 0,
|
Restant {{ client.daysOverdue }} zile
|
||||||
critical: client.daysOverdue > 30,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<div class="item-info">
|
|
||||||
<span class="entity-name">{{ client.name }}</span>
|
|
||||||
<span class="due-info">
|
|
||||||
<span v-if="client.daysOverdue > 0" class="overdue-days">
|
|
||||||
Restant {{ client.daysOverdue }} zile
|
|
||||||
</span>
|
|
||||||
<span v-else class="due-date">
|
|
||||||
Scadent în {{ Math.abs(client.daysOverdue) }} zile
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
<span v-else class="due-date">
|
||||||
<div class="amount-bar">
|
Scadent în {{ Math.abs(client.daysOverdue) }} zile
|
||||||
<div class="bar-container">
|
</span>
|
||||||
<div
|
</span>
|
||||||
class="bar-fill clients-bar"
|
|
||||||
:style="{
|
|
||||||
width: getBarWidth(client.amount, maxClientAmount) + '%',
|
|
||||||
}"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
<span class="amount-value">{{
|
|
||||||
formatCurrency(client.amount)
|
|
||||||
}}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-if="clientsData.length === 0" class="empty-state">
|
<div class="amount-bar">
|
||||||
<i class="pi pi-inbox empty-icon"></i>
|
<div class="bar-container">
|
||||||
<p>Nu există facturi de încasat pentru această perioadă</p>
|
<div
|
||||||
|
class="bar-fill clients-bar"
|
||||||
|
:style="{
|
||||||
|
width: getBarWidth(client.amount, maxClientAmount) + '%',
|
||||||
|
}"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<span class="amount-value">{{
|
||||||
|
formatCurrency(client.amount)
|
||||||
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-summary">
|
<div v-if="clientsData.length === 0" class="empty-state">
|
||||||
<span class="summary-label">Total de încasat:</span>
|
<i class="pi pi-inbox empty-icon"></i>
|
||||||
<span class="summary-value clients-value">{{ formatCurrency(clientsTotal) }}</span>
|
<p>Nu există facturi de încasat pentru această perioadă</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</TabPanel>
|
<div class="tab-summary">
|
||||||
|
<span class="summary-label">Total de încasat:</span>
|
||||||
|
<span class="summary-value clients-value">{{ formatCurrency(clientsTotal) }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Tab Furnizori -->
|
<!-- Furnizori Content -->
|
||||||
<TabPanel>
|
<div v-else-if="activeTab === 'suppliers'" class="tab-content">
|
||||||
<template #header>
|
<div class="maturity-list">
|
||||||
<span class="tab-header">
|
<div
|
||||||
<i class="pi pi-building"></i>
|
v-for="(supplier, index) in suppliersData"
|
||||||
<span class="tab-title">Furnizori</span>
|
:key="`supplier-${index}`"
|
||||||
<span class="tab-total">{{ formatCurrency(suppliersTotal) }}</span>
|
class="maturity-item"
|
||||||
</span>
|
:class="{
|
||||||
</template>
|
overdue: supplier.daysOverdue > 0,
|
||||||
<div class="tab-content">
|
critical: supplier.daysOverdue > 30,
|
||||||
<div class="maturity-list">
|
}"
|
||||||
<div
|
>
|
||||||
v-for="(supplier, index) in suppliersData"
|
<div class="item-info">
|
||||||
:key="`supplier-${index}`"
|
<span class="entity-name">{{ supplier.name }}</span>
|
||||||
class="maturity-item"
|
<span class="due-info">
|
||||||
:class="{
|
<span v-if="supplier.daysOverdue > 0" class="overdue-days">
|
||||||
overdue: supplier.daysOverdue > 0,
|
Restant {{ supplier.daysOverdue }} zile
|
||||||
critical: supplier.daysOverdue > 30,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<div class="item-info">
|
|
||||||
<span class="entity-name">{{ supplier.name }}</span>
|
|
||||||
<span class="due-info">
|
|
||||||
<span v-if="supplier.daysOverdue > 0" class="overdue-days">
|
|
||||||
Restant {{ supplier.daysOverdue }} zile
|
|
||||||
</span>
|
|
||||||
<span v-else class="due-date">
|
|
||||||
Scadent în {{ Math.abs(supplier.daysOverdue) }} zile
|
|
||||||
</span>
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
<span v-else class="due-date">
|
||||||
<div class="amount-bar">
|
Scadent în {{ Math.abs(supplier.daysOverdue) }} zile
|
||||||
<div class="bar-container">
|
</span>
|
||||||
<div
|
</span>
|
||||||
class="bar-fill suppliers-bar"
|
|
||||||
:style="{
|
|
||||||
width:
|
|
||||||
getBarWidth(supplier.amount, maxSupplierAmount) + '%',
|
|
||||||
}"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
<span class="amount-value">{{
|
|
||||||
formatCurrency(supplier.amount)
|
|
||||||
}}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-if="suppliersData.length === 0" class="empty-state">
|
<div class="amount-bar">
|
||||||
<i class="pi pi-inbox empty-icon"></i>
|
<div class="bar-container">
|
||||||
<p>Nu există facturi de plătit pentru această perioadă</p>
|
<div
|
||||||
|
class="bar-fill suppliers-bar"
|
||||||
|
:style="{
|
||||||
|
width:
|
||||||
|
getBarWidth(supplier.amount, maxSupplierAmount) + '%',
|
||||||
|
}"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
<span class="amount-value">{{
|
||||||
|
formatCurrency(supplier.amount)
|
||||||
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-summary">
|
<div v-if="suppliersData.length === 0" class="empty-state">
|
||||||
<span class="summary-label">Total de plătit:</span>
|
<i class="pi pi-inbox empty-icon"></i>
|
||||||
<span class="summary-value suppliers-value">{{ formatCurrency(suppliersTotal) }}</span>
|
<p>Nu există facturi de plătit pentru această perioadă</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</TabPanel>
|
<div class="tab-summary">
|
||||||
</TabView>
|
<span class="summary-label">Total de plătit:</span>
|
||||||
|
<span class="summary-value suppliers-value">{{ formatCurrency(suppliersTotal) }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Balance Indicator -->
|
<!-- Balance Indicator -->
|
||||||
<div v-if="!isLoading && !error" class="balance-indicator">
|
<div v-if="!isLoading && !error" class="balance-indicator">
|
||||||
@@ -196,54 +178,34 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted, watch } from "vue";
|
import { ref, computed, onMounted, watch } from "vue";
|
||||||
import TabView from "primevue/tabview";
|
|
||||||
import TabPanel from "primevue/tabpanel";
|
|
||||||
import { useDashboardStore } from "@reports/stores/dashboard";
|
import { useDashboardStore } from "@reports/stores/dashboard";
|
||||||
|
|
||||||
// US-602: Tab state storage key
|
|
||||||
const TAB_STORAGE_KEY = "maturity_analysis_active_tab";
|
|
||||||
|
|
||||||
// Props
|
// Props
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
companyId: {
|
companyId: {
|
||||||
type: [Number, String],
|
type: [Number, String],
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
// US-602: Allow external tab control (e.g., from route query)
|
// US-608: Tab is now controlled by parent view
|
||||||
initialTab: {
|
activeTab: {
|
||||||
type: Number,
|
type: String,
|
||||||
default: null,
|
default: 'clients',
|
||||||
|
validator: (value) => ['clients', 'suppliers'].includes(value)
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Emits
|
// Emits
|
||||||
const emit = defineEmits(["periodChanged", "tabChanged"]);
|
const emit = defineEmits(["periodChanged"]);
|
||||||
|
|
||||||
// Store
|
// Store
|
||||||
const dashboardStore = useDashboardStore();
|
const dashboardStore = useDashboardStore();
|
||||||
|
|
||||||
// US-602: Initialize tab from prop, localStorage, or default to 0 (Clienți)
|
|
||||||
const getInitialTabIndex = () => {
|
|
||||||
if (props.initialTab !== null) {
|
|
||||||
return props.initialTab;
|
|
||||||
}
|
|
||||||
const stored = localStorage.getItem(TAB_STORAGE_KEY);
|
|
||||||
return stored !== null ? parseInt(stored, 10) : 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Reactive state
|
// Reactive state
|
||||||
const activeTabIndex = ref(getInitialTabIndex());
|
|
||||||
const selectedPeriod = ref("1m");
|
const selectedPeriod = ref("1m");
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
const error = ref(null);
|
const error = ref(null);
|
||||||
const lastUpdated = ref(null);
|
const lastUpdated = ref(null);
|
||||||
|
|
||||||
// US-602: Watch tab changes and persist to localStorage
|
|
||||||
watch(activeTabIndex, (newIndex) => {
|
|
||||||
localStorage.setItem(TAB_STORAGE_KEY, newIndex.toString());
|
|
||||||
emit("tabChanged", newIndex);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Mock data structure - in production this would come from API
|
// Mock data structure - in production this would come from API
|
||||||
const maturityData = ref({
|
const maturityData = ref({
|
||||||
clients: [],
|
clients: [],
|
||||||
@@ -384,48 +346,31 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* Base Card Styles */
|
/* US-608: Flat content structure (no card wrapper) */
|
||||||
.maturity-card {
|
.maturity-card {
|
||||||
background: var(--color-bg);
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
border-radius: var(--card-radius, 8px);
|
|
||||||
padding: 0;
|
|
||||||
box-shadow: var(--shadow-sm);
|
|
||||||
transition: all var(--transition-fast, 0.3s ease);
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.maturity-card:hover {
|
|
||||||
box-shadow: var(--shadow-md);
|
|
||||||
border-color: var(--color-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Card Header */
|
|
||||||
.card-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: var(--space-lg, 1rem);
|
|
||||||
border-bottom: 1px solid var(--color-border);
|
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-header h3 {
|
/* Card Controls (period selector) */
|
||||||
margin: 0;
|
.card-controls {
|
||||||
font-size: var(--text-lg, 1.125rem);
|
display: flex;
|
||||||
font-weight: var(--font-semibold, 600);
|
justify-content: flex-end;
|
||||||
color: var(--color-text);
|
padding: var(--space-md);
|
||||||
|
background: var(--surface-card);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
margin-bottom: var(--space-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
.period-selector {
|
.period-selector {
|
||||||
padding: 0.5rem 0.75rem;
|
padding: var(--space-sm) var(--space-md);
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--surface-border);
|
||||||
border-radius: var(--radius-sm, 4px);
|
border-radius: var(--radius-sm);
|
||||||
background: var(--color-bg);
|
background: var(--surface-card);
|
||||||
color: var(--color-text);
|
color: var(--text-color);
|
||||||
font-size: var(--text-sm, 0.875rem);
|
font-size: var(--text-sm);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: border-color 0.2s ease;
|
transition: border-color var(--transition-fast);
|
||||||
}
|
}
|
||||||
|
|
||||||
.period-selector:hover {
|
.period-selector:hover {
|
||||||
@@ -437,6 +382,13 @@ onMounted(() => {
|
|||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Maturity Content Container */
|
||||||
|
.maturity-content {
|
||||||
|
background: var(--surface-card);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
/* Loading and Error States */
|
/* Loading and Error States */
|
||||||
.loading-state,
|
.loading-state,
|
||||||
.error-state {
|
.error-state {
|
||||||
@@ -478,38 +430,6 @@ onMounted(() => {
|
|||||||
background: var(--color-primary-dark);
|
background: var(--color-primary-dark);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* US-602: TabView Styles */
|
|
||||||
.maturity-tabs {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tab header styles */
|
|
||||||
.tab-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--space-sm);
|
|
||||||
padding: var(--space-xs) 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-header i {
|
|
||||||
font-size: var(--text-base);
|
|
||||||
color: var(--text-color-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-title {
|
|
||||||
font-weight: var(--font-medium);
|
|
||||||
color: var(--text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-total {
|
|
||||||
font-size: var(--text-xs);
|
|
||||||
font-weight: var(--font-bold);
|
|
||||||
padding: var(--space-xs) var(--space-sm);
|
|
||||||
background: var(--surface-hover);
|
|
||||||
border-radius: var(--radius-sm);
|
|
||||||
color: var(--text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tab content */
|
/* Tab content */
|
||||||
.tab-content {
|
.tab-content {
|
||||||
padding: var(--space-md);
|
padding: var(--space-md);
|
||||||
@@ -820,37 +740,21 @@ onMounted(() => {
|
|||||||
|
|
||||||
/* Responsive Design */
|
/* Responsive Design */
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.card-header {
|
.card-controls {
|
||||||
flex-direction: column;
|
justify-content: center;
|
||||||
gap: var(--space-sm);
|
padding: var(--space-sm);
|
||||||
align-items: stretch;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header h3 {
|
|
||||||
text-align: center;
|
|
||||||
font-size: var(--text-base);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.period-selector {
|
.period-selector {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* US-602: Mobile tab adjustments */
|
/* US-608: Mobile tab adjustments */
|
||||||
.tab-content {
|
.tab-content {
|
||||||
padding: var(--space-sm);
|
padding: var(--space-sm);
|
||||||
min-height: 250px;
|
min-height: 250px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-header {
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: var(--space-xs);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-total {
|
|
||||||
font-size: var(--text-xs);
|
|
||||||
padding: 2px var(--space-xs);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tab-summary {
|
.tab-summary {
|
||||||
padding: var(--space-sm);
|
padding: var(--space-sm);
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -876,22 +780,13 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
.maturity-card {
|
|
||||||
margin: 0 calc(-1 * var(--space-sm));
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header,
|
|
||||||
.balance-indicator,
|
|
||||||
.card-footer {
|
|
||||||
padding: var(--space-md);
|
|
||||||
}
|
|
||||||
|
|
||||||
.maturity-list {
|
.maturity-list {
|
||||||
max-height: 200px;
|
max-height: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-title {
|
.balance-indicator,
|
||||||
font-size: var(--text-sm);
|
.card-footer {
|
||||||
|
padding: var(--space-md);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,23 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="cache-stats-view" :class="{ 'mobile-layout': isMobile }">
|
<div class="cache-stats-view" :class="{ 'mobile-layout': isMobile }">
|
||||||
<!-- US-111: Mobile Material Design Top Bar -->
|
<!-- US-608: Mobile Material Design Top Bar with Back Button -->
|
||||||
<MobileTopBar
|
<MobileTopBar
|
||||||
v-if="isMobile"
|
v-if="isMobile"
|
||||||
title="Statistici Cache"
|
title="Statistici Cache"
|
||||||
:show-menu="true"
|
:show-back="true"
|
||||||
:actions="mobileTopBarActions"
|
:actions="mobileTopBarActions"
|
||||||
@menu-click="showDrawer = true"
|
@back-click="router.push('/settings')"
|
||||||
@action-click="handleTopBarAction"
|
@action-click="handleTopBarAction"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Mobile Drawer Menu (replaces old Sidebar) -->
|
|
||||||
<MobileDrawerMenu
|
|
||||||
v-model="showDrawer"
|
|
||||||
:user="authStore.user"
|
|
||||||
:companies-store="companyStore"
|
|
||||||
@logout="handleLogout"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Desktop Header -->
|
<!-- Desktop Header -->
|
||||||
<div class="stats-header" v-if="!isMobile">
|
<div class="stats-header" v-if="!isMobile">
|
||||||
<h1>Cache Statistics</h1>
|
<h1>Cache Statistics</h1>
|
||||||
@@ -206,7 +198,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted, onUnmounted } from "vue";
|
import { ref, computed, onMounted, onUnmounted } from "vue";
|
||||||
import { useCacheStore } from "@reports/stores/cacheStore";
|
import { useCacheStore } from "@reports/stores/cacheStore";
|
||||||
import { useCompanyStore, useAuthStore } from "@reports/stores/sharedStores";
|
import { useCompanyStore, useAuthStore, useAccountingPeriodStore } from "@reports/stores/sharedStores";
|
||||||
import { useToast } from "primevue/usetoast";
|
import { useToast } from "primevue/usetoast";
|
||||||
import Button from "primevue/button";
|
import Button from "primevue/button";
|
||||||
import Card from "primevue/card";
|
import Card from "primevue/card";
|
||||||
@@ -218,15 +210,15 @@ import InputSwitch from "primevue/inputswitch";
|
|||||||
import Dialog from "primevue/dialog";
|
import Dialog from "primevue/dialog";
|
||||||
import RadioButton from "primevue/radiobutton";
|
import RadioButton from "primevue/radiobutton";
|
||||||
import Message from "primevue/message";
|
import Message from "primevue/message";
|
||||||
// US-111: Mobile Material Design components
|
// US-111/US-608: Mobile Material Design components
|
||||||
import MobileTopBar from "@shared/components/mobile/MobileTopBar.vue";
|
import MobileTopBar from "@shared/components/mobile/MobileTopBar.vue";
|
||||||
import MobileBottomNav from "@shared/components/mobile/MobileBottomNav.vue";
|
import MobileBottomNav from "@shared/components/mobile/MobileBottomNav.vue";
|
||||||
import MobileDrawerMenu from "@shared/components/mobile/MobileDrawerMenu.vue";
|
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const cacheStore = useCacheStore();
|
const cacheStore = useCacheStore();
|
||||||
const companyStore = useCompanyStore();
|
const companyStore = useCompanyStore();
|
||||||
|
const periodStore = useAccountingPeriodStore();
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
const authStore = useAuthStore();
|
const authStore = useAuthStore();
|
||||||
|
|
||||||
@@ -238,15 +230,8 @@ const userCacheEnabled = ref(true);
|
|||||||
const showClearDialog = ref(false);
|
const showClearDialog = ref(false);
|
||||||
const clearScope = ref("current");
|
const clearScope = ref("current");
|
||||||
|
|
||||||
// US-111: Mobile state
|
// US-111/US-608: Mobile state
|
||||||
const isMobile = ref(window.innerWidth < 768);
|
const isMobile = ref(window.innerWidth < 768);
|
||||||
const showDrawer = ref(false);
|
|
||||||
|
|
||||||
// Handle logout from drawer menu
|
|
||||||
const handleLogout = async () => {
|
|
||||||
await authStore.logout();
|
|
||||||
router.push('/login');
|
|
||||||
};
|
|
||||||
|
|
||||||
// US-111: Mobile TopBar actions (refresh only for cache stats)
|
// US-111: Mobile TopBar actions (refresh only for cache stats)
|
||||||
const mobileTopBarActions = computed(() => [
|
const mobileTopBarActions = computed(() => [
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- US-513: Simplified Mobile Top Bar - no filters/export since no detailed data -->
|
<!-- US-608: Mobile Top Bar with Back Button -->
|
||||||
<MobileTopBar
|
<MobileTopBar
|
||||||
v-if="isMobile"
|
v-if="isMobile"
|
||||||
title="Scadențe"
|
title="Scadențe"
|
||||||
@@ -7,12 +7,55 @@
|
|||||||
@back-click="goBack"
|
@back-click="goBack"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<main class="main-content" :class="{ 'mobile-layout': isMobile }">
|
<!-- US-608: Mobile Tabs (sticky below MobileTopBar) - like DetailedInvoicesView -->
|
||||||
|
<div v-if="isMobile && companyStore.selectedCompany" class="mobile-tabs-container">
|
||||||
|
<div class="mobile-tabs">
|
||||||
|
<button
|
||||||
|
class="mobile-tab"
|
||||||
|
:class="{ active: activeTab === 'clients' }"
|
||||||
|
@click="switchTab('clients')"
|
||||||
|
>
|
||||||
|
<span class="tab-label">Clienți</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="mobile-tab"
|
||||||
|
:class="{ active: activeTab === 'suppliers' }"
|
||||||
|
@click="switchTab('suppliers')"
|
||||||
|
>
|
||||||
|
<span class="tab-label">Furnizori</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<main class="main-content" :class="{ 'mobile-layout': isMobile, 'has-tabs': isMobile && companyStore.selectedCompany }">
|
||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
<!-- Page Header - only on desktop -->
|
<!-- Page Header - only on desktop -->
|
||||||
<div v-if="!isMobile" class="page-header">
|
<div v-if="!isMobile" class="page-header">
|
||||||
<h1 class="page-title">Analiză Scadențe</h1>
|
<div class="header-top">
|
||||||
<p class="page-subtitle">Analiza scadențelor clienți și furnizori</p>
|
<div>
|
||||||
|
<h1 class="page-title">Analiză Scadențe</h1>
|
||||||
|
<p class="page-subtitle">Analiza scadențelor clienți și furnizori</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- US-608: Desktop Tabs (in header) -->
|
||||||
|
<div v-if="companyStore.selectedCompany" class="desktop-tabs">
|
||||||
|
<button
|
||||||
|
class="desktop-tab"
|
||||||
|
:class="{ active: activeTab === 'clients' }"
|
||||||
|
@click="switchTab('clients')"
|
||||||
|
>
|
||||||
|
<i class="pi pi-users"></i>
|
||||||
|
<span>Clienți</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="desktop-tab"
|
||||||
|
:class="{ active: activeTab === 'suppliers' }"
|
||||||
|
@click="switchTab('suppliers')"
|
||||||
|
>
|
||||||
|
<i class="pi pi-building"></i>
|
||||||
|
<span>Furnizori</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Loading state when no company selected -->
|
<!-- Loading state when no company selected -->
|
||||||
@@ -30,14 +73,13 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Main content - MaturityAnalysisCard (US-513: doar analiza scadențelor, fără facturi detaliate) -->
|
<!-- US-608: Flat content structure - no card wrapper -->
|
||||||
<!-- US-602: Now with TabView for Clienți/Furnizori -->
|
<div v-else class="maturity-content">
|
||||||
<div v-else class="maturity-container">
|
|
||||||
<MaturityAnalysisCard
|
<MaturityAnalysisCard
|
||||||
ref="maturityCardRef"
|
ref="maturityCardRef"
|
||||||
:companyId="companyStore.selectedCompany?.id_firma"
|
:companyId="companyStore.selectedCompany?.id_firma"
|
||||||
|
:activeTab="activeTab"
|
||||||
@periodChanged="handlePeriodChange"
|
@periodChanged="handlePeriodChange"
|
||||||
@tabChanged="handleTabChange"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -49,7 +91,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
import Button from 'primevue/button'
|
import Button from 'primevue/button'
|
||||||
import MobileTopBar from '@shared/components/mobile/MobileTopBar.vue'
|
import MobileTopBar from '@shared/components/mobile/MobileTopBar.vue'
|
||||||
import MobileBottomNav from '@shared/components/mobile/MobileBottomNav.vue'
|
import MobileBottomNav from '@shared/components/mobile/MobileBottomNav.vue'
|
||||||
@@ -57,6 +99,7 @@ import MaturityAnalysisCard from '@reports/components/dashboard/cards/MaturityAn
|
|||||||
import { useCompanyStore } from '@reports/stores/sharedStores'
|
import { useCompanyStore } from '@reports/stores/sharedStores'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const route = useRoute()
|
||||||
const companyStore = useCompanyStore()
|
const companyStore = useCompanyStore()
|
||||||
|
|
||||||
// Detectare mobile - reactive with resize listener
|
// Detectare mobile - reactive with resize listener
|
||||||
@@ -66,6 +109,30 @@ const isMobile = computed(() => windowWidth.value < 768)
|
|||||||
// Ref to MaturityAnalysisCard
|
// Ref to MaturityAnalysisCard
|
||||||
const maturityCardRef = ref(null)
|
const maturityCardRef = ref(null)
|
||||||
|
|
||||||
|
// US-608: Tab state (persisted via URL query)
|
||||||
|
const TAB_STORAGE_KEY = 'maturity_analysis_active_tab'
|
||||||
|
|
||||||
|
const getInitialTab = () => {
|
||||||
|
// Check URL query first
|
||||||
|
if (route.query.tab === 'suppliers') return 'suppliers'
|
||||||
|
// Then localStorage
|
||||||
|
const stored = localStorage.getItem(TAB_STORAGE_KEY)
|
||||||
|
if (stored === 'suppliers') return 'suppliers'
|
||||||
|
return 'clients'
|
||||||
|
}
|
||||||
|
|
||||||
|
const activeTab = ref(getInitialTab())
|
||||||
|
|
||||||
|
// US-608: Switch tab and update URL
|
||||||
|
const switchTab = (tab) => {
|
||||||
|
activeTab.value = tab
|
||||||
|
localStorage.setItem(TAB_STORAGE_KEY, tab)
|
||||||
|
// Update URL without full navigation
|
||||||
|
router.replace({
|
||||||
|
query: tab === 'suppliers' ? { tab: 'suppliers' } : {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Handle window resize for mobile detection
|
// Handle window resize for mobile detection
|
||||||
const handleResize = () => {
|
const handleResize = () => {
|
||||||
windowWidth.value = window.innerWidth
|
windowWidth.value = window.innerWidth
|
||||||
@@ -81,11 +148,6 @@ const handlePeriodChange = (period) => {
|
|||||||
console.log('Maturity period changed:', period)
|
console.log('Maturity period changed:', period)
|
||||||
}
|
}
|
||||||
|
|
||||||
// US-602: Handle tab change
|
|
||||||
const handleTabChange = (tabIndex) => {
|
|
||||||
console.log('Tab changed:', tabIndex === 0 ? 'Clienți' : 'Furnizori')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
window.addEventListener('resize', handleResize)
|
window.addEventListener('resize', handleResize)
|
||||||
@@ -103,6 +165,11 @@ onUnmounted(() => {
|
|||||||
padding-bottom: 56px;
|
padding-bottom: 56px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* US-608: Extra padding when tabs are visible on mobile */
|
||||||
|
.main-content.mobile-layout.has-tabs {
|
||||||
|
padding-top: calc(56px + 48px);
|
||||||
|
}
|
||||||
|
|
||||||
/* App container */
|
/* App container */
|
||||||
.app-container {
|
.app-container {
|
||||||
max-width: 1400px;
|
max-width: 1400px;
|
||||||
@@ -117,11 +184,17 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Page Header - Desktop only */
|
/* Page Header - Desktop only */
|
||||||
/* US-514: Reduced from var(--space-xl) to var(--space-md) for less excessive top spacing */
|
|
||||||
.page-header {
|
.page-header {
|
||||||
margin-bottom: var(--space-md);
|
margin-bottom: var(--space-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-top {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-bottom: var(--space-md);
|
||||||
|
}
|
||||||
|
|
||||||
.page-title {
|
.page-title {
|
||||||
font-size: var(--text-3xl);
|
font-size: var(--text-3xl);
|
||||||
font-weight: var(--font-bold);
|
font-weight: var(--font-bold);
|
||||||
@@ -135,8 +208,102 @@ onUnmounted(() => {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Maturity container */
|
/* ================================================
|
||||||
.maturity-container {
|
US-608: Mobile Tabs (sticky below MobileTopBar)
|
||||||
|
Copied from DetailedInvoicesView for consistency
|
||||||
|
================================================ */
|
||||||
|
|
||||||
|
.mobile-tabs-container {
|
||||||
|
position: fixed;
|
||||||
|
top: 56px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: var(--z-sticky, 100);
|
||||||
|
background: var(--surface-card);
|
||||||
|
border-bottom: 1px solid var(--surface-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-tabs {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-tab {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: var(--space-md);
|
||||||
|
min-height: 48px;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
border-bottom: 2px solid transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all var(--transition-fast);
|
||||||
|
color: var(--text-color-secondary);
|
||||||
|
font-size: var(--text-sm);
|
||||||
|
font-weight: var(--font-medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-tab.active {
|
||||||
|
color: var(--color-primary);
|
||||||
|
border-bottom-color: var(--color-primary);
|
||||||
|
font-weight: var(--font-semibold);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mobile-tab:hover:not(.active) {
|
||||||
|
background: var(--surface-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-label {
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================
|
||||||
|
US-608: Desktop Tabs (in page-header)
|
||||||
|
================================================ */
|
||||||
|
|
||||||
|
.desktop-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--space-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.desktop-tab {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-sm);
|
||||||
|
padding: var(--space-sm) var(--space-lg);
|
||||||
|
background: var(--surface-hover);
|
||||||
|
border: 1px solid var(--surface-border);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all var(--transition-fast);
|
||||||
|
color: var(--text-color-secondary);
|
||||||
|
font-size: var(--text-sm);
|
||||||
|
font-weight: var(--font-medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
.desktop-tab:hover:not(.active) {
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.desktop-tab.active {
|
||||||
|
background: var(--color-primary);
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
color: var(--color-text-inverse, white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.desktop-tab i {
|
||||||
|
font-size: var(--text-base);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================
|
||||||
|
US-608: Flat content (no card wrapper)
|
||||||
|
================================================ */
|
||||||
|
|
||||||
|
.maturity-content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: var(--space-lg);
|
gap: var(--space-lg);
|
||||||
@@ -180,4 +347,40 @@ onUnmounted(() => {
|
|||||||
.empty-action {
|
.empty-action {
|
||||||
margin-top: var(--space-md);
|
margin-top: var(--space-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ================================================
|
||||||
|
Dark Mode Support
|
||||||
|
================================================ */
|
||||||
|
|
||||||
|
[data-theme="dark"] .mobile-tabs-container {
|
||||||
|
background: var(--surface-card);
|
||||||
|
border-bottom-color: var(--surface-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .mobile-tab.active {
|
||||||
|
color: var(--blue-400);
|
||||||
|
border-bottom-color: var(--blue-400);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .desktop-tab.active {
|
||||||
|
background: var(--blue-600);
|
||||||
|
border-color: var(--blue-600);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root:not([data-theme]) .mobile-tabs-container {
|
||||||
|
background: var(--surface-card);
|
||||||
|
border-bottom-color: var(--surface-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root:not([data-theme]) .mobile-tab.active {
|
||||||
|
color: var(--blue-400);
|
||||||
|
border-bottom-color: var(--blue-400);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root:not([data-theme]) .desktop-tab.active {
|
||||||
|
background: var(--blue-600);
|
||||||
|
border-color: var(--blue-600);
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,22 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="server-logs-view" :class="{ 'mobile-layout': isMobile }">
|
<div class="server-logs-view" :class="{ 'mobile-layout': isMobile }">
|
||||||
<!-- US-110: Mobile Material Design Top Bar -->
|
<!-- US-608: Mobile Material Design Top Bar with Back Button -->
|
||||||
<MobileTopBar
|
<MobileTopBar
|
||||||
v-if="isMobile"
|
v-if="isMobile"
|
||||||
title="Loguri Server"
|
title="Loguri Server"
|
||||||
:show-menu="true"
|
:show-back="true"
|
||||||
:actions="mobileTopBarActions"
|
:actions="mobileTopBarActions"
|
||||||
@menu-click="showDrawer = true"
|
@back-click="router.push('/settings')"
|
||||||
@action-click="handleTopBarAction"
|
@action-click="handleTopBarAction"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Mobile Drawer Menu (replaces old Sidebar) -->
|
|
||||||
<MobileDrawerMenu
|
|
||||||
v-model="showDrawer"
|
|
||||||
:user="authStore.user"
|
|
||||||
@logout="handleLogout"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Desktop Header -->
|
<!-- Desktop Header -->
|
||||||
<div class="stats-header" v-if="!isMobile">
|
<div class="stats-header" v-if="!isMobile">
|
||||||
<h1>
|
<h1>
|
||||||
@@ -140,11 +133,9 @@ import Tag from 'primevue/tag'
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
// US-110: Mobile Material Design components
|
// US-110/US-608: Mobile Material Design components
|
||||||
import MobileTopBar from '@shared/components/mobile/MobileTopBar.vue'
|
import MobileTopBar from '@shared/components/mobile/MobileTopBar.vue'
|
||||||
import MobileBottomNav from '@shared/components/mobile/MobileBottomNav.vue'
|
import MobileBottomNav from '@shared/components/mobile/MobileBottomNav.vue'
|
||||||
import MobileDrawerMenu from '@shared/components/mobile/MobileDrawerMenu.vue'
|
|
||||||
import { useAuthStore } from '@reports/stores/sharedStores'
|
|
||||||
|
|
||||||
// System API - endpoint separat de reports
|
// System API - endpoint separat de reports
|
||||||
const systemApi = axios.create({
|
const systemApi = axios.create({
|
||||||
@@ -162,7 +153,6 @@ systemApi.interceptors.request.use((config) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const authStore = useAuthStore()
|
|
||||||
|
|
||||||
// State
|
// State
|
||||||
const logs = ref([])
|
const logs = ref([])
|
||||||
@@ -182,15 +172,8 @@ const debugInfo = ref({
|
|||||||
|
|
||||||
let refreshTimer = null
|
let refreshTimer = null
|
||||||
|
|
||||||
// US-110: Mobile state
|
// US-110/US-608: Mobile state
|
||||||
const isMobile = ref(window.innerWidth < 768)
|
const isMobile = ref(window.innerWidth < 768)
|
||||||
const showDrawer = ref(false)
|
|
||||||
|
|
||||||
// Handle logout from drawer menu
|
|
||||||
const handleLogout = async () => {
|
|
||||||
await authStore.logout()
|
|
||||||
router.push('/login')
|
|
||||||
}
|
|
||||||
|
|
||||||
// US-110: Mobile TopBar actions (refresh, export)
|
// US-110: Mobile TopBar actions (refresh, export)
|
||||||
const mobileTopBarActions = computed(() => [
|
const mobileTopBarActions = computed(() => [
|
||||||
|
|||||||
@@ -12,111 +12,85 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Company & Period Selection (below header, above navigation) -->
|
<!-- Company & Period Selection (below header, above navigation) -->
|
||||||
<!-- US-605: Collapsible sections with localStorage persistence -->
|
<!-- US-608: Direct dropdowns (no collapsible sections) -->
|
||||||
<div v-if="companiesStore" class="drawer-selectors">
|
<div v-if="companiesStore" class="drawer-selectors">
|
||||||
<!-- Company Selector (Collapsible) -->
|
<!-- Company Selector (Direct Dropdown) -->
|
||||||
<div class="selector-group" :class="{ 'is-collapsed': companyCollapsed }">
|
<div class="selector-group">
|
||||||
<!-- Collapsible Header -->
|
<label class="selector-label">Firma</label>
|
||||||
<button
|
<button
|
||||||
class="collapsible-header"
|
class="selector-trigger"
|
||||||
@click="toggleCompanyCollapsed"
|
@click="toggleCompanyDropdown"
|
||||||
:aria-expanded="!companyCollapsed"
|
:aria-expanded="companyDropdownOpen"
|
||||||
>
|
>
|
||||||
<span class="collapsible-label">Firma</span>
|
<div class="selector-value">
|
||||||
<span v-if="companyCollapsed" class="collapsible-value">{{ selectedCompanyName }}</span>
|
<span class="selector-main">{{ selectedCompanyName }}</span>
|
||||||
<i class="pi" :class="companyCollapsed ? 'pi-chevron-down' : 'pi-chevron-up'"></i>
|
<span v-if="selectedCompanyCode" class="selector-sub">{{ selectedCompanyCode }}</span>
|
||||||
|
</div>
|
||||||
|
<i class="pi pi-chevron-down" :class="{ 'rotate-180': companyDropdownOpen }"></i>
|
||||||
</button>
|
</button>
|
||||||
|
<!-- Company Dropdown Panel -->
|
||||||
<!-- Expanded Content -->
|
<div v-if="companyDropdownOpen" class="selector-panel">
|
||||||
<div v-if="!companyCollapsed" class="collapsible-content">
|
<div class="selector-search">
|
||||||
<button
|
<i class="pi pi-search"></i>
|
||||||
class="selector-trigger"
|
<input
|
||||||
@click="toggleCompanyDropdown"
|
ref="companySearchInput"
|
||||||
:aria-expanded="companyDropdownOpen"
|
type="text"
|
||||||
>
|
v-model="companySearchQuery"
|
||||||
<div class="selector-value">
|
placeholder="Caută firmă..."
|
||||||
<span class="selector-main">{{ selectedCompanyName }}</span>
|
class="selector-search-input"
|
||||||
<span v-if="selectedCompanyCode" class="selector-sub">{{ selectedCompanyCode }}</span>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<i class="pi pi-chevron-down" :class="{ 'rotate-180': companyDropdownOpen }"></i>
|
<div class="selector-list">
|
||||||
</button>
|
<div
|
||||||
<!-- Company Dropdown Panel -->
|
v-for="company in filteredCompanies"
|
||||||
<div v-if="companyDropdownOpen" class="selector-panel">
|
:key="company.id_firma"
|
||||||
<div class="selector-search">
|
class="selector-item"
|
||||||
<i class="pi pi-search"></i>
|
:class="{ active: company.id_firma === companiesStore.selectedCompany?.id_firma }"
|
||||||
<input
|
@click="selectCompany(company)"
|
||||||
ref="companySearchInput"
|
>
|
||||||
type="text"
|
<div class="selector-item-content">
|
||||||
v-model="companySearchQuery"
|
<span class="selector-item-name">{{ company.name }}</span>
|
||||||
placeholder="Caută firmă..."
|
<span v-if="company.fiscal_code" class="selector-item-sub">CUI: {{ company.fiscal_code }}</span>
|
||||||
class="selector-search-input"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="selector-list">
|
|
||||||
<div
|
|
||||||
v-for="company in filteredCompanies"
|
|
||||||
:key="company.id_firma"
|
|
||||||
class="selector-item"
|
|
||||||
:class="{ active: company.id_firma === companiesStore.selectedCompany?.id_firma }"
|
|
||||||
@click="selectCompany(company)"
|
|
||||||
>
|
|
||||||
<div class="selector-item-content">
|
|
||||||
<span class="selector-item-name">{{ company.name }}</span>
|
|
||||||
<span v-if="company.fiscal_code" class="selector-item-sub">CUI: {{ company.fiscal_code }}</span>
|
|
||||||
</div>
|
|
||||||
<i v-if="company.id_firma === companiesStore.selectedCompany?.id_firma" class="pi pi-check"></i>
|
|
||||||
</div>
|
|
||||||
<div v-if="filteredCompanies.length === 0" class="selector-empty">
|
|
||||||
<i class="pi pi-info-circle"></i>
|
|
||||||
<span>Nu s-au găsit firme</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
<i v-if="company.id_firma === companiesStore.selectedCompany?.id_firma" class="pi pi-check"></i>
|
||||||
|
</div>
|
||||||
|
<div v-if="filteredCompanies.length === 0" class="selector-empty">
|
||||||
|
<i class="pi pi-info-circle"></i>
|
||||||
|
<span>Nu s-au găsit firme</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Period Selector (Collapsible) -->
|
<!-- Period Selector (Direct Dropdown) -->
|
||||||
<div v-if="periodStore && companiesStore.selectedCompany" class="selector-group" :class="{ 'is-collapsed': periodCollapsed }">
|
<div v-if="periodStore && companiesStore.selectedCompany" class="selector-group">
|
||||||
<!-- Collapsible Header -->
|
<label class="selector-label">Perioada</label>
|
||||||
<button
|
<button
|
||||||
class="collapsible-header"
|
class="selector-trigger"
|
||||||
@click="togglePeriodCollapsed"
|
@click="togglePeriodDropdown"
|
||||||
:aria-expanded="!periodCollapsed"
|
:aria-expanded="periodDropdownOpen"
|
||||||
>
|
>
|
||||||
<span class="collapsible-label">Perioada</span>
|
<div class="selector-value">
|
||||||
<span v-if="periodCollapsed" class="collapsible-value">{{ selectedPeriodDisplay }}</span>
|
<span class="selector-main">{{ selectedPeriodDisplay }}</span>
|
||||||
<i class="pi" :class="periodCollapsed ? 'pi-chevron-down' : 'pi-chevron-up'"></i>
|
</div>
|
||||||
|
<i class="pi pi-chevron-down" :class="{ 'rotate-180': periodDropdownOpen }"></i>
|
||||||
</button>
|
</button>
|
||||||
|
<!-- Period Dropdown Panel -->
|
||||||
<!-- Expanded Content -->
|
<div v-if="periodDropdownOpen" class="selector-panel">
|
||||||
<div v-if="!periodCollapsed" class="collapsible-content">
|
<div class="selector-list">
|
||||||
<button
|
<div
|
||||||
class="selector-trigger"
|
v-for="period in availablePeriods"
|
||||||
@click="togglePeriodDropdown"
|
:key="`${period.an}-${period.luna}`"
|
||||||
:aria-expanded="periodDropdownOpen"
|
class="selector-item"
|
||||||
>
|
:class="{ active: isPeriodSelected(period) }"
|
||||||
<div class="selector-value">
|
@click="selectPeriod(period)"
|
||||||
<span class="selector-main">{{ selectedPeriodDisplay }}</span>
|
>
|
||||||
|
<span class="selector-item-name">{{ period.display_name }}</span>
|
||||||
|
<i v-if="isPeriodSelected(period)" class="pi pi-check"></i>
|
||||||
</div>
|
</div>
|
||||||
<i class="pi pi-chevron-down" :class="{ 'rotate-180': periodDropdownOpen }"></i>
|
<div v-if="availablePeriods.length === 0" class="selector-empty">
|
||||||
</button>
|
<i class="pi pi-info-circle"></i>
|
||||||
<!-- Period Dropdown Panel -->
|
<span>Nu sunt perioade disponibile</span>
|
||||||
<div v-if="periodDropdownOpen" class="selector-panel">
|
|
||||||
<div class="selector-list">
|
|
||||||
<div
|
|
||||||
v-for="period in availablePeriods"
|
|
||||||
:key="`${period.an}-${period.luna}`"
|
|
||||||
class="selector-item"
|
|
||||||
:class="{ active: isPeriodSelected(period) }"
|
|
||||||
@click="selectPeriod(period)"
|
|
||||||
>
|
|
||||||
<span class="selector-item-name">{{ period.display_name }}</span>
|
|
||||||
<i v-if="isPeriodSelected(period)" class="pi pi-check"></i>
|
|
||||||
</div>
|
|
||||||
<div v-if="availablePeriods.length === 0" class="selector-empty">
|
|
||||||
<i class="pi pi-info-circle"></i>
|
|
||||||
<span>Nu sunt perioade disponibile</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -252,8 +226,8 @@ import { computed, ref, watch, nextTick, onMounted } from 'vue'
|
|||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MobileDrawerMenu - Material Design 3 inspired navigation drawer for mobile (v3)
|
* MobileDrawerMenu - Material Design 3 inspired navigation drawer for mobile (v4)
|
||||||
* US-605: Added collapsible Firma/Perioada sections
|
* US-608: Direct dropdown selectors (removed collapsible sections)
|
||||||
*
|
*
|
||||||
* Props:
|
* Props:
|
||||||
* - modelValue (v-model): Controls visibility of the drawer
|
* - modelValue (v-model): Controls visibility of the drawer
|
||||||
@@ -271,15 +245,16 @@ import { useRoute, useRouter } from 'vue-router'
|
|||||||
* Features:
|
* Features:
|
||||||
* - Slide-in animation from left
|
* - Slide-in animation from left
|
||||||
* - Header with ROA2WEB logo
|
* - Header with ROA2WEB logo
|
||||||
* - Company & Period selectors (below header, like desktop)
|
* - Company & Period selectors as direct dropdowns (1-tap interaction)
|
||||||
* - Navigation organized into 4 category sections:
|
* - Navigation organized into 4 category sections:
|
||||||
* - PRINCIPALE: Dashboard, Bonuri
|
* - PRINCIPALE: Dashboard, Bonuri
|
||||||
* - RAPOARTE: Facturi, Balanță, Casă, Bancă (US-519: separate pages)
|
* - RAPOARTE: Facturi, Balanță, Casă, Bancă
|
||||||
* - ANALIZE: Scadențe, Facturi Detaliate
|
* - ANALIZE: Scadențe, Facturi Detaliate
|
||||||
* - ADMINISTRARE: Setări
|
* - ADMINISTRARE: Setări
|
||||||
* - Visual separators between sections
|
* - Visual separators between sections
|
||||||
* - Active state highlighting based on current route
|
* - Active state highlighting based on current route
|
||||||
* - Profile section with user name and logout button (footer)
|
* - Profile section with user name, logout button, and theme toggle
|
||||||
|
* - Theme toggle cycles through: Auto → Light → Dark
|
||||||
* - Close on tap outside or on link click
|
* - Close on tap outside or on link click
|
||||||
* - Full dark mode support
|
* - Full dark mode support
|
||||||
* - Teleported to body to avoid z-index issues
|
* - Teleported to body to avoid z-index issues
|
||||||
@@ -341,55 +316,7 @@ const companySearchInput = ref(null)
|
|||||||
// Period selector state
|
// Period selector state
|
||||||
const periodDropdownOpen = ref(false)
|
const periodDropdownOpen = ref(false)
|
||||||
|
|
||||||
// Collapsible section state (US-605)
|
// US-608: Removed collapsible state management - using direct dropdowns now
|
||||||
// Default to collapsed, persisted in localStorage
|
|
||||||
const COLLAPSED_STORAGE_KEY = 'mobile-drawer-sections-collapsed'
|
|
||||||
|
|
||||||
const loadCollapsedState = () => {
|
|
||||||
try {
|
|
||||||
const saved = localStorage.getItem(COLLAPSED_STORAGE_KEY)
|
|
||||||
if (saved) {
|
|
||||||
return JSON.parse(saved)
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// Ignore parse errors
|
|
||||||
}
|
|
||||||
// Default: both sections collapsed
|
|
||||||
return { company: true, period: true }
|
|
||||||
}
|
|
||||||
|
|
||||||
const savedCollapsedState = loadCollapsedState()
|
|
||||||
const companyCollapsed = ref(savedCollapsedState.company)
|
|
||||||
const periodCollapsed = ref(savedCollapsedState.period)
|
|
||||||
|
|
||||||
const saveCollapsedState = () => {
|
|
||||||
try {
|
|
||||||
localStorage.setItem(COLLAPSED_STORAGE_KEY, JSON.stringify({
|
|
||||||
company: companyCollapsed.value,
|
|
||||||
period: periodCollapsed.value
|
|
||||||
}))
|
|
||||||
} catch (e) {
|
|
||||||
// Ignore storage errors
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const toggleCompanyCollapsed = () => {
|
|
||||||
companyCollapsed.value = !companyCollapsed.value
|
|
||||||
// Close dropdown if collapsing
|
|
||||||
if (companyCollapsed.value) {
|
|
||||||
companyDropdownOpen.value = false
|
|
||||||
}
|
|
||||||
saveCollapsedState()
|
|
||||||
}
|
|
||||||
|
|
||||||
const togglePeriodCollapsed = () => {
|
|
||||||
periodCollapsed.value = !periodCollapsed.value
|
|
||||||
// Close dropdown if collapsing
|
|
||||||
if (periodCollapsed.value) {
|
|
||||||
periodDropdownOpen.value = false
|
|
||||||
}
|
|
||||||
saveCollapsedState()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Computed properties for company selector
|
// Computed properties for company selector
|
||||||
const selectedCompanyName = computed(() => {
|
const selectedCompanyName = computed(() => {
|
||||||
@@ -855,67 +782,9 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================
|
/* ================================================
|
||||||
Collapsible Sections (US-605)
|
US-608: Collapsible styles removed - using direct dropdowns
|
||||||
================================================ */
|
================================================ */
|
||||||
|
|
||||||
.collapsible-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
padding: var(--space-sm) var(--space-md);
|
|
||||||
background: transparent;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
min-height: 44px;
|
|
||||||
gap: var(--space-sm);
|
|
||||||
transition: background var(--transition-fast);
|
|
||||||
border-radius: var(--radius-md);
|
|
||||||
}
|
|
||||||
|
|
||||||
.collapsible-header:hover {
|
|
||||||
background: var(--surface-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
.collapsible-header:active {
|
|
||||||
background: var(--surface-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
.collapsible-label {
|
|
||||||
font-size: var(--text-xs);
|
|
||||||
font-weight: var(--font-medium);
|
|
||||||
color: var(--text-color-secondary);
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.05em;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.collapsible-value {
|
|
||||||
flex: 1;
|
|
||||||
font-size: var(--text-sm);
|
|
||||||
font-weight: var(--font-medium);
|
|
||||||
color: var(--text-color);
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.collapsible-header .pi {
|
|
||||||
font-size: var(--text-xs);
|
|
||||||
color: var(--text-color-secondary);
|
|
||||||
flex-shrink: 0;
|
|
||||||
transition: transform var(--transition-fast);
|
|
||||||
}
|
|
||||||
|
|
||||||
.collapsible-content {
|
|
||||||
padding-top: var(--space-xs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* When collapsed, the selector-group has minimal padding */
|
|
||||||
.selector-group.is-collapsed {
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================================================
|
/* ================================================
|
||||||
Navigation Sections Container
|
Navigation Sections Container
|
||||||
US-606: No longer independently scrollable, part of unified scroll
|
US-606: No longer independently scrollable, part of unified scroll
|
||||||
@@ -1022,9 +891,11 @@ onMounted(() => {
|
|||||||
gap: var(--space-xs);
|
gap: var(--space-xs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* US-607: Compact variant - reduced spacing */
|
/* US-607/US-608: Compact variant - reduced spacing but with MobileBottomNav clearance */
|
||||||
.drawer-profile--compact {
|
.drawer-profile--compact {
|
||||||
padding: var(--space-sm) var(--space-md);
|
padding: var(--space-sm) var(--space-md);
|
||||||
|
/* US-608: Ensure theme toggle is visible above MobileBottomNav (56px) */
|
||||||
|
padding-bottom: calc(56px + var(--space-md));
|
||||||
gap: var(--space-xs);
|
gap: var(--space-xs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1279,23 +1150,6 @@ onMounted(() => {
|
|||||||
color: var(--text-color-secondary);
|
color: var(--text-color-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dark mode: Collapsible Sections (US-605) */
|
|
||||||
[data-theme="dark"] .collapsible-header:hover {
|
|
||||||
background: var(--surface-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="dark"] .collapsible-label {
|
|
||||||
color: var(--text-color-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="dark"] .collapsible-value {
|
|
||||||
color: var(--text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="dark"] .collapsible-header .pi {
|
|
||||||
color: var(--text-color-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="dark"] .drawer-link {
|
[data-theme="dark"] .drawer-link {
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
@@ -1479,23 +1333,6 @@ onMounted(() => {
|
|||||||
color: var(--text-color-secondary);
|
color: var(--text-color-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Auto dark mode: Collapsible Sections (US-605) */
|
|
||||||
:root:not([data-theme]) .collapsible-header:hover {
|
|
||||||
background: var(--surface-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
:root:not([data-theme]) .collapsible-label {
|
|
||||||
color: var(--text-color-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
:root:not([data-theme]) .collapsible-value {
|
|
||||||
color: var(--text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
:root:not([data-theme]) .collapsible-header .pi {
|
|
||||||
color: var(--text-color-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
:root:not([data-theme]) .drawer-link {
|
:root:not([data-theme]) .drawer-link {
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|||||||
133
tasks/prd-mobile-ui-fixes-phase5.md
Normal file
133
tasks/prd-mobile-ui-fixes-phase5.md
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
# PRD: Mobile UI Fixes Phase 5
|
||||||
|
|
||||||
|
## 1. Introducere
|
||||||
|
|
||||||
|
Corecții pentru regresii și probleme de UX identificate după implementările din faza anterioară. Include repararea componentelor care nu mai funcționează (FAB, meniu acțiuni), corectarea navigării pe paginile Analize, și optimizarea spațiului vizual.
|
||||||
|
|
||||||
|
## 2. Obiective
|
||||||
|
|
||||||
|
### Obiectiv Principal
|
||||||
|
- Restabilirea funcționalității complete pe Lista Bonuri (FAB + meniu acțiuni per bon)
|
||||||
|
- Corectarea pattern-ului de navigare pe paginile Analize
|
||||||
|
|
||||||
|
### Obiective Secundare
|
||||||
|
- Mărirea spațiului de afișare pentru tabelul de scadențe
|
||||||
|
- Reducerea spațiului gol excesiv din partea de sus a paginilor
|
||||||
|
|
||||||
|
### Metrici de Succes
|
||||||
|
- Toate funcționalitățile interactive funcționează pe mobil și desktop
|
||||||
|
- Build-ul trece fără erori sau warnings relevante
|
||||||
|
- Verificare vizuală cu Playwright confirmă corecturile
|
||||||
|
|
||||||
|
## 3. User Stories
|
||||||
|
|
||||||
|
### US-701: Reparare SpeedDial FAB pe Lista Bonuri
|
||||||
|
**Ca** utilizator pe mobil
|
||||||
|
**Vreau** să văd butonul + în dreapta jos și să pot crea bonuri noi
|
||||||
|
**Pentru că** este metoda principală de adăugare bonuri pe mobil
|
||||||
|
|
||||||
|
**Acceptance Criteria:**
|
||||||
|
- [ ] SpeedDial este importat corect din 'primevue/speeddial'
|
||||||
|
- [ ] Butonul + apare în dreapta jos pe mobil (când nu este în selection mode)
|
||||||
|
- [ ] Click pe + deschide opțiunile: Bon Nou Manual, Scanare OCR, Upload în Masă
|
||||||
|
- [ ] npm run build passes fără warnings legate de SpeedDial
|
||||||
|
- [ ] Verify in browser: FAB vizibil și funcțional pe mobil
|
||||||
|
|
||||||
|
### US-702: Verificare Meniu Acțiuni (...) Per Bon
|
||||||
|
**Ca** utilizator
|
||||||
|
**Vreau** să pot accesa meniul de acțiuni pentru fiecare bon
|
||||||
|
**Pentru că** trebuie să pot edita, șterge, valida bonurile individual
|
||||||
|
|
||||||
|
**Acceptance Criteria:**
|
||||||
|
- [ ] Butonul ... pe fiecare bon deschide meniul cu opțiuni
|
||||||
|
- [ ] Meniul conține opțiunile corecte în funcție de status (Edit, View, Delete, Approve, etc.)
|
||||||
|
- [ ] Meniul se poziționează corect pe mobil și desktop
|
||||||
|
- [ ] npm run typecheck passes
|
||||||
|
|
||||||
|
### US-703: Navigare Hamburger pe Paginile Analize
|
||||||
|
**Ca** utilizator pe mobil
|
||||||
|
**Vreau** să văd meniul hamburger pe paginile Analize (nu săgeata înapoi)
|
||||||
|
**Pentru că** trebuie să pot accesa firma, perioada și navigarea din orice pagină
|
||||||
|
|
||||||
|
**Acceptance Criteria:**
|
||||||
|
- [ ] MaturityAnalysisView (Analize Scadențe) folosește `:show-menu="true"` nu `:show-back="true"`
|
||||||
|
- [ ] DetailedInvoicesView (Facturi Detaliate) folosește `:show-menu="true"` nu `:show-back="true"`
|
||||||
|
- [ ] Click pe hamburger deschide MobileDrawerMenu
|
||||||
|
- [ ] Pattern-ul este consistent cu InvoicesView și CashView/BankView
|
||||||
|
- [ ] Verify in browser: hamburger icon vizibil, drawer se deschide
|
||||||
|
|
||||||
|
### US-704: Mărire Spațiu Tabel Scadențe
|
||||||
|
**Ca** utilizator
|
||||||
|
**Vreau** să văd mai multe rânduri în tabelul de scadențe fără să scrollez
|
||||||
|
**Pentru că** acum încap doar 3-4 rânduri și trebuie să scrollez constant
|
||||||
|
|
||||||
|
**Acceptance Criteria:**
|
||||||
|
- [ ] max-height pentru .maturity-list crescut de la 250px la 400px pe desktop
|
||||||
|
- [ ] max-height pentru .maturity-list crescut de la 200px la 350px pe mobil
|
||||||
|
- [ ] min-height pentru .tab-content redus sau eliminat pentru a nu forța scroll
|
||||||
|
- [ ] Verify in browser: cel puțin 8-10 rânduri vizibile fără scroll pe desktop
|
||||||
|
|
||||||
|
### US-705: Reducere Padding/Margin Top Excesiv
|
||||||
|
**Ca** utilizator
|
||||||
|
**Vreau** să văd conținutul imediat fără spațiu gol excesiv în partea de sus
|
||||||
|
**Pentru că** acum partea de sus a paginilor are prea mult spațiu nefolosit
|
||||||
|
|
||||||
|
**Acceptance Criteria:**
|
||||||
|
- [ ] Reducere margin-bottom pe .page-header de la var(--space-md) la var(--space-sm)
|
||||||
|
- [ ] Verificare padding-top pe .app-container - eliminare dacă e redundant
|
||||||
|
- [ ] Verificare că modificările nu afectează negativ alte pagini
|
||||||
|
- [ ] Verificare în mod desktop și mobil
|
||||||
|
- [ ] npm run typecheck passes
|
||||||
|
|
||||||
|
## 4. Cerințe Funcționale
|
||||||
|
|
||||||
|
1. [REQ-001] SpeedDial trebuie importat și funcțional pe ReceiptsListView
|
||||||
|
2. [REQ-002] Meniul de acțiuni per bon trebuie să se deschidă la click pe ...
|
||||||
|
3. [REQ-003] Paginile Analize trebuie să aibă hamburger menu, nu back button
|
||||||
|
4. [REQ-004] Tabelul de scadențe trebuie să afișeze minimum 8 rânduri fără scroll
|
||||||
|
5. [REQ-005] Spațiul gol din partea de sus a paginilor trebuie redus
|
||||||
|
|
||||||
|
## 5. Non-Goals (Ce NU facem)
|
||||||
|
|
||||||
|
- Nu modificăm logica de business a bonurilor sau scadențelor
|
||||||
|
- Nu schimbăm stilul general Material Design
|
||||||
|
- Nu adăugăm funcționalități noi, doar corectăm cele existente
|
||||||
|
- Nu modificăm paginile admin (CacheStats, ServerLogs) - acele back buttons sunt corecte
|
||||||
|
|
||||||
|
## 6. Considerații Tehnice
|
||||||
|
|
||||||
|
### Stack/Tehnologii
|
||||||
|
- Vue.js 3 cu Composition API
|
||||||
|
- PrimeVue (SpeedDial, Menu components)
|
||||||
|
- CSS Design Tokens
|
||||||
|
|
||||||
|
### Patterns de Urmat
|
||||||
|
- InvoicesView.vue pentru pattern-ul hamburger menu
|
||||||
|
- CSS_PATTERNS.md pentru spacing tokens
|
||||||
|
|
||||||
|
### Fișiere de Modificat
|
||||||
|
| Fișier | Modificări |
|
||||||
|
|--------|------------|
|
||||||
|
| `src/modules/data-entry/views/receipts/ReceiptsListView.vue` | Import SpeedDial, verificare menuItems |
|
||||||
|
| `src/modules/reports/views/MaturityAnalysisView.vue` | show-menu în loc de show-back, reducere padding |
|
||||||
|
| `src/modules/reports/views/DetailedInvoicesView.vue` | show-menu în loc de show-back |
|
||||||
|
| `src/modules/reports/components/dashboard/cards/MaturityAnalysisCard.vue` | Mărire max-height maturity-list |
|
||||||
|
|
||||||
|
### Riscuri Tehnice
|
||||||
|
- SpeedDial mask-ul CSS poate necesita ajustări pentru dark mode
|
||||||
|
- Verificare că nu există conflicte cu MobileBottomNav pe z-index
|
||||||
|
|
||||||
|
## 7. Considerații UI/UX
|
||||||
|
|
||||||
|
- FAB button trebuie să fie vizibil deasupra MobileBottomNav
|
||||||
|
- Meniul de acțiuni trebuie să aibă touch targets de min 44px
|
||||||
|
- Hamburger menu trebuie să fie consistent pe toate paginile de rapoarte
|
||||||
|
|
||||||
|
## 8. Success Metrics
|
||||||
|
- 0 erori de build
|
||||||
|
- Toate cele 5 user stories verificate vizual cu Playwright
|
||||||
|
- Consistență navigare pe toate paginile de rapoarte
|
||||||
|
|
||||||
|
## 9. Open Questions
|
||||||
|
- [x] Cauză FAB nefuncțional: SpeedDial nu este importat (CONFIRMAT)
|
||||||
|
- [ ] Cauză meniu acțiuni nefuncțional: De verificat dacă menuItems e populat corect
|
||||||
Reference in New Issue
Block a user