Files
roa2web-service-auto/docs/MOBILE_PATTERNS.md
Claude Agent ac2966c629 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>
2026-01-12 16:59:01 +00:00

59 KiB
Raw Blame History

ROA2WEB Mobile Patterns Library

Version: 3.0.0 Last Updated: 2026-01-12 Status: Complete (Phase 3 - Mobile Navigation Overhaul)


Table of Contents

  1. Quick Start
  2. Mobile Layout Overview
  3. Navigation Architecture
  4. MobileTopBar
  5. MobileBottomNav (UPDATED - Phase 3)
  6. MobileDrawerMenu (UPDATED - Phase 3)
  7. Mobile Tab Pattern (NEW - Phase 3)
  8. FAB Pattern (NEW - Phase 3)
  9. MobileActionBar
  10. MobileSelectionFooter
  11. BottomSheet
  12. SwipeableCards
  13. Design Tokens for Mobile
  14. Best Practices
  15. Troubleshooting

Quick Start

For New Developers

Get a mobile view running in 5 minutes:

<template>
  <div class="mobile-view">
    <!-- 1. Top Bar -->
    <MobileTopBar
      title="My View"
      :showMenu="true"
      :actions="topBarActions"
      @menu-click="showDrawer = true"
      @action-click="handleAction"
    />

    <!-- 2. Drawer Menu (hamburger navigation) -->
    <MobileDrawerMenu
      v-model="showDrawer"
      :user="currentUser"
      @logout="handleLogout"
    />

    <!-- 3. Main Content (with padding for fixed bars) -->
    <main class="mobile-content">
      <!-- Your content here -->
    </main>

    <!-- 4. Bottom Navigation -->
    <MobileBottomNav />
  </div>
</template>

<script setup>
import { ref } from 'vue'
import MobileTopBar from '@shared/components/mobile/MobileTopBar.vue'
import MobileBottomNav from '@shared/components/mobile/MobileBottomNav.vue'
import MobileDrawerMenu from '@shared/components/mobile/MobileDrawerMenu.vue'

const showDrawer = ref(false)
const currentUser = { username: 'John Doe' }

const topBarActions = [
  { icon: 'pi pi-filter', label: 'Filter', tooltip: 'Filtrează' }
]

const handleLogout = () => {
  // Handle logout
}
</script>

<style scoped>
.mobile-content {
  padding-top: 56px;    /* MobileTopBar height */
  padding-bottom: 56px; /* MobileBottomNav height */
  min-height: 100vh;
}
</style>

Import Paths

All mobile components are located in src/shared/components/mobile/:

import MobileTopBar from '@shared/components/mobile/MobileTopBar.vue'
import MobileBottomNav from '@shared/components/mobile/MobileBottomNav.vue'
import MobileDrawerMenu from '@shared/components/mobile/MobileDrawerMenu.vue'
import MobileActionBar from '@shared/components/mobile/MobileActionBar.vue'
import MobileSelectionFooter from '@shared/components/mobile/MobileSelectionFooter.vue'
import BottomSheet from '@shared/components/mobile/BottomSheet.vue'
import SwipeableCards from '@shared/components/mobile/SwipeableCards.vue'

Mobile Layout Overview

ASCII Diagram: Standard Mobile Layout (Phase 3)

┌─────────────────────────────────────────┐
│         MobileTopBar (56px)             │
│  [≡]        Page Title      [📥] [🔍]  │
├─────────────────────────────────────────┤
│                                         │
│                                         │
│              MAIN CONTENT               │
│                                         │
│         (scrollable area)               │
│                                         │
│     padding-top: 56px                   │
│     padding-bottom: 56px                │
│                                    [+]  │  ← FAB (contextual)
│                                         │
├─────────────────────────────────────────┤
│        MobileBottomNav (56px)           │
│   🏠        📋       📄       ⚙️        │
│ Dashboard  Bonuri  Facturi  Setări      │
└─────────────────────────────────────────┘

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)

