feat(mobile-fixes-phase3): Complete US-305 - Tab-uri Clienți/Furnizori în Scadențe

Implemented by Ralph autonomous loop.
Iteration: 2

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-01-12 16:39:00 +00:00
parent edcdce58e2
commit 12e11fa44d
4 changed files with 141 additions and 14 deletions

View File

@@ -96,8 +96,8 @@
"MobileBottomNav activ",
"npm run build passes"
],
"passes": false,
"notes": ""
"passes": true,
"notes": "Completed in iteration 2"
},
{
"id": "US-301",

View File

@@ -1026,3 +1026,9 @@ User Stories: 11 (US-301 to US-311)
[2026-01-12 16:34:08] Working on story: US-304
[2026-01-12 16:34:08] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_1_US-304.log)
[2026-01-12 16:36:25] SUCCESS: Story US-304 passed!
[2026-01-12 16:36:26] Changes committed
[2026-01-12 16:36:26] Progress: 3/11 stories completed
[2026-01-12 16:36:28] === Iteration 2/100 ===
[2026-01-12 16:36:28] Working on story: US-305
[2026-01-12 16:36:28] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_2_US-305.log)
[2026-01-12 16:39:00] SUCCESS: Story US-305 passed!

View File

@@ -45,9 +45,10 @@
<!-- Main Content -->
<div v-else class="card-content">
<!-- Nivel 1: Comparație Grafică -->
<div class="comparison-section">
<!-- Clienți Side -->
<div class="comparison-side clients">
<!-- US-305: On mobile, show only the active tab's content -->
<div class="comparison-section" :class="{ 'mobile-single-view': props.isMobile }">
<!-- Clienți Side - Hidden on mobile when suppliers tab active -->
<div class="comparison-side clients" v-if="!props.isMobile || props.activeTab === 'clients'">
<div class="side-header">
<span class="side-label">Clienți - De încasat</span>
<span class="side-total">{{ formatCurrency(clientsTotal) }}</span>
@@ -86,11 +87,11 @@
</div>
</div>
<!-- Divider -->
<div class="comparison-divider"></div>
<!-- Divider - Hidden on mobile -->
<div v-if="!props.isMobile" class="comparison-divider"></div>
<!-- Furnizori Side -->
<div class="comparison-side suppliers">
<!-- Furnizori Side - Hidden on mobile when clients tab active -->
<div class="comparison-side suppliers" v-if="!props.isMobile || props.activeTab === 'suppliers'">
<div class="side-header">
<span class="side-label">Furnizori - De plătit</span>
<span class="side-total">{{ formatCurrency(suppliersTotal) }}</span>
@@ -512,6 +513,17 @@ const props = defineProps({
type: [Number, String],
required: true,
},
// US-305: Active tab for mobile view (clients/suppliers)
activeTab: {
type: String,
default: 'clients',
validator: (value) => ['clients', 'suppliers'].includes(value),
},
// US-305: Is mobile view
isMobile: {
type: Boolean,
default: false,
},
});
// Emits
@@ -1689,14 +1701,24 @@ onMounted(() => {
display: table;
}
/* US-305: Mobile single view - show only active tab */
.comparison-section.mobile-single-view {
grid-template-columns: 1fr;
gap: 0;
}
.comparison-section.mobile-single-view .comparison-side {
width: 100%;
}
/* Responsive - Tablet */
@media (max-width: 1024px) {
.comparison-section {
.comparison-section:not(.mobile-single-view) {
grid-template-columns: 1fr;
gap: 1.5rem;
}
.comparison-divider {
.comparison-section:not(.mobile-single-view) .comparison-divider {
display: none;
}
}

View File

@@ -2,12 +2,32 @@
<!-- Mobile Top Bar -->
<MobileTopBar
v-if="isMobile"
title="Analiză Scadențe"
title="Scadențe"
:show-back="true"
@back-click="goBack"
/>
<main class="main-content" :class="{ 'mobile-layout': isMobile }">
<!-- 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">
<!-- Page Header - only on desktop -->
<div v-if="!isMobile" class="page-header">
@@ -34,6 +54,8 @@
<div v-else class="maturity-container">
<MaturityAndDetailsCard
:companyId="companyStore.selectedCompany?.id_firma"
:activeTab="activeTab"
:isMobile="isMobile"
@periodChanged="handlePeriodChange"
/>
</div>
@@ -46,7 +68,7 @@
<script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue'
import { useRouter } from 'vue-router'
import { useRouter, useRoute } from 'vue-router'
import Button from 'primevue/button'
import MobileTopBar from '@shared/components/mobile/MobileTopBar.vue'
import MobileBottomNav from '@shared/components/mobile/MobileBottomNav.vue'
@@ -54,8 +76,12 @@ import MaturityAndDetailsCard from '@reports/components/dashboard/cards/Maturity
import { useCompanyStore } from '@reports/stores/sharedStores'
const router = useRouter()
const route = useRoute()
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
const windowWidth = ref(window.innerWidth)
const isMobile = computed(() => windowWidth.value < 768)
@@ -70,6 +96,22 @@ const goBack = () => {
router.push('/reports/dashboard')
}
// US-305: Switch between Clienți/Furnizori tabs
// 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) => {
console.log('Maturity period changed:', period)
@@ -95,6 +137,63 @@ onUnmounted(() => {
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 {
max-width: 1400px;