feat(unified-mobile-desktop-ui): Complete US-510 - Detailed Invoices - Eliminare Filtru Clienți/Furnizori

Implemented by Ralph autonomous loop.
Iteration: 10

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-01-12 22:55:24 +00:00
parent 1ad2c25933
commit 2f6aa6a37a
4 changed files with 68 additions and 71 deletions

View File

@@ -221,8 +221,8 @@
"npm run build passes",
"Verify in browser: filtrul Clienți/Furnizori nu mai apare"
],
"passes": false,
"notes": ""
"passes": true,
"notes": "Completed in iteration 10"
},
{
"id": "US-511",

View File

@@ -124,3 +124,9 @@ Design Reference: src/modules/reports/views/InvoicesView.vue
[2026-01-12 22:45:27] Working on story: US-509
[2026-01-12 22:45:27] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_9_US-509.log)
[2026-01-12 22:48:38] SUCCESS: Story US-509 passed!
[2026-01-12 22:48:38] Changes committed
[2026-01-12 22:48:38] Progress: 9/19 stories completed
[2026-01-12 22:48:40] === Iteration 10/100 ===
[2026-01-12 22:48:40] Working on story: US-510
[2026-01-12 22:48:40] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_10_US-510.log)
[2026-01-12 22:55:24] SUCCESS: Story US-510 passed!

View File