┌──────────────────┬──────────────────────┐
│  MobileDrawer    │ ░░░░░░░░░░░░░░░░░░░░ │
│  Menu (280px)    │ ░░░░░░░░░░░░░░░░░░░░ │
├──────────────────┤ ░░░ OVERLAY ░░░░░░░░ │
│  🏢 ROA2WEB      │ ░░ (tap to close) ░░ │
├──────────────────┤ ░░░░░░░░░░░░░░░░░░░░ │
│  PRINCIPALE      │ ░░░░░░░░░░░░░░░░░░░░ │
│  🏠 Dashboard    │ ░░░░░░░░░░░░░░░░░░░░ │
│  📋 Bonuri       │ ░░░░░░░░░░░░░░░░░░░░ │
│  ──────────────  │ ░░░░░░░░░░░░░░░░░░░░ │
│  RAPOARTE        │ ░░░░░░░░░░░░░░░░░░░░ │
│  📄 Facturi      │ ░░░░░░░░░░░░░░░░░░░░ │
│  🔢 Balanță      │ ░░░░░░░░░░░░░░░░░░░░ │
│  💰 Casa și Banca│ ░░░░░░░░░░░░░░░░░░░░ │
│  ──────────────  │ ░░░░░░░░░░░░░░░░░░░░ │
│  ANALIZE         │ ░░░░░░░░░░░░░░░░░░░░ │
│  ⏰ Scadențe     │ ░░░░░░░░░░░░░░░░░░░░ │
│  📋 Facturi Det. │ ░░░░░░░░░░░░░░░░░░░░ │
│  ──────────────  │ ░░░░░░░░░░░░░░░░░░░░ │
│  ADMINISTRARE    │ ░░░░░░░░░░░░░░░░░░░░ │
│  ⚙️ Setări       │ ░░░░░░░░░░░░░░░░░░░░ │
├──────────────────┤ ░░░░░░░░░░░░░░░░░░░░ │
│  👤 Username     │ ░░░░░░░░░░░░░░░░░░░░ │
│  🚪 Deconectare  │ ░░░░░░░░░░░░░░░░░░░░ │
└──────────────────┴──────────────────────┘

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

┌─────────────────────────────────────────┐
│      MobileTopBar (selection-active)    │
│  [←]      "3 selectate"      [☑] [✕]   │
├─────────────────────────────────────────┤
│                                         │
│     ☑ Item 1                            │
│     ☐ Item 2                            │
│     ☑ Item 3                            │
│     ☑ Item 4                            │
│     ☐ Item 5                            │
│                                         │
│                                         │
├─────────────────────────────────────────┤
│      MobileSelectionFooter              │
│  ┌──────────┐    ┌──────────┐          │
│  │  🗑 Șterge │    │ 📤 Export │          │
│  └──────────┘    └──────────┘          │
└─────────────────────────────────────────┘

ASCII Diagram: Action Bar Layout (Edit/Create Views)

┌─────────────────────────────────────────┐
│         MobileTopBar (56px)             │
│  [←]        Editare Bon         [⋮]     │
├─────────────────────────────────────────┤
│                                         │
│     Form Fields...                      │
│                                         │
│     ┌─────────────────────────────┐     │
│     │  Furnizor: LIDL             │     │
│     │  Total: 125.50 RON          │     │
│     │  Data: 2026-01-12           │     │
│     └─────────────────────────────┘     │
│                                         │
├─────────────────────────────────────────┤
│      MobileActionBar (context-aware)    │
│  ┌──────────┐    ┌──────────────────┐  │
│  │ Salvează │    │ Trimite aprobare │  │
│  └──────────┘    └──────────────────┘  │
├─────────────────────────────────────────┤
│        MobileBottomNav (56px)           │
└─────────────────────────────────────────┘

ASCII Diagram: BottomSheet Open

