|
|
|
|
@@ -1,25 +1,65 @@
|
|
|
|
|
<template>
|
|
|
|
|
<!-- US-501: Mobile Top Bar -->
|
|
|
|
|
<!-- US-510: Dynamic title based on route type -->
|
|
|
|
|
<!-- US-603: Title is now "Facturi Detaliate" with tabs below -->
|
|
|
|
|
<MobileTopBar
|
|
|
|
|
v-if="isMobile"
|
|
|
|
|
:title="pageTitle"
|
|
|
|
|
title="Facturi Detaliate"
|
|
|
|
|
:show-back="true"
|
|
|
|
|
:actions="topBarActions"
|
|
|
|
|
@back-click="goBack"
|
|
|
|
|
@action-click="handleTopBarAction"
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<!-- US-603: 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>
|
|
|
|
|
|
|
|
|
|
<!-- US-501: Export Menu (for mobile export dropdown) -->
|
|
|
|
|
<Menu ref="exportMenu" :model="exportMenuItems" :popup="true" />
|
|
|
|
|
|
|
|
|
|
<main class="main-content" :class="{ 'mobile-layout': isMobile }">
|
|
|
|
|
<main class="main-content" :class="{ 'mobile-layout': isMobile, 'has-tabs': isMobile }">
|
|
|
|
|
<div class="app-container">
|
|
|
|
|
<!-- Page Header - only on desktop -->
|
|
|
|
|
<!-- US-510: Dynamic title based on route type -->
|
|
|
|
|
<!-- US-603: Desktop header with tabs -->
|
|
|
|
|
<div v-if="!isMobile" class="page-header">
|
|
|
|
|
<h1 class="page-title">{{ pageTitle }}</h1>
|
|
|
|
|
<p class="page-subtitle">{{ pageSubtitle }}</p>
|
|
|
|
|
<h1 class="page-title">Facturi Detaliate</h1>
|
|
|
|
|
<p class="page-subtitle">Vizualizare detaliată a facturilor clienți și furnizori</p>
|
|
|
|
|
|
|
|
|
|
<!-- US-603: Desktop Tabs for Clienți/Furnizori -->
|
|
|
|
|
<div class="desktop-tabs">
|
|
|
|
|
<button
|
|
|
|
|
class="desktop-tab"
|
|
|
|
|
:class="{ active: activeTab === 'clients' }"
|
|
|
|
|
@click="switchTab('clients')"
|
|
|
|
|
>
|
|
|
|
|
<i class="pi pi-users"></i>
|
|
|
|
|
<span>Clienți</span>
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
class="desktop-tab"
|
|
|
|
|
:class="{ active: activeTab === 'suppliers' }"
|
|
|
|
|
@click="switchTab('suppliers')"
|
|
|
|
|
>
|
|
|
|
|
<i class="pi pi-truck"></i>
|
|
|
|
|
<span>Furnizori</span>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Loading state when no company selected -->
|
|
|
|
|
@@ -474,19 +514,31 @@ const dashboardStore = useDashboardStore()
|
|
|
|
|
const companyStore = useCompanyStore()
|
|
|
|
|
const periodStore = useAccountingPeriodStore()
|
|
|
|
|
|
|
|
|
|
// US-510: Get invoice type from route meta (clients or suppliers)
|
|
|
|
|
const invoiceType = computed(() => route.meta.invoiceType || 'clients')
|
|
|
|
|
// US-603: Tab state - initialized from route meta or query param
|
|
|
|
|
const activeTab = ref(route.query.tab === 'suppliers' ? 'suppliers' : (route.meta.invoiceType || 'clients'))
|
|
|
|
|
|
|
|
|
|
// US-510: Dynamic page title and subtitle based on invoice type
|
|
|
|
|
const pageTitle = computed(() => {
|
|
|
|
|
return invoiceType.value === 'clients' ? 'Facturi Clienți' : 'Facturi Furnizori'
|
|
|
|
|
})
|
|
|
|
|
// US-603: invoiceType is now derived from activeTab (not route meta)
|
|
|
|
|
const invoiceType = computed(() => activeTab.value)
|
|
|
|
|
|
|
|
|
|
const pageSubtitle = computed(() => {
|
|
|
|
|
return invoiceType.value === 'clients'
|
|
|
|
|
? 'Vizualizare detaliată a facturilor clienți'
|
|
|
|
|
: 'Vizualizare detaliată a facturilor furnizori'
|
|
|
|
|
})
|
|
|
|
|
// US-603: Switch between Clienți/Furnizori tabs
|
|
|
|
|
const switchTab = async (tab) => {
|
|
|
|
|
if (tab === activeTab.value) return
|
|
|
|
|
|
|
|
|
|
activeTab.value = tab
|
|
|
|
|
|
|
|
|
|
// Update URL query param without full navigation
|
|
|
|
|
router.replace({
|
|
|
|
|
query: {
|
|
|
|
|
...route.query,
|
|
|
|
|
tab: tab === 'suppliers' ? 'suppliers' : undefined // Remove param if 'clients' (default)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// Reset pagination and reload data
|
|
|
|
|
firstRow.value = 0
|
|
|
|
|
expandedGroups.value.clear()
|
|
|
|
|
await loadDetailedData()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Mobile detection
|
|
|
|
|
const windowWidth = ref(window.innerWidth)
|
|
|
|
|
@@ -880,6 +932,104 @@ onUnmounted(() => {
|
|
|
|
|
padding-bottom: 56px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* US-603: Extra padding when tabs are visible on mobile */
|
|
|
|
|
.main-content.mobile-layout.has-tabs {
|
|
|
|
|
padding-top: calc(56px + 48px); /* MobileTopBar + tabs */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ================================================
|
|
|
|
|
US-603: Mobile Tabs (Clienți/Furnizori)
|
|
|
|
|
Material Design 3 inspired full-width tabs
|
|
|
|
|
================================================ */
|
|
|
|
|
|
|
|
|
|
.mobile-tabs-container {
|
|
|
|
|
position: fixed;
|
|
|
|
|
top: 56px; /* Below MobileTopBar */
|
|
|
|
|
left: 0;
|
|
|
|
|
right: 0;
|
|
|
|
|
z-index: var(--z-sticky);
|
|
|
|
|
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);
|
|
|
|
|
min-height: 48px;
|
|
|
|
|
background: transparent;
|
|
|
|
|
border: none;
|
|
|
|
|
border-bottom: 2px solid transparent;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: all var(--transition-fast);
|
|
|
|
|
color: var(--text-color-secondary);
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tab-label {
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.5px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ================================================
|
|
|
|
|
US-603: Desktop Tabs (Clienți/Furnizori)
|
|
|
|
|
================================================ */
|
|
|
|
|
|
|
|
|
|
.desktop-tabs {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: var(--space-sm);
|
|
|
|
|
margin-top: var(--space-md);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.desktop-tab {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: var(--space-sm);
|
|
|
|
|
padding: var(--space-sm) var(--space-lg);
|
|
|
|
|
background: var(--surface-hover);
|
|
|
|
|
border: 1px solid var(--surface-border);
|
|
|
|
|
border-radius: var(--radius-md);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: all var(--transition-fast);
|
|
|
|
|
color: var(--text-color-secondary);
|
|
|
|
|
font-size: var(--text-sm);
|
|
|
|
|
font-weight: var(--font-medium);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.desktop-tab:hover {
|
|
|
|
|
background: var(--surface-card);
|
|
|
|
|
border-color: var(--color-primary);
|
|
|
|
|
color: var(--color-primary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.desktop-tab.active {
|
|
|
|
|
background: var(--color-primary);
|
|
|
|
|
border-color: var(--color-primary);
|
|
|
|
|
color: var(--color-text-inverse);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.desktop-tab i {
|
|
|
|
|
font-size: var(--text-base);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* App container */
|
|
|
|
|
.app-container {
|
|
|
|
|
max-width: 1400px;
|
|
|
|
|
|