feat(ui-fixes-phase6): Complete US-603 - Implementare Pagină Facturi Detaliate

Implemented by Ralph autonomous loop.
Iteration: 3

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-01-13 16:11:33 +00:00
parent ea82f61a74
commit 496fd8014e
4 changed files with 178 additions and 32 deletions

View File

@@ -84,8 +84,8 @@
"npm run typecheck passes",
"Verify in browser: Page shows detailed invoices table with Clienți/Furnizori tabs"
],
"passes": false,
"notes": "Creează fișier nou, adaugă rută în src/modules/reports/router/reports.routes.js"
"passes": true,
"notes": "Completed in iteration 3"
},
{
"id": "US-604",

View File

@@ -255,3 +255,9 @@ PRD: tasks/prd-ui-fixes-phase6.md
[2026-01-13 16:02:34] Working on story: US-602
[2026-01-13 16:02:34] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_2_US-602.log)
[2026-01-13 16:08:47] SUCCESS: Story US-602 passed!
[2026-01-13 16:08:47] Changes committed
[2026-01-13 16:08:47] Progress: 2/10 stories completed
[2026-01-13 16:08:49] === Iteration 3/30 ===
[2026-01-13 16:08:49] Working on story: US-603
[2026-01-13 16:08:49] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_3_US-603.log)
[2026-01-13 16:11:33] SUCCESS: Story US-603 passed!

View File

@@ -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)
// 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)
}
})
const pageSubtitle = computed(() => {
return invoiceType.value === 'clients'
? 'Vizualizare detaliată a facturilor clienți'
: 'Vizualizare detaliată a facturilor furnizori'
})
// 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;

View File

@@ -74,21 +74,11 @@ const routes = [
meta: { requiresAuth: true, title: 'Analiză Scadențe - ROA2WEB' }
},
{
// US-601: Redirect base detailed-invoices path to clients (default)
// US-603: Single route for Detailed Invoices with tabs (Clienți/Furnizori)
path: 'detailed-invoices',
redirect: '/reports/detailed-invoices/clients'
},
{
path: 'detailed-invoices/clients',
name: 'DetailedInvoicesClients',
name: 'DetailedInvoices',
component: () => import('@reports/views/DetailedInvoicesView.vue'),
meta: { requiresAuth: true, title: 'Facturi Clienți - ROA2WEB', invoiceType: 'clients' }
},
{
path: 'detailed-invoices/suppliers',
name: 'DetailedInvoicesSuppliers',
component: () => import('@reports/views/DetailedInvoicesView.vue'),
meta: { requiresAuth: true, title: 'Facturi Furnizori - ROA2WEB', invoiceType: 'suppliers' }
meta: { requiresAuth: true, title: 'Facturi Detaliate - ROA2WEB' }
}
]
},