┌─────────────────────────────────────────┐
│         MobileTopBar (56px)             │
├─────────────────────────────────────────┤
│ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
│ ░░░░░░░░░░░░ OVERLAY ░░░░░░░░░░░░░░░░░░ │
│ ░░░░░░░░░░ (tap to close) ░░░░░░░░░░░░░ │
│ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │
├─────────────────────────────────────────┤  ←─ BottomSheet
│            ───────────                  │     slides up
│            (drag handle)                │
│                                         │
│     Filter Options:                     │
│     ☐ Option A                          │
│     ☑ Option B                          │
│     ☐ Option C                          │
│                                         │
│     [  Apply Filters  ]                 │
│                                         │
└─────────────────────────────────────────┘

ASCII Diagram: SwipeableCards

┌─────────────────────────────────────────┐
│                                         │
│ ┌─────────────────────────────────────┐ │
│ │                                     │ │
│ │         KPI Card Content            │ │  ← Swipe left/right
│ │                                     │ │    to navigate
│ │         $125,430                    │ │
│ │         Total Sales                 │ │
│ │                                     │ │
│ └─────────────────────────────────────┘ │
│                                         │
│              ●━━━━●───●                 │  ← Dots indicator
│                                         │    (active = expanded)
└─────────────────────────────────────────┘

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 (Phase 3 - Updated)

                    ┌─────────────────────────────────────┐
                    │           / (root)                  │
                    │     Redirects to /dashboard         │
                    └───────────────┬─────────────────────┘
                                    │
        ┌───────────────────────────┼───────────────────────────┐
        │                           │                           │
        ▼                           ▼                           ▼
┌───────────────────┐   ┌───────────────────────┐   ┌─────────────────┐
│    /data-entry    │   │       /reports        │   │    /settings    │
│                   │   │                       │   │                 │
│  📋 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:

MobileBottomNav "Setări" button
          │
          ▼
    ┌───────────────────────────────────┐
    │         Settings Hub              │
    │         /settings                 │
    │                                   │
    │  ┌─────────┐   ┌─────────┐       │
    │  │   OCR   │   │  Cache  │       │
    │  │ Setări  │   │  Stats  │       │
    │  └────┬────┘   └────┬────┘       │
    │       │             │            │
    │  ┌────┴────┐   ┌────┴────┐       │
    │  │ Server  │   │Telegram │       │
    │  │  Logs   │   │  Bot    │       │
    │  └─────────┘   └─────────┘       │
    └───────────────────────────────────┘
          │
          ▼ (click on card)
    ┌───────────────────────────────────┐
    │  /data-entry/ocr-metrics          │
    │  /reports/cache-stats             │
    │  /reports/server-logs             │
    │  /reports/telegram                │
    └───────────────────────────────────┘
// 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' },
  { to: '/reports/invoices', icon: 'pi pi-file-edit', label: 'Facturi' },
  { to: '/settings', icon: 'pi pi-cog', label: 'Setări' }
]

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)
// 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         │   │
│  │   ...                          │   │
│  │                                 │   │
│  └─────────────────────────────────┘   │
│                                         │
│           ●━━━━●───●                    │
│                                         │
│  (No quick-links on mobile - use       │
│   Footer Nav or Hamburger Menu)        │
│                                         │
├─────────────────────────────────────────┤
│        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

Material Design 3 inspired top navigation bar for mobile views.

Props

Prop Type Default Description
title String '' Center title text
showBack Boolean false Show back arrow button on left
showMenu Boolean false Show hamburger menu on left (ignored if showBack is true)
actions Array [] Right-side action buttons
selectionActive Boolean false Enable selection mode styling

Events

Event Payload Description
menu-click - Hamburger menu clicked
back-click - Back arrow clicked
action-click action Action button clicked

Action Object Structure

interface TopBarAction {
  icon: string      // PrimeIcons class (e.g., 'pi pi-filter')
  label?: string    // Accessibility label
  tooltip?: string  // Tooltip text
  active?: boolean  // Highlight when active
}

Basic Usage

<template>
  <MobileTopBar
    title="Bonuri Fiscale"
    :showMenu="true"
    :actions="topBarActions"
    @menu-click="showSidebar = true"
    @action-click="handleAction"
  />
</template>

<script setup>
const topBarActions = [
  { icon: 'pi pi-filter', label: 'Filtrează', tooltip: 'Deschide filtre' },
  { icon: 'pi pi-search', label: 'Caută', tooltip: 'Căutare' }
]

