feat(mobile-fixes-phase3): Complete US-311 - Actualizare Documentație MOBILE_PATTERNS.md

Implemented by Ralph autonomous loop.
Iteration: 9

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-01-12 16:59:01 +00:00
parent 7cf8344e89
commit ac2966c629
3 changed files with 609 additions and 70 deletions

View File

@@ -1,8 +1,8 @@
# ROA2WEB Mobile Patterns Library
**Version:** 2.0.0
**Version:** 3.0.0
**Last Updated:** 2026-01-12
**Status:** ✅ Complete
**Status:** ✅ Complete (Phase 3 - Mobile Navigation Overhaul)
---
@@ -12,15 +12,17 @@
2. [Mobile Layout Overview](#mobile-layout-overview)
3. [Navigation Architecture](#navigation-architecture)
4. [MobileTopBar](#mobiletopbar)
5. [MobileBottomNav](#mobilebottomnav)
6. [MobileDrawerMenu](#mobiledrawermenu) *(NEW)*
7. [MobileActionBar](#mobileactionbar) *(NEW)*
8. [MobileSelectionFooter](#mobileselectionfooter)
9. [BottomSheet](#bottomsheet)
10. [SwipeableCards](#swipeablecards)
11. [Design Tokens for Mobile](#design-tokens-for-mobile)
12. [Best Practices](#best-practices)
13. [Troubleshooting](#troubleshooting)
5. [MobileBottomNav](#mobilebottomnav) *(UPDATED - Phase 3)*
6. [MobileDrawerMenu](#mobiledrawermenu) *(UPDATED - Phase 3)*
7. [Mobile Tab Pattern](#mobile-tab-pattern) *(NEW - Phase 3)*
8. [FAB Pattern](#fab-pattern) *(NEW - Phase 3)*
9. [MobileActionBar](#mobileactionbar)
10. [MobileSelectionFooter](#mobileselectionfooter)
11. [BottomSheet](#bottomsheet)
12. [SwipeableCards](#swipeablecards)
13. [Design Tokens for Mobile](#design-tokens-for-mobile)
14. [Best Practices](#best-practices)
15. [Troubleshooting](#troubleshooting)
---
@@ -104,12 +106,12 @@ import SwipeableCards from '@shared/components/mobile/SwipeableCards.vue'
## Mobile Layout Overview
### ASCII Diagram: Standard Mobile Layout
### ASCII Diagram: Standard Mobile Layout (Phase 3)
```
┌─────────────────────────────────────────┐
│ MobileTopBar (56px) │
│ [≡] Page Title [🔍] [] │
│ [≡] Page Title [📥] [🔍] │
├─────────────────────────────────────────┤
│ │
│ │
@@ -119,16 +121,21 @@ import SwipeableCards from '@shared/components/mobile/SwipeableCards.vue'
│ │
│ padding-top: 56px │
│ padding-bottom: 56px │
[+] ← FAB (contextual)
│ │
├─────────────────────────────────────────┤
│ MobileBottomNav (56px) │
📋 ☁️ 📊 ⚙️
Bonuri Upload Rapoarte Setări
🏠 📋 📄 ⚙️ │
Dashboard Bonuri Facturi Setări │
└─────────────────────────────────────────┘
```
### ASCII Diagram: Drawer Menu Open
> **Phase 3 Changes:**
> - Footer Nav: 4 direct links (Dashboard, Bonuri, Facturi, Setări)
> - Upload moved to FAB on Bonuri page
> - Rapoarte removed from footer (accessible via Hamburger Menu)
### ASCII Diagram: Drawer Menu Open (Phase 3 - Grouped Categories)
```
┌──────────────────┬──────────────────────┐
@@ -137,11 +144,20 @@ import SwipeableCards from '@shared/components/mobile/SwipeableCards.vue'
├──────────────────┤ ░░░ OVERLAY ░░░░░░░░ │
│ 🏢 ROA2WEB │ ░░ (tap to close) ░░ │
├──────────────────┤ ░░░░░░░░░░░░░░░░░░░░ │
│ PRINCIPALE │ ░░░░░░░░░░░░░░░░░░░░ │
│ 🏠 Dashboard │ ░░░░░░░░░░░░░░░░░░░░ │
│ 📋 Bonuri │ ░░░░░░░░░░░░░░░░░░░░ │
│ ────────────── │ ░░░░░░░░░░░░░░░░░░░░ │
│ RAPOARTE │ ░░░░░░░░░░░░░░░░░░░░ │
│ 📄 Facturi │ ░░░░░░░░░░░░░░░░░░░░ │
│ 🔢 Balanță │ ░░░░░░░░░░░░░░░░░░░░ │
│ 💰 Trezorerie │ ░░░░░░░░░░░░░░░░░░░░ │
│ 💰 Casa și Banca│ ░░░░░░░░░░░░░░░░░░░░ │
│ ────────────── │ ░░░░░░░░░░░░░░░░░░░░ │
│ ANALIZE │ ░░░░░░░░░░░░░░░░░░░░ │
│ ⏰ Scadențe │ ░░░░░░░░░░░░░░░░░░░░ │
│ 📋 Facturi Det. │ ░░░░░░░░░░░░░░░░░░░░ │
│ ────────────── │ ░░░░░░░░░░░░░░░░░░░░ │
│ ADMINISTRARE │ ░░░░░░░░░░░░░░░░░░░░ │
│ ⚙️ Setări │ ░░░░░░░░░░░░░░░░░░░░ │
├──────────────────┤ ░░░░░░░░░░░░░░░░░░░░ │
│ 👤 Username │ ░░░░░░░░░░░░░░░░░░░░ │
@@ -149,6 +165,13 @@ import SwipeableCards from '@shared/components/mobile/SwipeableCards.vue'
└──────────────────┴──────────────────────┘
```
> **Phase 3 Changes:**
> - Navigation organized into 4 category sections with visual separators
> - PRINCIPALE: Dashboard, Bonuri
> - RAPOARTE: Facturi, Balanță, Casa și Banca
> - ANALIZE: Scadențe, Facturi Detaliate
> - ADMINISTRARE: Setări
### ASCII Diagram: Selection Mode Layout
```
@@ -241,16 +264,70 @@ import SwipeableCards from '@shared/components/mobile/SwipeableCards.vue'
└─────────────────────────────────────────┘
```
### ASCII Diagram: Mobile Tab Pattern (Phase 3)
```
┌─────────────────────────────────────────┐
│ MobileTopBar (56px) │
│ [≡] Facturi [📥] [🔍] │
├─────────────────────────────────────────┤
│┌──────────────────┬────────────────────┐│
││ Clienți │ Furnizori ││ ← Full-width tabs
││ ═══════════ │ ││ (Material Design 3)
│└──────────────────┴────────────────────┘│
├─────────────────────────────────────────┤
│ │
│ TAB CONTENT │
│ (changes based on tab) │
│ │
│ padding-top: 104px (56+48) │
│ │
├─────────────────────────────────────────┤
│ MobileBottomNav (56px) │
│ 🏠 📋 📄 ⚙️ │
│ Dashboard Bonuri Facturi Setări │
└─────────────────────────────────────────┘
```
> **Used in:** Facturi (`/reports/invoices`), Scadențe (`/reports/maturity-analysis`)
### ASCII Diagram: FAB Pattern (Phase 3)
```
┌─────────────────────────────────────────┐
│ MobileTopBar (56px) │
│ [≡] Bonuri [📥] [🔍] │
├─────────────────────────────────────────┤
│ │
│ MAIN CONTENT │
│ │
│ ┌─────────────┐ │
│ │ Upload │ │ ← FAB Popup Menu
│ │ Bulk │ │ (appears on tap)
│ ├─────────────┤ │
│ │ Bon Nou │ │
│ └─────────────┘ │
│ [+] │ ← FAB (56x56px)
│ │ bottom: 72px
├─────────────────────────────────────────┤
│ MobileBottomNav (56px) │
│ 🏠 📋 📄 ⚙️ │
│ Dashboard Bonuri Facturi Setări │
└─────────────────────────────────────────┘
```
> **Used in:** Bonuri page (`/data-entry`) - Upload moved from footer to FAB
---
## Navigation Architecture
### Route Map
### Route Map (Phase 3 - Updated)
```
┌─────────────────────────────────────┐
│ / (root) │
│ Redirects to /reports/dashboard │
│ Redirects to /dashboard
└───────────────┬─────────────────────┘
┌───────────────────────────┼───────────────────────────┐
@@ -259,18 +336,33 @@ import SwipeableCards from '@shared/components/mobile/SwipeableCards.vue'
┌───────────────────┐ ┌───────────────────────┐ ┌─────────────────┐
│ /data-entry │ │ /reports │ │ /settings │
│ │ │ │ │ │
│ 📋 Bonuri List │ │ 📊 dashboard │ │ ⚙️ Settings Hub │
│ 📝 Bon Create │ │ 📄 invoices │ │ (centralized │
│ ✏️ Bon Edit │ │ 💰 bank-cash │ │ settings) │
│ 📊 OCR Metrics │ │ 🔢 trial-balance │ │ │
│ │ 📉 maturity-analysis │ │ Links to: │
│ │ │ 📋 detailed-invoices │ │ - OCR Settings │
│ │ 📈 cache-stats │ │ - Cache Stats │
│ │ 📝 server-logs │ │ - Server Logs │
📱 telegram │ │ - Telegram │
│ 📋 Bonuri List │ │ 📄 invoices [TABS] │ │ ⚙️ Settings Hub │
│ 📝 Bon Create │ │ 💰 bank-cash │ │ (centralized │
│ ✏️ Bon Edit │ │ 🔢 trial-balance │ │ settings) │
│ 📊 OCR Metrics │ │ 📉 maturity [TABS] │ │ │
☁️ Bulk Upload │ │ 📋 detailed-invoices │ │ Links to: │
│ │ │ 📈 cache-stats │ │ - OCR Settings │
[FAB: +] │ │ 📝 server-logs │ │ - Cache Stats │
- Bon Nou │ │ 📱 telegram │ │ - Server Logs │
- Upload Bulk │ │ - Telegram │
└───────────────────┘ └───────────────────────┘ └─────────────────┘
/dashboard
┌─────────────────────┐
│ 🏠 Dashboard │ ← Primary entry point (Footer Nav)
│ SwipeableCards KPIs│
│ (No quick-links │
│ on mobile) │
└─────────────────────┘
```
> **Phase 3 Navigation Changes:**
> - `/dashboard` is now a direct route in Footer Nav (not under `/reports`)
> - Invoices and Maturity Analysis have Clienți/Furnizori tabs
> - FAB on Bonuri page provides Bon Nou + Upload Bulk actions
### Navigation Flow: Settings Hub Pattern
The Settings Hub (`/settings`) is the centralized entry point for all administrative pages:
@@ -303,58 +395,95 @@ MobileBottomNav "Setări" button
└───────────────────────────────────┘
```
### MobileBottomNav Default Items (Updated)
### MobileBottomNav Default Items (Phase 3 - 4 Direct Links)
```javascript
// Default navigation items
// Default navigation items - Phase 3 (4 links, no action button)
[
{ to: '/dashboard', icon: 'pi pi-home', label: 'Dashboard' },
{ to: '/data-entry', icon: 'pi pi-receipt', label: 'Bonuri' },
{ icon: 'pi pi-cloud-upload', label: 'Upload' }, // Action button
{ to: '/reports/dashboard', icon: 'pi pi-chart-bar', label: 'Rapoarte' },
{ to: '/settings', icon: 'pi pi-cog', label: 'Setări' } // → Settings Hub
]
```
### MobileDrawerMenu Navigation Links
```javascript
// Drawer menu navigation items
[
{ to: '/reports/dashboard', icon: 'pi pi-home', label: 'Dashboard' },
{ to: '/data-entry', icon: 'pi pi-receipt', label: 'Bonuri' },
{ to: '/reports/invoices', icon: 'pi pi-file', label: 'Facturi' },
{ to: '/reports/trial-balance', icon: 'pi pi-calculator', label: 'Balanță' },
{ to: '/reports/bank-cash', icon: 'pi pi-money-bill', label: 'Trezorerie' },
{ to: '/reports/invoices', icon: 'pi pi-file-edit', label: 'Facturi' },
{ to: '/settings', icon: 'pi pi-cog', label: 'Setări' }
]
```
### Dashboard Mobile Quick Links
> **Phase 3 Changes:**
> - Upload button removed from footer (moved to FAB on Bonuri page)
> - Dashboard added as direct link
> - Facturi added as direct link (with Clienți/Furnizori tabs)
> - All 4 items are now router links (no action buttons)
On mobile, Dashboard shows KPI cards plus quick-link cards:
### MobileDrawerMenu Navigation Links (Phase 3 - Grouped Categories)
```javascript
// PRINCIPALE: Primary navigation
const principaleItems = [
{ to: '/dashboard', icon: 'pi pi-home', label: 'Dashboard', exactMatch: true },
{ to: '/data-entry', icon: 'pi pi-receipt', label: 'Bonuri', exactMatch: false }
]
// RAPOARTE: Reports section
const rapoarteItems = [
{ to: '/reports/invoices', icon: 'pi pi-file', label: 'Facturi', exactMatch: true },
{ to: '/reports/trial-balance', icon: 'pi pi-calculator', label: 'Balanță', exactMatch: true },
{ to: '/reports/bank-cash', icon: 'pi pi-money-bill', label: 'Casa și Banca', exactMatch: true }
]
// ANALIZE: Analysis section
const analizeItems = [
{ to: '/reports/maturity-analysis', icon: 'pi pi-clock', label: 'Scadențe', exactMatch: true },
{ to: '/reports/detailed-invoices', icon: 'pi pi-list', label: 'Facturi Detaliate', exactMatch: true }
]
// ADMINISTRARE: Admin section
const administrareItems = [
{ to: '/settings', icon: 'pi pi-cog', label: 'Setări', exactMatch: false }
]
```
> **Phase 3 Changes:**
> - Navigation items organized into 4 visual sections with headers
> - Visual dividers between sections (`.drawer-divider`)
> - Section headers styled as uppercase labels (PRINCIPALE, RAPOARTE, ANALIZE, ADMINISTRARE)
> - `exactMatch` flag for precise active state highlighting
### Dashboard Mobile Layout (Phase 3 - KPIs Only)
On mobile, Dashboard shows ONLY KPI cards (quick-links removed in Phase 3):
```
┌─────────────────────────────────────────┐
│ MobileTopBar: Dashboard │
│ [≡] Dashboard [📥] [🔍] │
├─────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────┐ │
│ │ SwipeableCards (KPIs) │ │
│ │ ← Swipe → │ │
│ │ │ │
│ │ Total Facturi: 125,430 RON │ │
│ │ Scadențe: 5 restante │ │
│ │ ... │ │
│ │ │ │
│ └─────────────────────────────────┘ │
│ │
┌─────────────┐ ┌─────────────┐
│ │ 📉 Analiză │ │ 📋 Facturi │ │
│ │ Scadențe → │ │ Detaliate → │ │
│ └─────────────┘ └─────────────┘ │
●━━━━●───●
│ │
│ (Quick links to dedicated pages)
│ (No quick-links on mobile - use
│ Footer Nav or Hamburger Menu) │
│ │
├─────────────────────────────────────────┤
│ MobileBottomNav
│ MobileBottomNav (56px)
│ 🏠 📋 📄 ⚙️ │
│ Dashboard Bonuri Facturi Setări │
└─────────────────────────────────────────┘
```
> **Phase 3 Changes:**
> - Quick-link cards removed from mobile Dashboard
> - Desktop Dashboard keeps quick-links (unchanged)
> - Navigation to other pages via Footer Nav or Hamburger Menu
---
## MobileTopBar
@@ -538,7 +667,9 @@ const handleNavClick = (item) => {
## MobileDrawerMenu
Material Design 3 inspired navigation drawer that slides in from the left. Replaces the desktop sidebar on mobile devices.
*(UPDATED - Phase 3)*
Material Design 3 inspired navigation drawer that slides in from the left. Replaces the desktop sidebar on mobile devices. **Phase 3** reorganized navigation into grouped categories with visual separators.
### Props
@@ -555,19 +686,61 @@ Material Design 3 inspired navigation drawer that slides in from the left. Repla
| `update:modelValue` | `boolean` | v-model update when visibility changes |
| `logout` | - | Emitted when logout is clicked (if no `onLogout` prop) |
### Navigation Items (Built-in)
### Navigation Items (Phase 3 - Grouped Categories)
The drawer contains the following navigation links:
The drawer navigation is organized into 4 sections with visual separators:
```javascript
[
{ to: '/reports/dashboard', icon: 'pi pi-home', label: 'Dashboard' },
{ to: '/data-entry', icon: 'pi pi-receipt', label: 'Bonuri' },
{ to: '/reports/invoices', icon: 'pi pi-file', label: 'Facturi' },
{ to: '/reports/trial-balance', icon: 'pi pi-calculator', label: 'Balanță' },
{ to: '/reports/bank-cash', icon: 'pi pi-money-bill', label: 'Trezorerie' },
{ to: '/settings', icon: 'pi pi-cog', label: 'Setări' }
// PRINCIPALE Section
const principaleItems = [
{ to: '/dashboard', icon: 'pi pi-home', label: 'Dashboard', exactMatch: true },
{ to: '/data-entry', icon: 'pi pi-receipt', label: 'Bonuri', exactMatch: false }
]
// RAPOARTE Section
const rapoarteItems = [
{ to: '/reports/invoices', icon: 'pi pi-file', label: 'Facturi', exactMatch: true },
{ to: '/reports/trial-balance', icon: 'pi pi-calculator', label: 'Balanță', exactMatch: true },
{ to: '/reports/bank-cash', icon: 'pi pi-money-bill', label: 'Casa și Banca', exactMatch: true }
]
// ANALIZE Section
const analizeItems = [
{ to: '/reports/maturity-analysis', icon: 'pi pi-clock', label: 'Scadențe', exactMatch: true },
{ to: '/reports/detailed-invoices', icon: 'pi pi-list', label: 'Facturi Detaliate', exactMatch: true }
]
// ADMINISTRARE Section
const administrareItems = [
{ to: '/settings', icon: 'pi pi-cog', label: 'Setări', exactMatch: false }
]
```
### Section Structure
```
┌──────────────────┐
│ 🏢 ROA2WEB │ ← Header with logo
├──────────────────┤
│ PRINCIPALE │ ← Section header (uppercase)
│ 🏠 Dashboard │
│ 📋 Bonuri │
│ ────────────── │ ← Divider
│ RAPOARTE │
│ 📄 Facturi │
│ 🔢 Balanță │
│ 💰 Casa și Banca│
│ ────────────── │
│ ANALIZE │
│ ⏰ Scadențe │
│ 📋 Facturi Det. │
│ ────────────── │
│ ADMINISTRARE │
│ ⚙️ Setări │
├──────────────────┤
│ 👤 Username │ ← Profile section (footer)
│ 🚪 Deconectare │
└──────────────────┘
```
### Basic Usage
@@ -631,15 +804,40 @@ const handleLogout = async () => {
| Class | Description |
|-------|-------------|
| `.drawer-overlay` | Full-screen backdrop (50% black) |
| `.drawer-overlay` | Full-screen backdrop (50% black, 70% in dark mode) |
| `.drawer-menu` | Main drawer container (280px, max 85vw) |
| `.drawer-header` | Header with logo |
| `.drawer-section` | Navigation links container |
| `.drawer-sections` | Scrollable container for all navigation sections |
| `.drawer-section` | Individual section container |
| `.section-header` | Section header label (PRINCIPALE, RAPOARTE, etc.) - uppercase, small text |
| `.drawer-nav` | List container for navigation links |
| `.drawer-link` | Individual navigation link (48px min-height) |
| `.drawer-link.active` | Active route styling (blue background) |
| `.drawer-divider` | Visual separator between sections |
| `.drawer-profile` | Profile section at bottom |
| `.profile-header` | User avatar and name row |
| `.profile-avatar` | Circular avatar with user icon |
| `.logout-link` | Red logout button styling |
### Section Header Styling
```css
.section-header {
padding: var(--space-sm) var(--space-lg);
font-size: var(--text-xs);
font-weight: var(--font-semibold);
color: var(--text-color-secondary);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.drawer-divider {
height: 1px;
background: var(--surface-border);
margin: var(--space-xs) var(--space-md);
}
```
### Animation
Uses Vue `<Transition name="drawer">`:
@@ -649,6 +847,330 @@ Uses Vue `<Transition name="drawer">`:
---
## Mobile Tab Pattern
*(NEW - Phase 3)*
Material Design 3 inspired full-width tabs for switching between Clienți and Furnizori views. Used in pages that display partner-related data.
### When to Use
Use the Mobile Tab Pattern when:
- A page shows data that can be filtered by partner type (Clienți/Furnizori)
- The tab switch should be persistent via URL query parameter
- Both tabs share the same filters (except type)
### Implementation
```vue
<template>
<!-- US-304/US-305: Mobile Tabs for Clienți/Furnizori -->
<div v-if="isMobile" 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>
</template>
<script setup>
import { ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter()
// Tab state synced with URL query param
const activeTab = ref(route.query.tab === 'suppliers' ? 'suppliers' : 'clients')
// Switch tab and update URL
const switchTab = (tab) => {
if (tab === activeTab.value) return
activeTab.value = tab
// Update URL query param without full navigation
router.replace({
query: { ...route.query, tab: tab === 'clients' ? undefined : tab }
})
// Trigger data reload with new type
loadData()
}
</script>
```
### CSS Classes
```css
/* Tab container - positioned below MobileTopBar */
.mobile-tabs-container {
position: fixed;
top: 56px; /* Below MobileTopBar */
left: 0;
right: 0;
z-index: 999;
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) var(--space-sm);
background: transparent;
border: none;
border-bottom: 2px solid transparent;
cursor: pointer;
transition: all var(--transition-fast);
color: var(--text-color-secondary);
min-height: 48px;
font-size: var(--text-base);
font-weight: var(--font-medium);
}
.mobile-tab:active {
background: var(--surface-hover);
}
.mobile-tab.active {
color: var(--color-primary);
border-bottom-color: var(--color-primary);
font-weight: var(--font-semibold);
}
```
### Content Padding
When using tabs, increase top padding to account for both bars:
```css
.mobile-content-with-tabs {
padding-top: 104px; /* 56px (TopBar) + 48px (Tabs) */
padding-bottom: 56px; /* BottomNav */
}
```
### Used In
| Page | Route | Description |
|------|-------|-------------|
| **Facturi** | `/reports/invoices` | Switch between client/supplier invoices |
| **Scadențe** | `/reports/maturity-analysis` | Switch between client/supplier maturity |
---
## FAB Pattern
*(NEW - Phase 3)*
Material Design 3 inspired Floating Action Button (FAB) for contextual actions. Positioned above MobileBottomNav with popup menu support.
### When to Use
Use the FAB Pattern when:
- A page has primary actions that benefit from prominent placement
- Multiple related actions can be grouped (popup menu)
- Actions were previously in footer/header but need better mobile UX
### Design Tokens
```javascript
// From PRD cssRules.mobileLayoutTokens
{
fabSize: '56px', // FAB diameter
fabBottomOffset: '72px', // 56px (nav) + 16px spacing
touchTargetMin: '48px' // Minimum touch target
}
```
### Implementation
```vue
<template>
<!-- FAB Button -->
<Transition name="fab-slide">
<button
v-if="isMobile && !selectionMode && fabVisible"
class="mobile-fab"
:class="{ 'fab-active': fabMenuOpen }"
@click="toggleFabMenu"
aria-label="Meniu acțiuni"
aria-haspopup="true"
:aria-expanded="fabMenuOpen"
>
<i class="pi pi-plus" :class="{ 'fab-icon-rotate': fabMenuOpen }"></i>
</button>
</Transition>
<!-- FAB Popup Menu -->
<Menu
ref="fabMenuRef"
:model="fabMenuItems"
:popup="true"
class="fab-popup-menu"
/>
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import Menu from 'primevue/menu'
const router = useRouter()
const fabMenuRef = ref(null)
const fabMenuOpen = ref(false)
const fabVisible = ref(true)
// FAB Menu Items
const fabMenuItems = [
{
label: 'Bon Nou',
icon: 'pi pi-plus',
command: () => router.push('/data-entry/receipts/new')
},
{
label: 'Upload Bulk',
icon: 'pi pi-cloud-upload',
command: () => openBulkUpload()
}
]
// Toggle FAB Menu
const toggleFabMenu = (event) => {
fabMenuOpen.value = !fabMenuOpen.value
fabMenuRef.value?.toggle(event)
}
// Hide FAB during scroll, show when scroll stops
let lastScrollY = 0
const handleScroll = () => {
const currentScrollY = window.scrollY
const scrollDelta = currentScrollY - lastScrollY
if (scrollDelta > 10) {
fabVisible.value = false // Scrolling down - hide
} else if (scrollDelta < -10) {
fabVisible.value = true // Scrolling up - show
}
lastScrollY = currentScrollY
// Show after scroll stops (300ms)
clearTimeout(scrollTimeout)
scrollTimeout = setTimeout(() => {
fabVisible.value = true
}, 300)
}
</script>
```
### CSS Classes
```css
/* FAB Button */
.mobile-fab {
position: fixed;
bottom: 72px; /* 56px nav + 16px spacing */
right: var(--space-md);
width: 56px;
height: 56px;
border-radius: var(--radius-full);
background: var(--color-primary);
color: white;
border: none;
display: flex;
align-items: center;
justify-content: center;
box-shadow: var(--shadow-lg);
cursor: pointer;
z-index: 999;
transition: all var(--transition-fast);
}
.mobile-fab:active {
transform: scale(0.95);
box-shadow: var(--shadow-md);
}
.mobile-fab i {
font-size: var(--text-2xl);
transition: transform var(--transition-fast);
}
/* FAB active state when menu open */
.mobile-fab.fab-active {
background: var(--color-primary-dark);
}
.mobile-fab .fab-icon-rotate {
transform: rotate(45deg);
}
/* FAB Popup Menu positioning */
.fab-popup-menu {
position: fixed !important;
bottom: 140px !important; /* 72px + 56px + 12px */
right: var(--space-md) !important;
left: auto !important;
top: auto !important;
min-width: 180px;
z-index: 1000 !important;
}
/* FAB slide animation */
.fab-slide-enter-active,
.fab-slide-leave-active {
transition: transform var(--transition-normal), opacity var(--transition-normal);
}
.fab-slide-enter-from,
.fab-slide-leave-to {
transform: translateY(100px);
opacity: 0;
}
```
### Behavior
| Interaction | Result |
|-------------|--------|
| **Tap FAB** | Opens popup menu with actions |
| **Tap menu item** | Executes action, closes menu |
| **Tap outside** | Closes menu |
| **Scroll down** | FAB hides (slide down) |
| **Scroll up** | FAB shows (slide up) |
| **Scroll stops** | FAB reappears after 300ms |
| **Selection mode** | FAB hidden (replaced by selection footer) |
### Used In
| Page | Route | Actions |
|------|-------|---------|
| **Bonuri** | `/data-entry` | Bon Nou, Upload Bulk |
> **Phase 3 Note:** Upload was moved from Footer Nav to FAB to reduce footer clutter and provide better contextual placement.
---
## MobileActionBar
Context-aware bottom action bar for forms and edit views. Positions above MobileBottomNav and displays action buttons based on current state.
@@ -1355,6 +1877,17 @@ Check that `data-theme` attribute is set correctly.
## Changelog
### Version 3.0.0 (2026-01-12) - Phase 3: Mobile Navigation Overhaul
- **NEW**: Added `Mobile Tab Pattern` section for Clienți/Furnizori switching (US-304, US-305)
- **NEW**: Added `FAB Pattern` section for contextual actions (US-303)
- **UPDATED**: `MobileBottomNav` - 4 direct links: Dashboard, Bonuri, Facturi, Setări (US-307)
- **UPDATED**: `MobileDrawerMenu` - Grouped categories: PRINCIPALE, RAPOARTE, ANALIZE, ADMINISTRARE (US-308)
- **UPDATED**: Dashboard mobile layout - KPIs only, quick-links removed (US-309)
- **UPDATED**: ASCII diagrams with new navigation structure and FAB/Tab patterns
- **UPDATED**: Navigation Architecture with Phase 3 route map
- **REMOVED**: Upload button from footer nav (moved to FAB on Bonuri page)
### Version 2.0.0 (2026-01-12)
- **NEW**: Added `MobileDrawerMenu` component documentation
@@ -1371,5 +1904,5 @@ Check that `data-theme` attribute is set correctly.
---
**Last Updated:** 2026-01-12
**Version:** 2.0.0
**Version:** 3.0.0
**Maintained By:** Frontend Team