feat(ui-fixes-phase6): Complete US-605 - Companie/Perioadă Colapsabile în Meniul Mobil
Implemented by Ralph autonomous loop. Iteration: 6 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -116,8 +116,8 @@
|
|||||||
"npm run typecheck passes",
|
"npm run typecheck passes",
|
||||||
"Verify in browser: Tap on Firma/Perioada toggles expansion"
|
"Verify in browser: Tap on Firma/Perioada toggles expansion"
|
||||||
],
|
],
|
||||||
"passes": false,
|
"passes": true,
|
||||||
"notes": "Modifică MobileDrawerMenu.vue, adaugă state pentru collapsed și persistență localStorage"
|
"notes": "Completed in iteration 6"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "US-606",
|
"id": "US-606",
|
||||||
|
|||||||
@@ -272,3 +272,9 @@ PRD: tasks/prd-ui-fixes-phase6.md
|
|||||||
[2026-01-13 16:17:06] Working on story: US-604
|
[2026-01-13 16:17:06] Working on story: US-604
|
||||||
[2026-01-13 16:17:06] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_5_US-604.log)
|
[2026-01-13 16:17:06] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_5_US-604.log)
|
||||||
[2026-01-13 16:18:21] SUCCESS: Story US-604 passed!
|
[2026-01-13 16:18:21] SUCCESS: Story US-604 passed!
|
||||||
|
[2026-01-13 16:18:22] Changes committed
|
||||||
|
[2026-01-13 16:18:22] Progress: 4/10 stories completed
|
||||||
|
[2026-01-13 16:18:24] === Iteration 6/30 ===
|
||||||
|
[2026-01-13 16:18:24] Working on story: US-605
|
||||||
|
[2026-01-13 16:18:24] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_6_US-605.log)
|
||||||
|
[2026-01-13 16:23:39] SUCCESS: Story US-605 passed!
|
||||||
|
|||||||
@@ -12,84 +12,111 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Company & Period Selection (below header, above navigation) -->
|
<!-- Company & Period Selection (below header, above navigation) -->
|
||||||
|
<!-- US-605: Collapsible sections with localStorage persistence -->
|
||||||
<div v-if="companiesStore" class="drawer-selectors">
|
<div v-if="companiesStore" class="drawer-selectors">
|
||||||
<!-- Company Selector -->
|
<!-- Company Selector (Collapsible) -->
|
||||||
<div class="selector-group">
|
<div class="selector-group" :class="{ 'is-collapsed': companyCollapsed }">
|
||||||
<label class="selector-label">Firma</label>
|
<!-- Collapsible Header -->
|
||||||
<button
|
<button
|
||||||
class="selector-trigger"
|
class="collapsible-header"
|
||||||
@click="toggleCompanyDropdown"
|
@click="toggleCompanyCollapsed"
|
||||||
:aria-expanded="companyDropdownOpen"
|
:aria-expanded="!companyCollapsed"
|
||||||
>
|
>
|
||||||
<div class="selector-value">
|
<span class="collapsible-label">Firma</span>
|
||||||
<span class="selector-main">{{ selectedCompanyName }}</span>
|
<span v-if="companyCollapsed" class="collapsible-value">{{ selectedCompanyName }}</span>
|
||||||
<span v-if="selectedCompanyCode" class="selector-sub">{{ selectedCompanyCode }}</span>
|
<i class="pi" :class="companyCollapsed ? 'pi-chevron-down' : 'pi-chevron-up'"></i>
|
||||||
</div>
|
|
||||||
<i class="pi pi-chevron-down" :class="{ 'rotate-180': companyDropdownOpen }"></i>
|
|
||||||
</button>
|
</button>
|
||||||
<!-- Company Dropdown Panel -->
|
|
||||||
<div v-if="companyDropdownOpen" class="selector-panel">
|
<!-- Expanded Content -->
|
||||||
<div class="selector-search">
|
<div v-if="!companyCollapsed" class="collapsible-content">
|
||||||
<i class="pi pi-search"></i>
|
<button
|
||||||
<input
|
class="selector-trigger"
|
||||||
ref="companySearchInput"
|
@click="toggleCompanyDropdown"
|
||||||
type="text"
|
:aria-expanded="companyDropdownOpen"
|
||||||
v-model="companySearchQuery"
|
>
|
||||||
placeholder="Caută firmă..."
|
<div class="selector-value">
|
||||||
class="selector-search-input"
|
<span class="selector-main">{{ selectedCompanyName }}</span>
|
||||||
/>
|
<span v-if="selectedCompanyCode" class="selector-sub">{{ selectedCompanyCode }}</span>
|
||||||
</div>
|
|
||||||
<div class="selector-list">
|
|
||||||
<div
|
|
||||||
v-for="company in filteredCompanies"
|
|
||||||
:key="company.id_firma"
|
|
||||||
class="selector-item"
|
|
||||||
:class="{ active: company.id_firma === companiesStore.selectedCompany?.id_firma }"
|
|
||||||
@click="selectCompany(company)"
|
|
||||||
>
|
|
||||||
<div class="selector-item-content">
|
|
||||||
<span class="selector-item-name">{{ company.name }}</span>
|
|
||||||
<span v-if="company.fiscal_code" class="selector-item-sub">CUI: {{ company.fiscal_code }}</span>
|
|
||||||
</div>
|
|
||||||
<i v-if="company.id_firma === companiesStore.selectedCompany?.id_firma" class="pi pi-check"></i>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-if="filteredCompanies.length === 0" class="selector-empty">
|
<i class="pi pi-chevron-down" :class="{ 'rotate-180': companyDropdownOpen }"></i>
|
||||||
<i class="pi pi-info-circle"></i>
|
</button>
|
||||||
<span>Nu s-au găsit firme</span>
|
<!-- Company Dropdown Panel -->
|
||||||
|
<div v-if="companyDropdownOpen" class="selector-panel">
|
||||||
|
<div class="selector-search">
|
||||||
|
<i class="pi pi-search"></i>
|
||||||
|
<input
|
||||||
|
ref="companySearchInput"
|
||||||
|
type="text"
|
||||||
|
v-model="companySearchQuery"
|
||||||
|
placeholder="Caută firmă..."
|
||||||
|
class="selector-search-input"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="selector-list">
|
||||||
|
<div
|
||||||
|
v-for="company in filteredCompanies"
|
||||||
|
:key="company.id_firma"
|
||||||
|
class="selector-item"
|
||||||
|
:class="{ active: company.id_firma === companiesStore.selectedCompany?.id_firma }"
|
||||||
|
@click="selectCompany(company)"
|
||||||
|
>
|
||||||
|
<div class="selector-item-content">
|
||||||
|
<span class="selector-item-name">{{ company.name }}</span>
|
||||||
|
<span v-if="company.fiscal_code" class="selector-item-sub">CUI: {{ company.fiscal_code }}</span>
|
||||||
|
</div>
|
||||||
|
<i v-if="company.id_firma === companiesStore.selectedCompany?.id_firma" class="pi pi-check"></i>
|
||||||
|
</div>
|
||||||
|
<div v-if="filteredCompanies.length === 0" class="selector-empty">
|
||||||
|
<i class="pi pi-info-circle"></i>
|
||||||
|
<span>Nu s-au găsit firme</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Period Selector -->
|
<!-- Period Selector (Collapsible) -->
|
||||||
<div v-if="periodStore && companiesStore.selectedCompany" class="selector-group">
|
<div v-if="periodStore && companiesStore.selectedCompany" class="selector-group" :class="{ 'is-collapsed': periodCollapsed }">
|
||||||
<label class="selector-label">Perioada</label>
|
<!-- Collapsible Header -->
|
||||||
<button
|
<button
|
||||||
class="selector-trigger"
|
class="collapsible-header"
|
||||||
@click="togglePeriodDropdown"
|
@click="togglePeriodCollapsed"
|
||||||
:aria-expanded="periodDropdownOpen"
|
:aria-expanded="!periodCollapsed"
|
||||||
>
|
>
|
||||||
<div class="selector-value">
|
<span class="collapsible-label">Perioada</span>
|
||||||
<span class="selector-main">{{ selectedPeriodDisplay }}</span>
|
<span v-if="periodCollapsed" class="collapsible-value">{{ selectedPeriodDisplay }}</span>
|
||||||
</div>
|
<i class="pi" :class="periodCollapsed ? 'pi-chevron-down' : 'pi-chevron-up'"></i>
|
||||||
<i class="pi pi-chevron-down" :class="{ 'rotate-180': periodDropdownOpen }"></i>
|
|
||||||
</button>
|
</button>
|
||||||
<!-- Period Dropdown Panel -->
|
|
||||||
<div v-if="periodDropdownOpen" class="selector-panel">
|
<!-- Expanded Content -->
|
||||||
<div class="selector-list">
|
<div v-if="!periodCollapsed" class="collapsible-content">
|
||||||
<div
|
<button
|
||||||
v-for="period in availablePeriods"
|
class="selector-trigger"
|
||||||
:key="`${period.an}-${period.luna}`"
|
@click="togglePeriodDropdown"
|
||||||
class="selector-item"
|
:aria-expanded="periodDropdownOpen"
|
||||||
:class="{ active: isPeriodSelected(period) }"
|
>
|
||||||
@click="selectPeriod(period)"
|
<div class="selector-value">
|
||||||
>
|
<span class="selector-main">{{ selectedPeriodDisplay }}</span>
|
||||||
<span class="selector-item-name">{{ period.display_name }}</span>
|
|
||||||
<i v-if="isPeriodSelected(period)" class="pi pi-check"></i>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-if="availablePeriods.length === 0" class="selector-empty">
|
<i class="pi pi-chevron-down" :class="{ 'rotate-180': periodDropdownOpen }"></i>
|
||||||
<i class="pi pi-info-circle"></i>
|
</button>
|
||||||
<span>Nu sunt perioade disponibile</span>
|
<!-- Period Dropdown Panel -->
|
||||||
|
<div v-if="periodDropdownOpen" class="selector-panel">
|
||||||
|
<div class="selector-list">
|
||||||
|
<div
|
||||||
|
v-for="period in availablePeriods"
|
||||||
|
:key="`${period.an}-${period.luna}`"
|
||||||
|
class="selector-item"
|
||||||
|
:class="{ active: isPeriodSelected(period) }"
|
||||||
|
@click="selectPeriod(period)"
|
||||||
|
>
|
||||||
|
<span class="selector-item-name">{{ period.display_name }}</span>
|
||||||
|
<i v-if="isPeriodSelected(period)" class="pi pi-check"></i>
|
||||||
|
</div>
|
||||||
|
<div v-if="availablePeriods.length === 0" class="selector-empty">
|
||||||
|
<i class="pi pi-info-circle"></i>
|
||||||
|
<span>Nu sunt perioade disponibile</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -231,6 +258,7 @@ import { useRoute, useRouter } from 'vue-router'
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* MobileDrawerMenu - Material Design 3 inspired navigation drawer for mobile (v3)
|
* MobileDrawerMenu - Material Design 3 inspired navigation drawer for mobile (v3)
|
||||||
|
* US-605: Added collapsible Firma/Perioada sections
|
||||||
*
|
*
|
||||||
* Props:
|
* Props:
|
||||||
* - modelValue (v-model): Controls visibility of the drawer
|
* - modelValue (v-model): Controls visibility of the drawer
|
||||||
@@ -318,6 +346,56 @@ const companySearchInput = ref(null)
|
|||||||
// Period selector state
|
// Period selector state
|
||||||
const periodDropdownOpen = ref(false)
|
const periodDropdownOpen = ref(false)
|
||||||
|
|
||||||
|
// Collapsible section state (US-605)
|
||||||
|
// Default to collapsed, persisted in localStorage
|
||||||
|
const COLLAPSED_STORAGE_KEY = 'mobile-drawer-sections-collapsed'
|
||||||
|
|
||||||
|
const loadCollapsedState = () => {
|
||||||
|
try {
|
||||||
|
const saved = localStorage.getItem(COLLAPSED_STORAGE_KEY)
|
||||||
|
if (saved) {
|
||||||
|
return JSON.parse(saved)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Ignore parse errors
|
||||||
|
}
|
||||||
|
// Default: both sections collapsed
|
||||||
|
return { company: true, period: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
const savedCollapsedState = loadCollapsedState()
|
||||||
|
const companyCollapsed = ref(savedCollapsedState.company)
|
||||||
|
const periodCollapsed = ref(savedCollapsedState.period)
|
||||||
|
|
||||||
|
const saveCollapsedState = () => {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(COLLAPSED_STORAGE_KEY, JSON.stringify({
|
||||||
|
company: companyCollapsed.value,
|
||||||
|
period: periodCollapsed.value
|
||||||
|
}))
|
||||||
|
} catch (e) {
|
||||||
|
// Ignore storage errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const toggleCompanyCollapsed = () => {
|
||||||
|
companyCollapsed.value = !companyCollapsed.value
|
||||||
|
// Close dropdown if collapsing
|
||||||
|
if (companyCollapsed.value) {
|
||||||
|
companyDropdownOpen.value = false
|
||||||
|
}
|
||||||
|
saveCollapsedState()
|
||||||
|
}
|
||||||
|
|
||||||
|
const togglePeriodCollapsed = () => {
|
||||||
|
periodCollapsed.value = !periodCollapsed.value
|
||||||
|
// Close dropdown if collapsing
|
||||||
|
if (periodCollapsed.value) {
|
||||||
|
periodDropdownOpen.value = false
|
||||||
|
}
|
||||||
|
saveCollapsedState()
|
||||||
|
}
|
||||||
|
|
||||||
// Computed properties for company selector
|
// Computed properties for company selector
|
||||||
const selectedCompanyName = computed(() => {
|
const selectedCompanyName = computed(() => {
|
||||||
return props.companiesStore?.selectedCompany?.name || 'Selectare firmă'
|
return props.companiesStore?.selectedCompany?.name || 'Selectare firmă'
|
||||||
@@ -780,6 +858,68 @@ onMounted(() => {
|
|||||||
font-size: var(--text-sm);
|
font-size: var(--text-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ================================================
|
||||||
|
Collapsible Sections (US-605)
|
||||||
|
================================================ */
|
||||||
|
|
||||||
|
.collapsible-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
padding: var(--space-sm) var(--space-md);
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
min-height: 44px;
|
||||||
|
gap: var(--space-sm);
|
||||||
|
transition: background var(--transition-fast);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsible-header:hover {
|
||||||
|
background: var(--surface-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsible-header:active {
|
||||||
|
background: var(--surface-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsible-label {
|
||||||
|
font-size: var(--text-xs);
|
||||||
|
font-weight: var(--font-medium);
|
||||||
|
color: var(--text-color-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsible-value {
|
||||||
|
flex: 1;
|
||||||
|
font-size: var(--text-sm);
|
||||||
|
font-weight: var(--font-medium);
|
||||||
|
color: var(--text-color);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsible-header .pi {
|
||||||
|
font-size: var(--text-xs);
|
||||||
|
color: var(--text-color-secondary);
|
||||||
|
flex-shrink: 0;
|
||||||
|
transition: transform var(--transition-fast);
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsible-content {
|
||||||
|
padding-top: var(--space-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* When collapsed, the selector-group has minimal padding */
|
||||||
|
.selector-group.is-collapsed {
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* ================================================
|
/* ================================================
|
||||||
Navigation Sections Container (scrollable)
|
Navigation Sections Container (scrollable)
|
||||||
================================================ */
|
================================================ */
|
||||||
@@ -1113,6 +1253,23 @@ onMounted(() => {
|
|||||||
color: var(--text-color-secondary);
|
color: var(--text-color-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Dark mode: Collapsible Sections (US-605) */
|
||||||
|
[data-theme="dark"] .collapsible-header:hover {
|
||||||
|
background: var(--surface-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .collapsible-label {
|
||||||
|
color: var(--text-color-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .collapsible-value {
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .collapsible-header .pi {
|
||||||
|
color: var(--text-color-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
[data-theme="dark"] .drawer-link {
|
[data-theme="dark"] .drawer-link {
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
@@ -1293,6 +1450,23 @@ onMounted(() => {
|
|||||||
color: var(--text-color-secondary);
|
color: var(--text-color-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Auto dark mode: Collapsible Sections (US-605) */
|
||||||
|
:root:not([data-theme]) .collapsible-header:hover {
|
||||||
|
background: var(--surface-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root:not([data-theme]) .collapsible-label {
|
||||||
|
color: var(--text-color-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root:not([data-theme]) .collapsible-value {
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root:not([data-theme]) .collapsible-header .pi {
|
||||||
|
color: var(--text-color-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
:root:not([data-theme]) .drawer-link {
|
:root:not([data-theme]) .drawer-link {
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user