const handleAction = (action) => {
  if (action.icon === 'pi pi-filter') {
    openFilterSheet()
  }
}
</script>

With Back Navigation

<MobileTopBar
  title="Detalii Bon"
  :showBack="true"
  @back-click="router.back()"
/>

Selection Mode

<MobileTopBar
  :title="selectedCount > 0 ? `${selectedCount} selectate` : 'Bonuri'"
  :showBack="selectedCount > 0"
  :selectionActive="selectedCount > 0"
  :actions="selectionActions"
  @back-click="clearSelection"
/>

CSS Classes

Class Description
.mobile-top-bar Base container (fixed, 56px height)
.selection-active Blue background for selection mode
.top-bar-left Left button container
.top-bar-right Right action buttons container
.top-bar-title Center title (ellipsis on overflow)
.top-bar-btn Individual button (48x48px touch target)
.top-bar-btn.active Highlighted state

MobileBottomNav

Material Design 3 inspired bottom navigation bar with router integration.

Props

Prop Type Default Description
items Array Default nav items Navigation items array

Events

Event Payload Description
item-click item Non-router item clicked

Item Object Structure

interface NavItem {
  to?: string       // Route path (uses router-link when provided)
  icon: string      // PrimeIcons class (e.g., 'pi pi-receipt')
  label: string     // Display text
  active?: boolean  // Force active state (for non-router items)
}

Default Items

