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:
@@ -96,8 +96,8 @@
|
|||||||
"MobileBottomNav activ",
|
"MobileBottomNav activ",
|
||||||
"npm run build passes"
|
"npm run build passes"
|
||||||
],
|
],
|
||||||
"passes": false,
|
"passes": true,
|
||||||
"notes": ""
|
"notes": "Completed in iteration 2"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "US-301",
|
"id": "US-301",
|
||||||
|
|||||||
@@ -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] 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: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: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!
|
||||||
|
|||||||
@@ -45,9 +45,10 @@
|
|||||||
<!-- Main Content -->
|
<!-- Main Content -->
|
||||||
<div v-else class="card-content">
|
<div v-else class="card-content">
|
||||||
<!-- Nivel 1: Comparație Grafică -->
|
<!-- Nivel 1: Comparație Grafică -->
|
||||||
<div class="comparison-section">
|
<!-- US-305: On mobile, show only the active tab's content -->
|
||||||
<!-- Clienți Side -->
|
<div class="comparison-section" :class="{ 'mobile-single-view': props.isMobile }">
|
||||||
<div class="comparison-side clients">
|
<!-- 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">
|
<div class="side-header">
|
||||||
<span class="side-label">Clienți - De încasat</span>
|
<span class="side-label">Clienți - De încasat</span>
|
||||||
<span class="side-total">{{ formatCurrency(clientsTotal) }}</span>
|
<span class="side-total">{{ formatCurrency(clientsTotal) }}</span>
|
||||||
@@ -86,11 +87,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Divider -->
|
<!-- Divider - Hidden on mobile -->
|
||||||
<div class="comparison-divider"></div>
|
<div v-if="!props.isMobile" class="comparison-divider"></div>
|
||||||
|
|
||||||
<!-- Furnizori Side -->
|
<!-- Furnizori Side - Hidden on mobile when clients tab active -->
|
||||||
<div class="comparison-side suppliers">
|
<div class="comparison-side suppliers" v-if="!props.isMobile || props.activeTab === 'suppliers'">
|
||||||
<div class="side-header">
|
<div class="side-header">
|
||||||
<span class="side-label">Furnizori - De plătit</span>
|
<span class="side-label">Furnizori - De plătit</span>
|
||||||
<span class="side-total">{{ formatCurrency(suppliersTotal) }}</span>
|
<span class="side-total">{{ formatCurrency(suppliersTotal) }}</span>
|
||||||
@@ -512,6 +513,17 @@ const props = defineProps({
|
|||||||
type: [Number, String],
|
type: [Number, String],
|
||||||
required: true,
|
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
|
// Emits
|
||||||
@@ -1689,14 +1701,24 @@ onMounted(() => {
|
|||||||
display: table;
|
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 */
|
/* Responsive - Tablet */
|
||||||
@media (max-width: 1024px) {
|
@media (max-width: 1024px) {
|
||||||
.comparison-section {
|
.comparison-section:not(.mobile-single-view) {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
gap: 1.5rem;
|
gap: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.comparison-divider {
|
.comparison-section:not(.mobile-single-view) .comparison-divider {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,32 @@
|
|||||||
<!-- Mobile Top Bar -->
|
<!-- Mobile Top Bar -->
|
||||||
<MobileTopBar
|
<MobileTopBar
|
||||||
v-if="isMobile"
|
v-if="isMobile"
|
||||||
title="Analiză Scadențe"
|
title="Scadențe"
|
||||||
:show-back="true"
|
:show-back="true"
|
||||||
@back-click="goBack"
|
@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">
|
<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">
|
||||||
@@ -34,6 +54,8 @@
|
|||||||
<div v-else class="maturity-container">
|
<div v-else class="maturity-container">
|
||||||
<MaturityAndDetailsCard
|
<MaturityAndDetailsCard
|
||||||
:companyId="companyStore.selectedCompany?.id_firma"
|
:companyId="companyStore.selectedCompany?.id_firma"
|
||||||
|
:activeTab="activeTab"
|
||||||
|
:isMobile="isMobile"
|
||||||
@periodChanged="handlePeriodChange"
|
@periodChanged="handlePeriodChange"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -46,7 +68,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
import Button from 'primevue/button'
|
import Button from 'primevue/button'
|
||||||
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'
|
||||||
@@ -54,8 +76,12 @@ import MaturityAndDetailsCard from '@reports/components/dashboard/cards/Maturity
|
|||||||
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)
|
||||||
@@ -70,6 +96,22 @@ const goBack = () => {
|
|||||||
router.push('/reports/dashboard')
|
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
|
// Handle period change from MaturityAndDetailsCard
|
||||||
const handlePeriodChange = (period) => {
|
const handlePeriodChange = (period) => {
|
||||||
console.log('Maturity period changed:', period)
|
console.log('Maturity period changed:', period)
|
||||||
@@ -95,6 +137,63 @@ 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;
|
||||||
|
|||||||
Reference in New Issue
Block a user