feat(mobile-navigation-improvements): Complete US-214 - Actualizare Documentație MOBILE_PATTERNS.md

Implemented by Ralph autonomous loop.
Iteration: 14

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-01-12 12:45:42 +00:00
parent 45439a40f9
commit b7f442ed33
3 changed files with 499 additions and 23 deletions

View File

@@ -1,6 +1,6 @@
# ROA2WEB Mobile Patterns Library
**Version:** 1.0.0
**Version:** 2.0.0
**Last Updated:** 2026-01-12
**Status:** ✅ Complete
@@ -10,14 +10,17 @@
1. [Quick Start](#quick-start)
2. [Mobile Layout Overview](#mobile-layout-overview)
3. [MobileTopBar](#mobiletopbar)
4. [MobileBottomNav](#mobilebottomnav)
5. [MobileSelectionFooter](#mobileselectionfooter)
6. [BottomSheet](#bottomsheet)
7. [SwipeableCards](#swipeablecards)
8. [Design Tokens for Mobile](#design-tokens-for-mobile)
9. [Best Practices](#best-practices)
10. [Troubleshooting](#troubleshooting)
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)
---
@@ -35,32 +38,43 @@ Get a mobile view running in **5 minutes**:
title="My View"
:showMenu="true"
:actions="topBarActions"
@menu-click="toggleSidebar"
@menu-click="showDrawer = true"
@action-click="handleAction"
/>
<!-- 2. Main Content (with padding for fixed bars) -->
<!-- 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>
<!-- 3. Bottom Navigation -->
<MobileBottomNav :items="navItems" />
<!-- 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 navItems = [
{ to: '/data-entry', icon: 'pi pi-receipt', label: 'Bonuri' },
{ to: '/reports', icon: 'pi pi-chart-bar', label: 'Rapoarte' }
]
const handleLogout = () => {
// Handle logout
}
</script>
<style scoped>
@@ -79,6 +93,8 @@ All mobile components are located in `src/shared/components/mobile/`:
```javascript
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'
@@ -107,11 +123,32 @@ import SwipeableCards from '@shared/components/mobile/SwipeableCards.vue'
│ │
├─────────────────────────────────────────┤
│ MobileBottomNav (56px) │
🏠 📋 📊 ⚙️
Home Upload Reports Settings
📋 ☁️ 📊 ⚙️ │
Bonuri Upload Rapoarte Setări
└─────────────────────────────────────────┘
```
### ASCII Diagram: Drawer Menu Open
```
┌──────────────────┬──────────────────────┐
│ MobileDrawer │ ░░░░░░░░░░░░░░░░░░░░ │
│ Menu (280px) │ ░░░░░░░░░░░░░░░░░░░░ │
├──────────────────┤ ░░░ OVERLAY ░░░░░░░░ │
│ 🏢 ROA2WEB │ ░░ (tap to close) ░░ │
├──────────────────┤ ░░░░░░░░░░░░░░░░░░░░ │
│ 🏠 Dashboard │ ░░░░░░░░░░░░░░░░░░░░ │
│ 📋 Bonuri │ ░░░░░░░░░░░░░░░░░░░░ │
│ 📄 Facturi │ ░░░░░░░░░░░░░░░░░░░░ │
│ 🔢 Balanță │ ░░░░░░░░░░░░░░░░░░░░ │
│ 💰 Trezorerie │ ░░░░░░░░░░░░░░░░░░░░ │
│ ⚙️ Setări │ ░░░░░░░░░░░░░░░░░░░░ │
├──────────────────┤ ░░░░░░░░░░░░░░░░░░░░ │
│ 👤 Username │ ░░░░░░░░░░░░░░░░░░░░ │
│ 🚪 Deconectare │ ░░░░░░░░░░░░░░░░░░░░ │
└──────────────────┴──────────────────────┘
```
### ASCII Diagram: Selection Mode Layout
```
@@ -135,6 +172,32 @@ import SwipeableCards from '@shared/components/mobile/SwipeableCards.vue'
└─────────────────────────────────────────┘
```
### 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
```
@@ -180,6 +243,120 @@ import SwipeableCards from '@shared/components/mobile/SwipeableCards.vue'
---
## Navigation Architecture
### Route Map
```
┌─────────────────────────────────────┐
│ / (root) │
│ Redirects to /reports/dashboard │
└───────────────┬─────────────────────┘
┌───────────────────────────┼───────────────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────────┐ ┌───────────────────────┐ ┌─────────────────┐
│ /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 │
└───────────────────┘ └───────────────────────┘ └─────────────────┘
```
### 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 │
└───────────────────────────────────┘
```
### MobileBottomNav Default Items (Updated)
```javascript
// Default navigation 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
]
```
### 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: '/settings', icon: 'pi pi-cog', label: 'Setări' }
]
```
### Dashboard Mobile Quick Links
On mobile, Dashboard shows KPI cards plus quick-link cards:
```
┌─────────────────────────────────────────┐
│ MobileTopBar: Dashboard │
├─────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────┐ │
│ │ SwipeableCards (KPIs) │ │
│ │ ← Swipe → │ │
│ └─────────────────────────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ 📉 Analiză │ │ 📋 Facturi │ │
│ │ Scadențe → │ │ Detaliate → │ │
│ └─────────────┘ └─────────────┘ │
│ │
│ (Quick links to dedicated pages) │
│ │
├─────────────────────────────────────────┤
│ MobileBottomNav │
└─────────────────────────────────────────┘
```
---
## MobileTopBar
Material Design 3 inspired top navigation bar for mobile views.
@@ -310,7 +487,7 @@ interface NavItem {
{ 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: '/data-entry/ocr-metrics', icon: 'pi pi-cog', label: 'Setări' }
{ to: '/settings', icon: 'pi pi-cog', label: 'Setări' } // → Settings Hub
]
```
@@ -359,6 +536,282 @@ const handleNavClick = (item) => {
---
## MobileDrawerMenu
Material Design 3 inspired navigation drawer that slides in from the left. Replaces the desktop sidebar on mobile devices.
### 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 (Built-in)
The drawer contains the following navigation links:
```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' }
]
```
### Basic Usage
```vue
<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
```vue
<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) |
| `.drawer-menu` | Main drawer container (280px, max 85vw) |
| `.drawer-header` | Header with logo |
| `.drawer-section` | Navigation links container |
| `.drawer-link` | Individual navigation link (48px min-height) |
| `.drawer-link.active` | Active route styling (blue background) |
| `.drawer-profile` | Profile section at bottom |
| `.logout-link` | Red logout button styling |
### Animation
Uses Vue `<Transition name="drawer">`:
- Overlay fades in/out
- Drawer slides from left (`translateX(-100%)``translateX(0)`)
- Duration: `var(--transition-normal)` (250ms)
---
## 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
```typescript
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
```vue
<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)
```vue
<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:
```vue
<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
```css
.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:
```css
.mobile-content {
padding-bottom: 120px; /* 56px (nav) + 64px (action bar) */
}
```
---
## MobileSelectionFooter
Slide-up action bar for batch operations on selected items.
@@ -900,6 +1353,23 @@ Check that `data-theme` attribute is set correctly.
---
## Changelog
### 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:** 1.0.0
**Version:** 2.0.0
**Maintained By:** Frontend Team

View File

@@ -246,8 +246,8 @@
"Diagrame ASCII actualizate cu noile rute",
"npm run build passes (doar pentru validare că nu s-a stricat nimic)"
],
"passes": false,
"notes": ""
"passes": true,
"notes": "Completed in iteration 14"
}
]
}

View File

@@ -82,3 +82,9 @@ User Stories: 14 (US-201 to US-214)
[2026-01-12 12:40:31] Working on story: US-211
[2026-01-12 12:40:31] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_13_US-211.log)
[2026-01-12 12:42:01] SUCCESS: Story US-211 passed!
[2026-01-12 12:42:01] Changes committed
[2026-01-12 12:42:01] Progress: 13/14 stories completed
[2026-01-12 12:42:03] === Iteration 14/100 ===
[2026-01-12 12:42:03] Working on story: US-214
[2026-01-12 12:42:03] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_14_US-214.log)
[2026-01-12 12:45:42] SUCCESS: Story US-214 passed!