[
  { 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
]

Basic Usage

<template>
  <MobileBottomNav />
</template>

Custom Items

<template>
  <MobileBottomNav
    :items="customItems"
    @item-click="handleNavClick"
  />
</template>

<script setup>
const customItems = [
  { to: '/home', icon: 'pi pi-home', label: 'Acasă' },
  { to: '/reports', icon: 'pi pi-chart-bar', label: 'Rapoarte' },
  { icon: 'pi pi-plus', label: 'Adaugă' },  // Button, not route
  { to: '/settings', icon: 'pi pi-cog', label: 'Setări' }
]

const handleNavClick = (item) => {
  if (item.label === 'Adaugă') {
    showAddDialog()
  }
}
</script>

CSS Classes

Class Description
.mobile-bottom-nav Base container (fixed, 56px height)
.bottom-nav-item Individual nav item (flex: 1)
.bottom-nav-item.active Active state (primary color)
.bottom-nav-item.router-link-active Auto-active via Vue Router

MobileDrawerMenu

(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

Prop Type Default Description
modelValue Boolean false Controls visibility (v-model support)
user Object null User object for profile display { username: string }
onLogout Function null Optional logout callback; emits logout event if not provided

Events

Event Payload Description
update:modelValue boolean v-model update when visibility changes
logout - Emitted when logout is clicked (if no onLogout prop)

Navigation Items (Phase 3 - Grouped Categories)

The drawer navigation is organized into 4 sections with visual separators:

// 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

<template>
  <MobileTopBar
    title="Dashboard"
    :showMenu="true"
    @menu-click="showDrawer = true"
  />

  <MobileDrawerMenu
    v-model="showDrawer"
    :user="authStore.user"
    @logout="handleLogout"
  />
</template>

<script setup>
import { ref } from 'vue'
import MobileDrawerMenu from '@shared/components/mobile/MobileDrawerMenu.vue'
import { useAuthStore } from '@/stores/auth'

const showDrawer = ref(false)
const authStore = useAuthStore()

const handleLogout = async () => {
  await authStore.logout()
  router.push('/login')
}
</script>

With Logout Callback

<MobileDrawerMenu
  v-model="showDrawer"
  :user="{ username: 'John Doe' }"
  :onLogout="async () => {
    await api.logout()
    router.push('/login')
  }"
/>

Features

Feature Description
Slide Animation Slides in from left with overlay fade
Close on Tap Outside Tapping the overlay closes the drawer
Close on Navigation Drawer closes automatically when a link is clicked
Active State Current route is highlighted
Profile Section Displays username and logout button at bottom
Teleported Rendered to <body> to avoid z-index issues
Dark Mode Full support for light/dark themes

CSS Classes

Class Description
.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-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

.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">:

  • Overlay fades in/out
  • Drawer slides from left (translateX(-100%)translateX(0))
  • Duration: var(--transition-normal) (250ms)

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

<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

/* 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:

.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

// From PRD cssRules.mobileLayoutTokens
{
  fabSize: '56px',           // FAB diameter
  fabBottomOffset: '72px',   // 56px (nav) + 16px spacing
  touchTargetMin: '48px'     // Minimum touch target
}

Implementation

<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

/* 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.

Props

Prop Type Default Description
visible Boolean false Controls visibility (triggers slide-up animation)
actions Array [] Array of action buttons to display

Action Object Structure

interface ActionBarAction {
  label: string          // Button text (e.g., 'Salvează')
  icon: string           // PrimeIcons class (e.g., 'pi pi-save')
  severity?: string      // PrimeVue severity ('primary', 'secondary', 'danger', etc.)
  handler: () => void    // Click handler function
  disabled?: boolean     // Disable the button
}

Layout Behavior

Actions Count Layout
1 button Full-width
2 buttons Side-by-side, equal width
3+ buttons Equal distribution

Basic Usage

<template>
  <MobileActionBar
    :visible="isMobile"
    :actions="formActions"
  />
</template>

<script setup>
import MobileActionBar from '@shared/components/mobile/MobileActionBar.vue'

const formActions = [
  {
    label: 'Salvează',
    icon: 'pi pi-save',
    severity: 'primary',
    handler: handleSave
  },
  {
    label: 'Anulează',
    icon: 'pi pi-times',
    severity: 'secondary',
    handler: handleCancel
  }
]
</script>

Context-Aware Actions (Receipt Edit Example)

<template>
  <MobileActionBar
    :visible="isMobile"
    :actions="contextAwareActions"
  />
</template>

<script setup>
import { computed } from 'vue'

const receiptStatus = ref('draft') // 'draft' | 'pending' | 'approved' | 'rejected'

const contextAwareActions = computed(() => {
  switch (receiptStatus.value) {
    case 'draft':
      return [
        { label: 'Salvează', icon: 'pi pi-save', severity: 'secondary', handler: handleSave },
        { label: 'Trimite', icon: 'pi pi-send', severity: 'primary', handler: handleSubmit }
      ]
    case 'pending':
      return [
        { label: 'Salvează', icon: 'pi pi-save', severity: 'secondary', handler: handleSave },
        { label: 'Aprobă', icon: 'pi pi-check', severity: 'success', handler: handleApprove },
        { label: 'Respinge', icon: 'pi pi-times', severity: 'danger', handler: handleReject }
      ]
    case 'approved':
      return [] // No actions for approved receipts
    case 'rejected':
      return [
        { label: 'Salvează', icon: 'pi pi-save', severity: 'secondary', handler: handleSave },
        { label: 'Re-trimite', icon: 'pi pi-refresh', severity: 'primary', handler: handleResubmit }
      ]
    default:
      return []
  }
})
</script>

Hiding During Overlays

Hide the action bar when BottomSheet or other overlays are open:

<template>
  <MobileActionBar
    :visible="isMobile && !isBottomSheetOpen"
    :actions="formActions"
  />

  <BottomSheet v-model="isBottomSheetOpen">
    <!-- Filter content -->
  </BottomSheet>
</template>

CSS Classes

Class Description
.mobile-action-bar Base container (fixed, bottom: 56px)
.action-bar-content Button container (max-width: 500px)
.layout-single Single button layout (full-width)
.layout-dual Two button layout (50% each)
.layout-multi Three+ button layout (equal distribution)
.action-bar-btn Individual button (48px min-height)

Animation

Uses Vue <Transition name="slide-up">:

  • Slides up from below screen (translateY(100%)translateY(0))
  • Duration: var(--transition-normal) (250ms)
  • Fades in/out simultaneously

Positioning

.mobile-action-bar {
  position: fixed;
  bottom: 56px; /* Above MobileBottomNav */
  left: 0;
  right: 0;
  /* Safe area support for iPhone X+ */
  bottom: calc(56px + env(safe-area-inset-bottom));
}