@@ -1,8 +1,9 @@
<template>
<!-- US-501: Mobile Top Bar -->
<!-- US-510: Dynamic title based on route type -->
<MobileTopBar
v-if="isMobile"
title="Facturi Detaliate"
:title="pageTitle"
:show-back="true"
:actions="topBarActions"
@back-click="goBack"
@@ -15,9 +16,10 @@
<main class="main-content" :class="{ 'mobile-layout': isMobile }">
<div class="app-container">
<!-- Page Header - only on desktop -->
<!-- US-510: Dynamic title based on route type -->
<div v-if="!isMobile" class="page-header">
<h1 class="page-title">Facturi Detaliate</h1>
<p class="page-subtitle">Vizualizare detaliată a facturilor clienți și furnizori</p>
<h1 class="page-title">{{ pageTitle }}</h1>
<p class="page-subtitle">{{ pageSubtitle }}</p>
</div>
<!-- Loading state when no company selected -->
@@ -38,20 +40,9 @@
<!-- Main content -->
<div v-else class="invoices-container">
<!-- Desktop Filters Card -->
<!-- US-510: Removed type dropdown - type is determined by route -->
<div v-if="!isMobile" class="filters-card">
<div class="filters-row">
<div class="filter-group">
<label class="form-label">Tip</label>
<Dropdown
v-model="selectedType"
:options="typeOptions"
optionLabel="label"
optionValue="value"
placeholder="Selectați tipul"
class="w-full"
@change="loadDetailedData"
/>
</div>
<div class="filter-group">
<label class="form-label">Căutare</label>
<InputText
@@ -101,11 +92,8 @@
</div>
<!-- Mobile Filter Chips (compact summary) -->
<!-- US-510: Removed type filter chip - type is determined by route -->
<div v-if="isMobile" class="mobile-filter-summary">
<div class="filter-chip" @click="openFilters">
<i class="pi pi-list"></i>
<span>{{ getTypeLabel(selectedType) }}</span>
</div>
<div v-if="searchTerm" class="filter-chip active" @click="openFilters">
<i class="pi pi-search"></i>
<span>{{ searchTerm }}</span>
@@ -136,7 +124,7 @@
<div v-if="!isMobile" class="table-wrapper">
<!-- Treasury DataTable (no expansion needed) -->
<DataTable
v-if="selectedType === 'treasury'"
v-if="invoiceType === 'treasury'"
:value="paginatedData"
:loading="isLoading"
stripedRows
@@ -158,12 +146,12 @@
<!-- Table Header -->
<div class="groups-table-header">
<div class="header-cell expand-col"></div>
<div class="header-cell name-col">{{ selectedType === 'clients' ? 'Client' : 'Furnizor' }}</div>
<div class="header-cell name-col">{{ invoiceType === 'clients' ? 'Client' : 'Furnizor' }}</div>
<div class="header-cell">Nr. Document</div>
<div class="header-cell">Data Document</div>
<div class="header-cell">Data Scadență</div>
<div class="header-cell text-right">Facturat</div>
<div class="header-cell text-right">{{ selectedType === 'clients' ? 'Încasat' : 'Achitat' }}</div>
<div class="header-cell text-right">{{ invoiceType === 'clients' ? 'Încasat' : 'Achitat' }}</div>
<div class="header-cell text-right">Sold</div>
</div>
@@ -209,7 +197,7 @@
<div class="row-cell text-right">
<span class="font-mono">
{{ group.facturi.length === 1
? formatCurrency(group.facturi[0][selectedType === 'clients' ? 'incasat' : 'achitat'])
? formatCurrency(group.facturi[0][invoiceType === 'clients' ? 'incasat' : 'achitat'])
: '-' }}
</span>
</div>
@@ -247,7 +235,7 @@
</div>
<div class="row-cell text-right">
<span class="font-mono">
{{ formatCurrency(factura[selectedType === 'clients' ? 'incasat' : 'achitat']) }}
{{ formatCurrency(factura[invoiceType === 'clients' ? 'incasat' : 'achitat']) }}
</span>
</div>
<div class="row-cell text-right">
@@ -275,7 +263,7 @@
<!-- Mobile Cards -->
<div v-if="isMobile" class="mobile-cards">
<!-- Treasury Cards -->
<template v-if="selectedType === 'treasury'">
<template v-if="invoiceType === 'treasury'">
<div
v-for="row in paginatedData"
:key="row.id"
@@ -375,7 +363,7 @@
</template>
<!-- Empty state -->
<div v-if="(selectedType === 'treasury' ? paginatedData : paginatedGroups).length === 0" class="empty-data">
<div v-if="(invoiceType === 'treasury' ? paginatedData : paginatedGroups).length === 0" class="empty-data">
<i class="pi pi-inbox"></i>
<p>Nu există facturi pentru criteriile selectate</p>
</div>
@@ -398,7 +386,7 @@
<span class="total-label">Total Sold:</span>
<span class="total-value">{{ formatCurrency(calculateTotal('sold')) }}</span>
</div>
<div v-if="selectedType !== 'treasury'" class="total-item">
<div v-if="invoiceType !== 'treasury'" class="total-item">
<span class="total-label">Total Facturat:</span>
<span class="total-value">{{ formatCurrency(calculateTotal('facturat')) }}</span>
</div>
@@ -412,22 +400,11 @@
<MobileBottomNav v-if="isMobile" />
<!-- Mobile Filters BottomSheet -->
<!-- US-510: Removed type dropdown - type is determined by route -->
<BottomSheet v-model="isFilterSheetOpen">
<div class="filter-sheet-content">
<h3 class="filter-sheet-title">Filtre</h3>
<div class="filter-sheet-group">
<label class="form-label">Tip</label>
<Dropdown
v-model="selectedType"
:options="typeOptions"
optionLabel="label"
optionValue="value"
placeholder="Selectați tipul"
class="w-full"
/>
</div>
<div class="filter-sheet-group">
<label class="form-label">Căutare</label>
<InputText
@@ -471,7 +448,7 @@
<script setup>
import { ref, computed, onMounted, onUnmounted, watch, Transition } from 'vue'
import { useRouter } from 'vue-router'
import { useRouter, useRoute } from 'vue-router'
import Button from 'primevue/button'
import Dropdown from 'primevue/dropdown'
import InputText from 'primevue/inputtext'
@@ -491,11 +468,26 @@ import jsPDF from 'jspdf'
import 'jspdf-autotable'
const router = useRouter()
const route = useRoute()
const toast = useToast()
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-510: Dynamic page title and subtitle based on invoice type
const pageTitle = computed(() => {
return invoiceType.value === 'clients' ? 'Facturi Clienți' : 'Facturi Furnizori'
})
const pageSubtitle = computed(() => {
return invoiceType.value === 'clients'
? 'Vizualizare detaliată a facturilor clienți'
: 'Vizualizare detaliată a facturilor furnizori'
})
// Mobile detection
const windowWidth = ref(window.innerWidth)
const isMobile = computed(() => windowWidth.value < 768)
@@ -507,7 +499,7 @@ const handleResize = () => {
// State
const isLoading = ref(false)
const error = ref(null)
const selectedType = ref('clients')
// US-510: Removed selectedType - now using invoiceType from route
const searchTerm = ref('')
const selectedPeriod = ref('all')
const detailedData = ref([])
@@ -517,11 +509,7 @@ const expandedGroups = ref(new Set())
const isFilterSheetOpen = ref(false)
// Options
const typeOptions = [
{ label: 'Clienți', value: 'clients' },
{ label: 'Furnizori', value: 'suppliers' },
{ label: 'Trezorerie', value: 'treasury' }
]
// US-510: Removed typeOptions - type is now determined by route
const periodOptions = [
{ label: 'Toate', value: 'all' },
@@ -533,10 +521,10 @@ const periodOptions = [
]
// US-501: Check if filters have non-default values
// US-510: Removed selectedType check - type is now route-based
const hasActiveFilters = computed(() => {
return searchTerm.value !== '' ||
selectedPeriod.value !== 'all' ||
selectedType.value !== 'clients'
selectedPeriod.value !== 'all'
})
// US-501: Mobile TopBar actions (filter, reset, export dropdown)
@@ -625,10 +613,10 @@ const filteredData = computed(() => {
})
const groupedData = computed(() => {
if (selectedType.value === 'treasury') return []
if (invoiceType.value === 'treasury') return []
const groups = {}
const nameField = selectedType.value === 'clients' ? 'client' : 'furnizor'
const nameField = invoiceType.value === 'clients' ? 'client' : 'furnizor'
filteredData.value.forEach((row) => {
const name = row[nameField]
@@ -654,12 +642,12 @@ const groupedData = computed(() => {
})
const paginatedGroups = computed(() => {
if (selectedType.value === 'treasury') return []
if (invoiceType.value === 'treasury') return []
return groupedData.value
})
const paginatedData = computed(() => {
if (selectedType.value !== 'treasury') return []
if (invoiceType.value !== 'treasury') return []
return filteredData.value
})
@@ -668,10 +656,7 @@ const totalRecords = computed(() => {
})
// Helper functions
const getTypeLabel = (type) => {
const option = typeOptions.find(o => o.value === type)
return option?.label || type
}
// US-510: Removed getTypeLabel - no longer needed
const getPeriodLabel = (period) => {
const option = periodOptions.find(o => o.value === period)
@@ -731,7 +716,7 @@ const handleSearch = () => {
}
const resetFilters = () => {
selectedType.value = 'clients'
// US-510: Removed type reset - type is now determined by route
searchTerm.value = ''
selectedPeriod.value = 'all'
firstRow.value = 0
@@ -765,7 +750,7 @@ const loadDetailedData = async () => {
const an = periodStore.selectedPeriod?.an || null
const response = await dashboardStore.loadDetailedData(
selectedType.value,
invoiceType.value,
companyStore.selectedCompany.id_firma,
page,
rowsPerPage.value,
@@ -794,8 +779,8 @@ const loadDetailedData = async () => {
const exportExcel = () => {
const ws = XLSX.utils.json_to_sheet(filteredData.value)
const wb = XLSX.utils.book_new()
XLSX.utils.book_append_sheet(wb, ws, selectedType.value)
XLSX.writeFile(wb, `facturi_${selectedType.value}_${new Date().toISOString().split('T')[0]}.xlsx`)
XLSX.utils.book_append_sheet(wb, ws, invoiceType.value)
XLSX.writeFile(wb, `facturi_${invoiceType.value}_${new Date().toISOString().split('T')[0]}.xlsx`)
toast.add({
severity: 'success',
@@ -808,18 +793,18 @@ const exportExcel = () => {
const exportPDF = () => {
const doc = new jsPDF()
const columns = selectedType.value === 'treasury'
const columns = invoiceType.value === 'treasury'
? ['Cont', 'Nume Cont', 'Sold', 'Valută', 'Tip']
: selectedType.value === 'clients'
: invoiceType.value === 'clients'
? ['Client', 'Nr. Document', 'Data Doc.', 'Scadență', 'Facturat', 'Încasat', 'Sold']
: ['Furnizor', 'Nr. Document', 'Data Doc.', 'Scadență', 'Facturat', 'Achitat', 'Sold']
const rows = filteredData.value.map((row) => {
if (selectedType.value === 'treasury') {
if (invoiceType.value === 'treasury') {
return [row.cont, row.nume_cont, formatCurrency(row.sold), row.valuta, row.tip]
}
const nameField = selectedType.value === 'clients' ? 'client' : 'furnizor'
const paidField = selectedType.value === 'clients' ? 'incasat' : 'achitat'
const nameField = invoiceType.value === 'clients' ? 'client' : 'furnizor'
const paidField = invoiceType.value === 'clients' ? 'incasat' : 'achitat'
return [
row[nameField],
row.numar_document,
@@ -839,7 +824,7 @@ const exportPDF = () => {
headStyles: { fillColor: [59, 130, 246] }
})
doc.save(`facturi_${selectedType.value}_${new Date().toISOString().split('T')[0]}.pdf`)
doc.save(`facturi_${invoiceType.value}_${new Date().toISOString().split('T')[0]}.pdf`)
toast.add({
severity: 'success',

View File

@@ -61,10 +61,16 @@ const routes = [
meta: { requiresAuth: true, title: 'Analiză Scadențe - ROA2WEB' }
},
{
path: 'detailed-invoices',
name: 'DetailedInvoices',
path: 'detailed-invoices/clients',
name: 'DetailedInvoicesClients',
component: () => import('@reports/views/DetailedInvoicesView.vue'),
meta: { requiresAuth: true, title: 'Facturi Detaliate - ROA2WEB' }
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' }
}
]
},