feat(unified-mobile-material-design): Complete US-109 - BankCashRegisterView Mobile Material Design

Implemented by Ralph autonomous loop.
Iteration: 13

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-01-12 10:16:15 +00:00
parent 1584d7ae61
commit 936e7aa755
3 changed files with 429 additions and 73 deletions

View File

@@ -215,8 +215,8 @@
"Filtre în BottomSheet pe mobil", "Filtre în BottomSheet pe mobil",
"npm run build passes" "npm run build passes"
], ],
"passes": false, "passes": true,
"notes": "" "notes": "Completed in iteration 13"
}, },
{ {
"id": "US-110", "id": "US-110",

View File

@@ -78,3 +78,9 @@ Mon Jan 12 09:44:54 AM UTC 2026
[2026-01-12 10:11:10] Working on story: US-108 [2026-01-12 10:11:10] Working on story: US-108
[2026-01-12 10:11:10] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_12_US-108.log) [2026-01-12 10:11:10] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_12_US-108.log)
[2026-01-12 10:13:33] SUCCESS: Story US-108 passed! [2026-01-12 10:13:33] SUCCESS: Story US-108 passed!
[2026-01-12 10:13:33] Changes committed
[2026-01-12 10:13:33] Progress: 11/20 stories completed
[2026-01-12 10:13:35] === Iteration 13/100 ===
[2026-01-12 10:13:35] Working on story: US-109
[2026-01-12 10:13:35] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_13_US-109.log)
[2026-01-12 10:16:15] SUCCESS: Story US-109 passed!

View File