Content Padding Adjustment

When MobileActionBar is visible, increase bottom padding:

.mobile-content {
  padding-bottom: 120px; /* 56px (nav) + 64px (action bar) */
}

MobileSelectionFooter

Slide-up action bar for batch operations on selected items.

Props

Prop Type Default Description
visible Boolean false Controls visibility (triggers animation)
actions Array [] Action buttons to display

Action Object Structure

interface SelectionAction {
  label: string           // Button text
  icon: string            // PrimeIcons class
  severity?: string       // PrimeVue severity ('secondary', 'danger', etc.)
  handler: () => void     // Click handler function
}

Basic Usage

<template>
  <MobileSelectionFooter
    :visible="selectedItems.length > 0"
    :actions="selectionActions"
  />
</template>

<script setup>
const selectedItems = ref([])

const selectionActions = computed(() => [
  {
    label: 'Șterge',
    icon: 'pi pi-trash',
    severity: 'danger',
    handler: deleteSelected
  },
  {
    label: 'Export',
    icon: 'pi pi-download',
    severity: 'secondary',
    handler: exportSelected
  }
])

const deleteSelected = () => {
  // Delete logic
  selectedItems.value = []
}

const exportSelected = () => {
  // Export logic
}
</script>

Single Action

<MobileSelectionFooter
  :visible="hasSelection"
  :actions="[
    { label: 'Confirmă Selectate', icon: 'pi pi-check', severity: 'success', handler: confirmAll }
  ]"
/>

CSS Classes

Class Description
.mobile-selection-footer Base container (fixed, z-index 1030)
.selection-actions Button container (max-width: 400px)
.selection-action-btn Individual button (48px min-height)

Animation

Uses Vue <Transition name="slide-up">:

  • Slides up from bottom when visible becomes true
  • Slides down when visible becomes false
  • Duration: var(--transition-normal) (250ms)

BottomSheet

Modal-style sheet that slides up from the bottom, ideal for filters and forms.

Props

Prop Type Default Description
modelValue Boolean false v-model for visibility
closeOnOverlay Boolean true Close when tapping overlay
closeOnSwipeDown Boolean true Close when swiping down on handle

Events

Event Payload Description
update:modelValue boolean v-model update

Slots

Slot Description
default Content inside the sheet

Basic Usage

<template>
  <Button label="Open Filters" @click="showFilters = true" />

  <BottomSheet v-model="showFilters">
    <h3>Filtre</h3>

    <div class="form-group">
      <label class="form-label">Status</label>
      <Dropdown v-model="filters.status" :options="statusOptions" />
    </div>

    <div class="form-group">
      <label class="form-label">Data</label>
      <Calendar v-model="filters.date" />
    </div>

    <div class="flex gap-sm mt-md">
      <Button label="Resetează" severity="secondary" @click="resetFilters" />
      <Button label="Aplică" @click="applyFilters" />
    </div>
  </BottomSheet>
</template>

<script setup>
const showFilters = ref(false)

const applyFilters = () => {
  // Apply filter logic
  showFilters.value = false
}
</script>

Prevent Close on Overlay

<BottomSheet
  v-model="showImportantForm"
  :closeOnOverlay="false"
>
  <!-- User must explicitly close this -->
</BottomSheet>

CSS Classes

Class Description
.bottom-sheet-overlay Full-screen overlay (50% opacity black)
.bottom-sheet Sheet container (max-height: 90vh)
.bottom-sheet-handle Handle area at top (32px min-height)
.handle-bar Visual drag indicator (40px × 4px)
.bottom-sheet-content Scrollable content area

Touch Gestures

Gesture Action
Tap overlay Close (if closeOnOverlay: true)
Tap handle Close
Swipe down > 100px Close (if closeOnSwipeDown: true)

SwipeableCards

Touch-swipeable carousel for KPI cards with dots indicator.

