feat(unified-mobile-desktop-ui): Complete US-513 - Analize Scadențe - Eliminare Secțiune Facturi Detaliate
Implemented by Ralph autonomous loop. Iteration: 14 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -278,8 +278,8 @@
|
|||||||
"npm run build passes",
|
"npm run build passes",
|
||||||
"Verify in browser: pagina conține doar scadențe"
|
"Verify in browser: pagina conține doar scadențe"
|
||||||
],
|
],
|
||||||
"passes": false,
|
"passes": true,
|
||||||
"notes": ""
|
"notes": "Completed in iteration 14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "US-514",
|
"id": "US-514",
|
||||||
|
|||||||
@@ -142,3 +142,14 @@ Design Reference: src/modules/reports/views/InvoicesView.vue
|
|||||||
[2026-01-12 22:57:15] Working on story: US-512
|
[2026-01-12 22:57:15] Working on story: US-512
|
||||||
[2026-01-12 22:57:15] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_12_US-512.log)
|
[2026-01-12 22:57:15] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_12_US-512.log)
|
||||||
[2026-01-12 23:05:48] SUCCESS: Story US-512 passed!
|
[2026-01-12 23:05:48] SUCCESS: Story US-512 passed!
|
||||||
|
[2026-01-12 23:05:48] Changes committed
|
||||||
|
[2026-01-12 23:05:48] Progress: 12/19 stories completed
|
||||||
|
[2026-01-12 23:05:50] === Iteration 13/100 ===
|
||||||
|
[2026-01-12 23:05:50] Working on story: US-513
|
||||||
|
[2026-01-12 23:05:50] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_13_US-513.log)
|
||||||
|
[2026-01-12 23:09:55] Story US-513 not yet complete, continuing...
|
||||||
|
[2026-01-12 23:09:55] Progress: 12/19 stories completed
|
||||||
|
[2026-01-12 23:09:57] === Iteration 14/100 ===
|
||||||
|
[2026-01-12 23:09:57] Working on story: US-513
|
||||||
|
[2026-01-12 23:09:57] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_14_US-513.log)
|
||||||
|
[2026-01-12 23:11:56] SUCCESS: Story US-513 passed!
|
||||||
|
|||||||
@@ -1,85 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- US-501: Mobile Top Bar with unified header actions -->
|
<!-- US-513: Simplified Mobile Top Bar - no filters/export since no detailed data -->
|
||||||
<MobileTopBar
|
<MobileTopBar
|
||||||
v-if="isMobile"
|
v-if="isMobile"
|
||||||
title="Scadențe"
|
title="Scadențe"
|
||||||
:show-back="true"
|
:show-back="true"
|
||||||
:actions="mobileTopBarActions"
|
|
||||||
@back-click="goBack"
|
@back-click="goBack"
|
||||||
@action-click="handleTopBarAction"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- US-501: Export Menu (for mobile export dropdown) -->
|
<main class="main-content" :class="{ 'mobile-layout': isMobile }">
|
||||||
<Menu ref="exportMenu" :model="exportMenuItems" :popup="true" />
|
|
||||||
|
|
||||||
<!-- US-306: Filter BottomSheet for mobile -->
|
|
||||||
<BottomSheet v-model="showFilters">
|
|
||||||
<h3 class="bottom-sheet-title">Filtre</h3>
|
|
||||||
<div class="bottom-sheet-filters">
|
|
||||||
<!-- Period Filter -->
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="form-label">Perioadă</label>
|
|
||||||
<Dropdown
|
|
||||||
v-model="filterPeriod"
|
|
||||||
:options="periodOptions"
|
|
||||||
optionLabel="label"
|
|
||||||
optionValue="value"
|
|
||||||
placeholder="Selectați perioada"
|
|
||||||
class="w-full"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Type Filter -->
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="form-label">Tip</label>
|
|
||||||
<Dropdown
|
|
||||||
v-model="filterType"
|
|
||||||
:options="typeOptions"
|
|
||||||
optionLabel="label"
|
|
||||||
optionValue="value"
|
|
||||||
placeholder="Selectați tipul"
|
|
||||||
class="w-full"
|
|
||||||
/>
|
|
||||||
</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"
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
icon="pi pi-check"
|
|
||||||
label="Aplică"
|
|
||||||
@click="applyFilters"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</BottomSheet>
|
|
||||||
|
|
||||||
<!-- US-305: 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>
|
|
||||||
|
|
||||||
<main class="main-content" :class="{ 'mobile-layout': isMobile, 'has-tabs': isMobile }">
|
|
||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
<!-- Page Header - only on desktop -->
|
<!-- Page Header - only on desktop -->
|
||||||
<div v-if="!isMobile" class="page-header">
|
<div v-if="!isMobile" class="page-header">
|
||||||
@@ -102,157 +30,43 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Main content - MaturityAndDetailsCard -->
|
<!-- Main content - MaturityAnalysisCard (US-513: doar analiza scadențelor, fără facturi detaliate) -->
|
||||||
<div v-else class="maturity-container">
|
<div v-else class="maturity-container">
|
||||||
<MaturityAndDetailsCard
|
<MaturityAnalysisCard
|
||||||
ref="maturityCardRef"
|
ref="maturityCardRef"
|
||||||
:companyId="companyStore.selectedCompany?.id_firma"
|
:companyId="companyStore.selectedCompany?.id_firma"
|
||||||
:activeTab="activeTab"
|
|
||||||
:isMobile="isMobile"
|
|
||||||
@periodChanged="handlePeriodChange"
|
@periodChanged="handlePeriodChange"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<!-- Mobile Bottom Nav - US-307: Using default nav items -->
|
<!-- Mobile Bottom Nav -->
|
||||||
<MobileBottomNav v-if="isMobile" />
|
<MobileBottomNav v-if="isMobile" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import Button from 'primevue/button'
|
import Button from 'primevue/button'
|
||||||
import Dropdown from 'primevue/dropdown'
|
|
||||||
import MobileTopBar from '@shared/components/mobile/MobileTopBar.vue'
|
import MobileTopBar from '@shared/components/mobile/MobileTopBar.vue'
|
||||||
import MobileBottomNav from '@shared/components/mobile/MobileBottomNav.vue'
|
import MobileBottomNav from '@shared/components/mobile/MobileBottomNav.vue'
|
||||||
import BottomSheet from '@shared/components/mobile/BottomSheet.vue'
|
import MaturityAnalysisCard from '@reports/components/dashboard/cards/MaturityAnalysisCard.vue'
|
||||||
import Menu from 'primevue/menu'
|
|
||||||
import MaturityAndDetailsCard from '@reports/components/dashboard/cards/MaturityAndDetailsCard.vue'
|
|
||||||
import { useCompanyStore } from '@reports/stores/sharedStores'
|
import { useCompanyStore } from '@reports/stores/sharedStores'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
|
||||||
const companyStore = useCompanyStore()
|
const companyStore = useCompanyStore()
|
||||||
|
|
||||||
// US-305: Tab state synced with URL query params
|
|
||||||
const activeTab = ref(route.query.tab === 'suppliers' ? 'suppliers' : 'clients')
|
|
||||||
|
|
||||||
// Detectare mobile - reactive with resize listener
|
// Detectare mobile - reactive with resize listener
|
||||||
const windowWidth = ref(window.innerWidth)
|
const windowWidth = ref(window.innerWidth)
|
||||||
const isMobile = computed(() => windowWidth.value < 768)
|
const isMobile = computed(() => windowWidth.value < 768)
|
||||||
|
|
||||||
// US-306: Ref to MaturityAndDetailsCard for calling exposed methods
|
// Ref to MaturityAnalysisCard
|
||||||
const maturityCardRef = ref(null)
|
const maturityCardRef = ref(null)
|
||||||
|
|
||||||
// US-306: Mobile filter state
|
|
||||||
const showFilters = ref(false)
|
|
||||||
const filterPeriod = ref('1m')
|
|
||||||
const filterType = ref('clients')
|
|
||||||
|
|
||||||
// US-306: Filter options
|
|
||||||
const periodOptions = [
|
|
||||||
{ label: '7 zile', value: '7d' },
|
|
||||||
{ label: '1 lună', value: '1m' },
|
|
||||||
{ label: '3 luni', value: '3m' },
|
|
||||||
{ label: '6 luni', value: '6m' },
|
|
||||||
{ label: '12 luni', value: '12m' },
|
|
||||||
{ label: 'Toate', value: 'all' }
|
|
||||||
]
|
|
||||||
|
|
||||||
const typeOptions = [
|
|
||||||
{ label: 'Clienți', value: 'clients' },
|
|
||||||
{ label: 'Furnizori', value: 'suppliers' },
|
|
||||||
{ label: 'Trezorerie', value: 'treasury' }
|
|
||||||
]
|
|
||||||
|
|
||||||
// US-306: Check if filters have non-default values
|
|
||||||
const hasActiveFilters = computed(() => {
|
|
||||||
return filterPeriod.value !== '1m' || filterType.value !== 'clients'
|
|
||||||
})
|
|
||||||
|
|
||||||
// US-501: Mobile TopBar actions (filter, reset, export dropdown)
|
|
||||||
const mobileTopBarActions = computed(() => [
|
|
||||||
{
|
|
||||||
icon: 'pi pi-filter',
|
|
||||||
label: 'Filtre',
|
|
||||||
tooltip: 'Filtre',
|
|
||||||
active: hasActiveFilters.value
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: 'pi pi-filter-slash',
|
|
||||||
label: 'Resetează',
|
|
||||||
tooltip: 'Resetează Filtrele'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: 'pi pi-download',
|
|
||||||
label: 'Export',
|
|
||||||
tooltip: 'Export'
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
// US-501: Export menu ref for mobile
|
|
||||||
const exportMenu = ref(null)
|
|
||||||
const exportMenuItems = ref([
|
|
||||||
{
|
|
||||||
label: 'Export PDF',
|
|
||||||
icon: 'pi pi-file-pdf',
|
|
||||||
command: () => {
|
|
||||||
if (maturityCardRef.value) {
|
|
||||||
maturityCardRef.value.exportPDF()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Export XLSX',
|
|
||||||
icon: 'pi pi-file-excel',
|
|
||||||
command: () => {
|
|
||||||
if (maturityCardRef.value) {
|
|
||||||
maturityCardRef.value.exportExcel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
// US-501: Handle top bar action clicks
|
|
||||||
const handleTopBarAction = (action, event) => {
|
|
||||||
if (action.icon === 'pi pi-filter') {
|
|
||||||
// Sync current values from child component before opening
|
|
||||||
if (maturityCardRef.value) {
|
|
||||||
filterPeriod.value = maturityCardRef.value.selectedPeriod
|
|
||||||
filterType.value = maturityCardRef.value.selectedType
|
|
||||||
}
|
|
||||||
showFilters.value = !showFilters.value
|
|
||||||
} else if (action.icon === 'pi pi-filter-slash') {
|
|
||||||
resetFilters()
|
|
||||||
} else if (action.icon === 'pi pi-download') {
|
|
||||||
exportMenu.value.toggle(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// US-306: Apply filters to child component
|
|
||||||
const applyFilters = () => {
|
|
||||||
if (maturityCardRef.value) {
|
|
||||||
maturityCardRef.value.selectedPeriod = filterPeriod.value
|
|
||||||
maturityCardRef.value.selectedType = filterType.value
|
|
||||||
maturityCardRef.value.loadDetailedData()
|
|
||||||
}
|
|
||||||
showFilters.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// US-306: Reset filters to defaults
|
|
||||||
const resetFilters = () => {
|
|
||||||
filterPeriod.value = '1m'
|
|
||||||
filterType.value = 'clients'
|
|
||||||
applyFilters()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle window resize for mobile detection
|
// Handle window resize for mobile detection
|
||||||
const handleResize = () => {
|
const handleResize = () => {
|
||||||
windowWidth.value = window.innerWidth
|
windowWidth.value = window.innerWidth
|
||||||
if (!isMobile.value) {
|
|
||||||
showFilters.value = false // Reset when switching to desktop
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
@@ -260,30 +74,11 @@ const goBack = () => {
|
|||||||
router.push('/reports/dashboard')
|
router.push('/reports/dashboard')
|
||||||
}
|
}
|
||||||
|
|
||||||
// US-305: Switch between Clienți/Furnizori tabs
|
// Handle period change from MaturityAnalysisCard
|
||||||
// Updates URL query param without full navigation, preserves filters
|
|
||||||
const switchTab = (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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle period change from MaturityAndDetailsCard
|
|
||||||
const handlePeriodChange = (period) => {
|
const handlePeriodChange = (period) => {
|
||||||
console.log('Maturity period changed:', period)
|
console.log('Maturity period changed:', period)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mobile navigation items
|
|
||||||
// US-307: Removed custom mobileNavItems - using MobileBottomNav defaults
|
|
||||||
|
|
||||||
// Lifecycle
|
// Lifecycle
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
window.addEventListener('resize', handleResize)
|
window.addEventListener('resize', handleResize)
|
||||||
@@ -301,63 +96,6 @@ onUnmounted(() => {
|
|||||||
padding-bottom: 56px;
|
padding-bottom: 56px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Mobile layout with tabs - extra padding for tab bar */
|
|
||||||
.main-content.mobile-layout.has-tabs {
|
|
||||||
padding-top: calc(56px + 48px); /* MobileTopBar + tabs height */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ================================================
|
|
||||||
US-305: 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* App container */
|
/* App container */
|
||||||
.app-container {
|
.app-container {
|
||||||
max-width: 1400px;
|
max-width: 1400px;
|
||||||
@@ -434,30 +172,4 @@ onUnmounted(() => {
|
|||||||
.empty-action {
|
.empty-action {
|
||||||
margin-top: var(--space-md);
|
margin-top: var(--space-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================
|
|
||||||
US-306: 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);
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user