@@ -1,8 +1,105 @@
<template> <template>
<div class="app-container"> <div class="app-container" :class="{ 'mobile-layout': isMobile }">
<!-- US-109: Mobile Material Design Top Bar -->
<MobileTopBar
v-if="isMobile"
title="Trezorerie"
:show-menu="true"
:actions="mobileTopBarActions"
@menu-click="toggleMobileMenu"
@action-click="handleTopBarAction"
/>
<!-- US-109: Mobile Hamburger Menu -->
<Sidebar v-model:visible="mobileMenuVisible" position="left" class="mobile-sidebar">
<template #header>
<div class="sidebar-header">
<span class="sidebar-title">ROA2WEB</span>
</div>
</template>
<div class="sidebar-menu">
<router-link to="/data-entry" class="sidebar-item">
<i class="pi pi-receipt"></i>
<span>Bonuri</span>
</router-link>
<router-link to="/reports/bank-cash-register" class="sidebar-item active">
<i class="pi pi-wallet"></i>
<span>Trezorerie</span>
</router-link>
<router-link to="/reports/dashboard" class="sidebar-item">
<i class="pi pi-chart-bar"></i>
<span>Rapoarte</span>
</router-link>
<router-link to="/data-entry/ocr-metrics" class="sidebar-item">
<i class="pi pi-cog"></i>
<span>Setări</span>
</router-link>
</div>
</Sidebar>
<!-- US-109: Filter BottomSheet for mobile -->
<BottomSheet v-model="showFilters">
<h3 class="bottom-sheet-title">Filtre</h3>
<div class="bottom-sheet-filters">
<!-- Register Type -->
<div class="form-group">
<label class="form-label">Tip Registru</label>
<Dropdown
v-model="filters.registerType"
:options="registerTypeOptions"
option-label="label"
option-value="value"
placeholder="Selectați tipul"
class="w-full"
@change="handleFilterChange"
/>
</div>
<!-- Bank/Cash Account -->
<div class="form-group">
<label class="form-label">{{ contColumnHeader }}</label>
<Dropdown
v-model="filters.bankAccount"
:options="bankAccountOptions"
:placeholder="`Toate ${isBancaType ? 'băncile' : 'casele'}`"
:showClear="true"
class="w-full"
@change="handleFilterChange"
:disabled="!filters.registerType || bankAccountOptions.length === 0"
/>
</div>
<!-- Partner Search -->
<div class="form-group">
<label class="form-label">Căutare Partener</label>
<InputText
v-model="filters.partnerName"
placeholder="Nume partener..."
class="w-full"
@input="handleSearchChange"
/>
</div>
<!-- Bottom sheet actions -->
<div class="bottom-sheet-actions">
<Button
icon="pi pi-filter-slash"
label="Resetează"
class="p-button-outlined p-button-secondary"
@click="resetFilters(); showFilters = false"
/>
<Button
icon="pi pi-check"
label="Aplică"
@click="showFilters = false"
/>
</div>
</div>
</BottomSheet>
<div class="register-view"> <div class="register-view">
<!-- Header --> <!-- Header - Desktop only -->
<div class="page-header"> <div class="page-header" v-if="!isMobile">
<h1 class="page-title"> <h1 class="page-title">
<i class="pi pi-wallet"></i> <i class="pi pi-wallet"></i>
Registru Casă / Bancă Registru Casă / Bancă
@@ -30,71 +127,30 @@
</template> </template>
</Card> </Card>
<!-- Mobile: Two-row toolbar --> <!-- US-109: Mobile Totals Bar (replaces old two-row toolbar) -->
<div v-if="isMobile && companyStore.selectedCompany" class="mobile-toolbar-container"> <div v-if="isMobile && companyStore.selectedCompany && hasData" class="mobile-totals-bar">
<!-- Row 1: Icon-only action buttons --> <div class="mobile-totals-grid">
<div class="mobile-toolbar-buttons"> <div class="total-item">
<Button <span class="total-label">Sold Prec:</span>
icon="pi pi-filter" <span class="total-value">{{ formatCompact(treasuryStore.totals.sold_precedent_all) }}</span>
:class="{ 'filter-active': hasActiveFilters }" </div>
class="p-button-text" <div class="total-item">
@click="showFilters = !showFilters" <span class="total-label">Încasări:</span>
v-tooltip.bottom="'Filtre'" <span class="total-value incasari">{{ formatCompact(treasuryStore.totals.total_incasari_all) }}</span>
/> </div>
<Button <div class="total-item">
icon="pi pi-filter-slash" <span class="total-label">Plăți:</span>
class="p-button-text" <span class="total-value plati">{{ formatCompact(treasuryStore.totals.total_plati_all) }}</span>
@click="resetFilters" </div>
v-tooltip.bottom="'Resetează'" <div class="total-item">
/> <span class="total-label">Sold Final:</span>
<Button <span class="total-value">{{ formatCompact(treasuryStore.totals.sold_final_all) }}</span>
icon="pi pi-file-excel"
class="p-button-text p-button-success"
@click="exportExcel"
:disabled="!hasData"
v-tooltip.bottom="'Excel'"
/>
<Button
icon="pi pi-file-pdf"
class="p-button-text p-button-danger"
@click="exportPDF"
:disabled="!hasData"
v-tooltip.bottom="'PDF'"
/>
<Button
icon="pi pi-refresh"
class="p-button-text"
:loading="treasuryStore.isLoading"
@click="refreshData"
v-tooltip.bottom="'Actualizează'"
/>
</div>
<!-- Row 2: Totals grid -->
<div class="mobile-toolbar-totals">
<div class="mobile-totals-grid">
<div class="total-item">
<span class="total-label">Sold Prec:</span>
<span class="total-value">{{ formatCompact(treasuryStore.totals.sold_precedent_all) }}</span>
</div>
<div class="total-item">
<span class="total-label">Încasări:</span>
<span class="total-value incasari">{{ formatCompact(treasuryStore.totals.total_incasari_all) }}</span>
</div>
<div class="total-item">
<span class="total-label">Plăți:</span>
<span class="total-value plati">{{ formatCompact(treasuryStore.totals.total_plati_all) }}</span>
</div>
<div class="total-item">
<span class="total-label">Sold Final:</span>
<span class="total-value">{{ formatCompact(treasuryStore.totals.sold_final_all) }}</span>
</div>
</div> </div>
</div> </div>
</div> </div>
<!-- Filters --> <!-- Filters - Desktop only (mobile uses BottomSheet) -->
<Card v-if="companyStore.selectedCompany && (!isMobile || showFilters)" class="filters-card"> <Card v-if="companyStore.selectedCompany && !isMobile" class="filters-card">
<template #content> <template #content>
<div class="form"> <div class="form">
<div class="form-row"> <div class="form-row">
@@ -344,6 +400,12 @@
</template> </template>
</Card> </Card>
</div> </div>
<!-- US-109: Mobile Bottom Navigation -->
<MobileBottomNav
v-if="isMobile"
:items="mobileBottomNavItems"
/>
</div> </div>
</template> </template>
@@ -356,6 +418,12 @@ import { useAccountingPeriodStore } from "@reports/stores/sharedStores";
import { format } from "date-fns"; import { format } from "date-fns";
import { exportToExcel, exportBankCashRegisterPDF } from "@reports/utils/exportUtils"; import { exportToExcel, exportBankCashRegisterPDF } from "@reports/utils/exportUtils";
// US-109: Mobile Material Design components
import MobileTopBar from "@shared/components/mobile/MobileTopBar.vue";
import MobileBottomNav from "@shared/components/mobile/MobileBottomNav.vue";
import BottomSheet from "@shared/components/mobile/BottomSheet.vue";
import Sidebar from "primevue/sidebar";
const toast = useToast(); const toast = useToast();
const treasuryStore = useTreasuryStore(); const treasuryStore = useTreasuryStore();
const companyStore = useCompanyStore(); const companyStore = useCompanyStore();
@@ -368,6 +436,51 @@ const selectedCompanyId = ref(companyStore.selectedCompany?.id_firma || null);
const isMobile = ref(window.innerWidth < 768); const isMobile = ref(window.innerWidth < 768);
const showFilters = ref(false); const showFilters = ref(false);
const actionsMenu = ref(null); const actionsMenu = ref(null);
const mobileMenuVisible = ref(false);
// US-109: Toggle mobile hamburger menu
const toggleMobileMenu = () => {
mobileMenuVisible.value = !mobileMenuVisible.value;
};
// US-109: Mobile TopBar actions (filter, refresh, export)
const mobileTopBarActions = computed(() => [
{
icon: "pi pi-filter",
label: "Filtre",
tooltip: "Filtre",
active: hasActiveFilters.value
},
{
icon: "pi pi-refresh",
label: "Actualizează",
tooltip: "Actualizează"
},
{
icon: "pi pi-download",
label: "Export",
tooltip: "Export Excel"
}
]);
// US-109: Handle top bar action clicks
const handleTopBarAction = (action) => {
if (action.icon === "pi pi-filter") {
showFilters.value = !showFilters.value;
} else if (action.icon === "pi pi-refresh") {
refreshData();
} else if (action.icon === "pi pi-download") {
exportExcel();
}
};
// US-109: Bottom nav items for MobileBottomNav component
const mobileBottomNavItems = computed(() => [
{ to: "/data-entry", icon: "pi pi-receipt", label: "Bonuri" },
{ to: "/reports/bank-cash-register", icon: "pi pi-wallet", label: "Trezorerie", active: true },
{ to: "/reports/dashboard", icon: "pi pi-chart-bar", label: "Rapoarte" },
{ to: "/data-entry/ocr-metrics", icon: "pi pi-cog", label: "Setări" }
]);
// Handle window resize // Handle window resize
const handleResize = () => { const handleResize = () => {
@@ -897,12 +1010,9 @@ watch(
</script> </script>
<style scoped> <style scoped>
/* ===== Page-Specific Styles Only ===== */ /* ================================================
/* Uses shared CSS: dashboard.css (.page-header, .page-title, .page-subtitle) */ US-109: BankCashRegisterView Mobile Material Design Styles
/* Uses shared CSS: forms.css (.form-actions) */ ================================================ */
/* Uses shared CSS: tables.css (.table-empty, .loading-state, .negative) */
/* Uses shared CSS: stats.css (.summary-stats-inline) */
/* Uses shared CSS: primevue-overrides.css (DataTable striped rows, hover, compact) */
/* Page Container */ /* Page Container */
.register-view { .register-view {
@@ -911,6 +1021,14 @@ watch(
padding: var(--space-xl); padding: var(--space-xl);
} }
/* Mobile layout adjustments for top/bottom bars */
.mobile-layout .register-view {
padding-top: calc(56px + var(--space-md)); /* Account for fixed MobileTopBar */
padding-bottom: calc(56px + var(--space-md)); /* Account for fixed MobileBottomNav */
padding-left: var(--space-md);
padding-right: var(--space-md);
}
/* Card Spacing */ /* Card Spacing */
.company-selection-card, .company-selection-card,
.filters-card, .filters-card,
@@ -934,7 +1052,239 @@ watch(
color: var(--color-error); color: var(--color-error);
} }
/* Responsive */ /* ================================================
US-109: Mobile Totals Bar
================================================ */
.mobile-totals-bar {
background: var(--surface-card);
border: 1px solid var(--surface-border);
padding: var(--space-sm) var(--space-md);
margin-bottom: var(--space-md);
border-radius: var(--radius-md);
}
.mobile-totals-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: var(--space-xs) var(--space-md);
}
.mobile-totals-bar .total-item {
display: flex;
justify-content: space-between;
align-items: center;
}
.mobile-totals-bar .total-label {
font-size: var(--text-xs);
color: var(--text-color-secondary);
font-weight: var(--font-medium);
}
.mobile-totals-bar .total-value {
font-size: var(--text-sm);
font-weight: var(--font-bold);
color: var(--text-color);
}
.mobile-totals-bar .total-value.incasari {
color: var(--green-600);
}
.mobile-totals-bar .total-value.plati {
color: var(--red-600);
}
/* ================================================
US-109: Mobile Card List (Treasury Cards)
================================================ */
.mobile-card-list {
display: flex;
flex-direction: column;
gap: var(--space-sm);
}
.mobile-data-card {
background: var(--surface-card);
border: 1px solid var(--surface-border);
border-radius: var(--radius-md);
padding: var(--space-md);
}
.mobile-data-card .card-header {
font-weight: var(--font-semibold);
color: var(--text-color);
margin-bottom: var(--space-xs);
font-size: var(--text-base);
}
.mobile-data-card .card-row {
display: flex;
justify-content: space-between;
align-items: center;
font-size: var(--text-sm);
color: var(--text-color-secondary);
}
.mobile-data-card .card-meta {
font-size: var(--text-xs);
color: var(--text-color-secondary);
}
.mobile-data-card .card-amount {
font-weight: var(--font-semibold);
color: var(--text-color);
font-variant-numeric: tabular-nums;
}
.mobile-data-card .card-amount.positive {
color: var(--green-600);
}
.mobile-data-card .card-amount.negative {
color: var(--red-600);
}
.mobile-empty {
text-align: center;
padding: var(--space-xl);
color: var(--text-color-secondary);
}
.mobile-empty i {
font-size: var(--text-3xl);
margin-bottom: var(--space-sm);
display: block;
}
/* ================================================
US-109: Bottom Sheet Styles
================================================ */
.bottom-sheet-title {
font-size: var(--text-lg);
font-weight: var(--font-semibold);
color: var(--text-color);
margin: 0 0 var(--space-md) 0;
}
.bottom-sheet-filters {
display: flex;
flex-direction: column;
gap: var(--space-md);
}
.bottom-sheet-actions {
display: flex;
gap: var(--space-sm);
justify-content: flex-end;
margin-top: var(--space-md);
padding-top: var(--space-md);
border-top: 1px solid var(--surface-border);
}
/* ================================================
US-109: Mobile Sidebar Menu Styles
================================================ */
.sidebar-header {
padding: var(--space-md);
}
.sidebar-title {
font-size: var(--text-xl);
font-weight: var(--font-bold);
color: var(--text-color);
}
.sidebar-menu {
display: flex;
flex-direction: column;
padding: var(--space-sm);
}
.sidebar-item {
display: flex;
align-items: center;
gap: var(--space-md);
padding: var(--space-md);
color: var(--text-color);
text-decoration: none;
border-radius: var(--radius-md);
font-weight: var(--font-medium);
transition: background var(--transition-fast);
}
.sidebar-item:hover {
background: var(--surface-hover);
}
.sidebar-item.active {
background: var(--blue-50);
color: var(--color-primary);
}
.sidebar-item i {
font-size: var(--text-xl);
width: 24px;
text-align: center;
}
/* ================================================
Dark Mode Support
================================================ */
[data-theme="dark"] .mobile-totals-bar .total-value.incasari {
color: var(--green-400);
}
[data-theme="dark"] .mobile-totals-bar .total-value.plati {
color: var(--red-400);
}
[data-theme="dark"] .mobile-data-card .card-amount.positive {
color: var(--green-400);
}
[data-theme="dark"] .mobile-data-card .card-amount.negative {
color: var(--red-400);
}
[data-theme="dark"] .sidebar-item.active {
background: var(--blue-900);
color: var(--blue-400);
}
/* Auto dark mode */
@media (prefers-color-scheme: dark) {
:root:not([data-theme]) .mobile-totals-bar .total-value.incasari {
color: var(--green-400);
}
:root:not([data-theme]) .mobile-totals-bar .total-value.plati {
color: var(--red-400);
}
:root:not([data-theme]) .mobile-data-card .card-amount.positive {
color: var(--green-400);
}
:root:not([data-theme]) .mobile-data-card .card-amount.negative {
color: var(--red-400);
}
:root:not([data-theme]) .sidebar-item.active {
background: var(--blue-900);
color: var(--blue-400);
}
}
/* ================================================
Responsive Design
================================================ */
@media (max-width: 768px) { @media (max-width: 768px) {
.register-view { .register-view {
padding: var(--space-md); padding: var(--space-md);