Props

Prop Type Default Description
totalCards Number required Number of cards in carousel
showDots Boolean true Show navigation dots
autoPlay Boolean false Auto-advance cards
autoPlayInterval Number 5000 Auto-play interval (ms)

Events

Event Payload Description
update:currentIndex number Current card index changed

Slots

Named slots for each card: card-0, card-1, card-2, etc.

Exposed Methods

Method Description
goToCard(index) Navigate to specific card
nextCard() Go to next card
prevCard() Go to previous card
currentIndex Current card index (ref)

Basic Usage

<template>
  <SwipeableCards :totalCards="3">
    <template #card-0>
      <div class="kpi-card">
        <div class="kpi-value">$125,430</div>
        <div class="kpi-label">Total Sales</div>
      </div>
    </template>

    <template #card-1>
      <div class="kpi-card">
        <div class="kpi-value">1,234</div>
        <div class="kpi-label">Orders</div>
      </div>
    </template>

    <template #card-2>
      <div class="kpi-card">
        <div class="kpi-value">98.5%</div>
        <div class="kpi-label">Success Rate</div>
      </div>
    </template>
  </SwipeableCards>
</template>

With Auto-Play

<SwipeableCards
  :totalCards="4"
  :autoPlay="true"
  :autoPlayInterval="3000"
>
  <!-- Cards -->
</SwipeableCards>

Programmatic Navigation

<template>
  <SwipeableCards ref="carousel" :totalCards="3">
    <!-- Cards -->
  </SwipeableCards>

  <Button label="Next" @click="carousel.nextCard()" />
</template>

<script setup>
const carousel = ref(null)
</script>

CSS Classes

Class Description
.swipeable-cards-container Base container with overflow hidden
.cards-track Horizontal flex container (will-change: transform)
.card-slide Individual card (flex: 0 0 100%)
.dots-indicator Navigation dots container
.dot Individual dot (8px, expands to 24px when active)
.dot.active Active dot (primary color)

Touch Thresholds

Threshold Value Description
Swipe distance 50px Minimum swipe to change card
Velocity 0.3 Quick swipe detection
Angle 30° Max angle for horizontal detection

Design Tokens for Mobile

Layout Tokens

Token Value Use
--header-height 56px MobileTopBar & MobileBottomNav height
--z-fixed 1030 Fixed elements z-index
--z-modal-backdrop 1040 BottomSheet overlay
--z-modal 1050 BottomSheet content

Touch Target Sizes

/* Minimum touch target: 48x48px */
.touch-target {
  min-width: 48px;
  min-height: 48px;
}

/* Button in top bar */
.top-bar-btn {
  width: 48px;
  height: 48px;
}

Spacing for Mobile

Token Value Mobile Use
var(--space-xs) 4px Icon gaps, tight spacing
var(--space-sm) 8px Between nav items, card gaps
var(--space-md) 16px Card padding, content margins
var(--space-lg) 24px Section spacing

Mobile Content Padding

/* Standard mobile content area */
.mobile-content {
  padding-top: 56px;    /* MobileTopBar */
  padding-bottom: 56px; /* MobileBottomNav */
  padding-left: var(--space-md);
  padding-right: var(--space-md);
}

/* When selection footer is visible */
.mobile-content-with-selection {
  padding-bottom: 80px; /* Higher for selection footer */
}

Best Practices

1. Always Use Design Tokens

/* ✅ CORRECT */
.mobile-card {
  padding: var(--space-md);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
}

/* ❌ WRONG */
.mobile-card {
  padding: 16px;
  border-radius: 8px;
  box-shadow: 0 1px 2px rgba(0,0,0,0.05);
}

2. Account for Fixed Headers/Footers

<template>
  <div class="view-container">
    <MobileTopBar title="My View" />

    <main class="view-content">
      <!-- Content that scrolls -->
    </main>

    <MobileBottomNav />
  </div>
</template>

<style scoped>
.view-content {
  padding-top: 56px;    /* TopBar height */
  padding-bottom: 56px; /* BottomNav height */
  min-height: 100vh;
  overflow-y: auto;
}
</style>

