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:
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user