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:
@@ -1,8 +1,105 @@
|
||||
<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">
|
||||
<!-- Header -->
|
||||
<div class="page-header">
|
||||
<!-- Header - Desktop only -->
|
||||
<div class="page-header" v-if="!isMobile">
|
||||
<h1 class="page-title">
|
||||
<i class="pi pi-wallet"></i>
|
||||
Registru Casă / Bancă
|
||||
@@ -30,71 +127,30 @@
|
||||
</template>
|
||||
</Card>
|
||||
|
||||
<!-- Mobile: Two-row toolbar -->
|
||||
<div v-if="isMobile && companyStore.selectedCompany" class="mobile-toolbar-container">
|
||||
<!-- Row 1: Icon-only action buttons -->
|
||||
<div class="mobile-toolbar-buttons">
|
||||
<Button
|
||||
icon="pi pi-filter"
|
||||
:class="{ 'filter-active': hasActiveFilters }"
|
||||
class="p-button-text"
|
||||
@click="showFilters = !showFilters"
|
||||
v-tooltip.bottom="'Filtre'"
|
||||
/>
|
||||
<Button
|
||||
icon="pi pi-filter-slash"
|
||||
class="p-button-text"
|
||||
@click="resetFilters"
|
||||
v-tooltip.bottom="'Resetează'"
|
||||
/>
|
||||
<Button
|
||||
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>
|
||||
<!-- US-109: Mobile Totals Bar (replaces old two-row toolbar) -->
|
||||
<div v-if="isMobile && companyStore.selectedCompany && hasData" class="mobile-totals-bar">
|
||||
<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>
|
||||
|
||||
<!-- Filters -->
|
||||
<Card v-if="companyStore.selectedCompany && (!isMobile || showFilters)" class="filters-card">
|
||||
<!-- Filters - Desktop only (mobile uses BottomSheet) -->
|
||||
<Card v-if="companyStore.selectedCompany && !isMobile" class="filters-card">
|
||||
<template #content>
|
||||
<div class="form">
|
||||
<div class="form-row">
|
||||
@@ -344,6 +400,12 @@
|
||||
</template>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<!-- US-109: Mobile Bottom Navigation -->
|
||||
<MobileBottomNav
|
||||
v-if="isMobile"
|
||||
:items="mobileBottomNavItems"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -356,6 +418,12 @@ import { useAccountingPeriodStore } from "@reports/stores/sharedStores";
|
||||
import { format } from "date-fns";
|
||||
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 treasuryStore = useTreasuryStore();
|
||||
const companyStore = useCompanyStore();
|
||||
@@ -368,6 +436,51 @@ const selectedCompanyId = ref(companyStore.selectedCompany?.id_firma || null);
|
||||
const isMobile = ref(window.innerWidth < 768);
|
||||
const showFilters = ref(false);
|
||||
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
|
||||
const handleResize = () => {
|
||||
@@ -897,12 +1010,9 @@ watch(
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* ===== Page-Specific Styles Only ===== */
|
||||
/* Uses shared CSS: dashboard.css (.page-header, .page-title, .page-subtitle) */
|
||||
/* 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) */
|
||||
/* ================================================
|
||||
US-109: BankCashRegisterView Mobile Material Design Styles
|
||||
================================================ */
|
||||
|
||||
/* Page Container */
|
||||
.register-view {
|
||||
@@ -911,6 +1021,14 @@ watch(
|
||||
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 */
|
||||
.company-selection-card,
|
||||
.filters-card,
|
||||
@@ -934,7 +1052,239 @@ watch(
|
||||
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) {
|
||||
.register-view {
|
||||
padding: var(--space-md);
|
||||
|
||||
Reference in New Issue
Block a user