3. Test Both Light and Dark Mode

All mobile components support:

  • Manual dark mode: [data-theme="dark"]
  • System preference: @media (prefers-color-scheme: dark)
// Toggle theme in DevTools:
document.documentElement.setAttribute('data-theme', 'dark')

4. Handle Safe Areas (Notch/Home Indicator)

.mobile-bottom-nav {
  padding-bottom: env(safe-area-inset-bottom);
}

.mobile-content {
  padding-bottom: calc(56px + env(safe-area-inset-bottom));
}

5. Selection Mode Pattern

<script setup>
const selectedItems = ref([])
const isSelectionMode = computed(() => selectedItems.value.length > 0)

const toggleSelection = (item) => {
  const index = selectedItems.value.findIndex(i => i.id === item.id)
  if (index === -1) {
    selectedItems.value.push(item)
  } else {
    selectedItems.value.splice(index, 1)
  }
}

const clearSelection = () => {
  selectedItems.value = []
}
</script>

6. Combine Components for Complex Views

<template>
  <!-- Top Bar adapts to selection mode -->
  <MobileTopBar
    :title="isSelectionMode ? `${selectedCount} selectate` : 'Bonuri'"
    :showBack="isSelectionMode"
    :selectionActive="isSelectionMode"
    :actions="currentActions"
    @back-click="clearSelection"
    @action-click="handleAction"
  />

  <!-- Main Content -->
  <main class="content">
    <SwipeableCards :totalCards="kpis.length">
      <template v-for="(kpi, i) in kpis" #[`card-${i}`]>
        <KPICard :data="kpi" />
      </template>
    </SwipeableCards>

    <ItemList :items="items" @select="toggleSelection" />
  </main>

  <!-- Bottom Nav (hidden in selection mode) -->
  <MobileBottomNav v-if="!isSelectionMode" />

  <!-- Selection Footer (shown in selection mode) -->
  <MobileSelectionFooter
    :visible="isSelectionMode"
    :actions="selectionActions"
  />

  <!-- Filter Sheet -->
  <BottomSheet v-model="showFilters">
    <FilterForm @apply="applyFilters" />
  </BottomSheet>
</template>

Troubleshooting

Top/Bottom bars not fixed

Problem: Bars scroll with content.

Solution: Ensure .mobile-top-bar and .mobile-bottom-nav have position: fixed. Check for overflow: hidden on ancestors.

Content hidden behind bars

Problem: Content is cut off at top/bottom.

Solution: Add padding to content area:

.content {
  padding-top: 56px;
  padding-bottom: 56px;
}

BottomSheet z-index issues

Problem: BottomSheet appears behind other elements.

Solution: BottomSheet uses <Teleport to="body"> to avoid stacking context issues. Check for z-index wars in your CSS.

Swipe conflicts with scrolling

Problem: SwipeableCards interferes with vertical scroll.

Solution: Component uses angle detection (30° threshold). Ensure cards have sufficient content area. Check touch-action: pan-y is set.

Dark mode not working

Problem: Components don't respond to theme changes.

Solution: All components support both mechanisms:

  1. Manual: [data-theme="dark"] on <html>
  2. System: @media (prefers-color-scheme: dark)

Check that data-theme attribute is set correctly.

Problem: When selection footer appears, it covers list items.

Solution: Use dynamic padding:

.content {
  padding-bottom: v-bind(isSelectionMode ? '96px' : '56px');
}


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
  • NEW: Added MobileActionBar component documentation
  • NEW: Added Navigation Architecture section with route maps
  • UPDATED: MobileBottomNav default items now point to Settings Hub (/settings)
  • UPDATED: ASCII diagrams with new routes and component layouts
  • UPDATED: Quick Start section to include drawer menu integration

Version 1.0.0 (2026-01-11)

  • Initial release with MobileTopBar, MobileBottomNav, MobileSelectionFooter, BottomSheet, SwipeableCards

Last Updated: 2026-01-12 Version: 3.0.0 Maintained By: Frontend Team