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",
|
||||
"Verify in browser: Tap on Firma/Perioada toggles expansion"
|
||||
],
|
||||
"passes": false,
|
||||
"notes": "Modifică MobileDrawerMenu.vue, adaugă state pentru collapsed și persistență localStorage"
|
||||
"passes": true,
|
||||
"notes": "Completed in iteration 6"
|
||||
},
|
||||
{
|
||||
"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] 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: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>
|
||||
|
||||
<!-- Company & Period Selection (below header, above navigation) -->
|
||||
<!-- US-605: Collapsible sections with localStorage persistence -->
|
||||
<div v-if="companiesStore" class="drawer-selectors">
|
||||
<!-- Company Selector -->
|
||||
<div class="selector-group">
|
||||
<label class="selector-label">Firma</label>
|
||||
<!-- Company Selector (Collapsible) -->
|
||||
<div class="selector-group" :class="{ 'is-collapsed': companyCollapsed }">
|
||||
<!-- Collapsible Header -->
|
||||
<button
|
||||
class="selector-trigger"
|
||||
@click="toggleCompanyDropdown"
|
||||
:aria-expanded="companyDropdownOpen"
|
||||
class="collapsible-header"
|
||||
@click="toggleCompanyCollapsed"
|
||||
:aria-expanded="!companyCollapsed"
|
||||
>
|
||||
<div class="selector-value">
|
||||
<span class="selector-main">{{ selectedCompanyName }}</span>
|
||||
<span v-if="selectedCompanyCode" class="selector-sub">{{ selectedCompanyCode }}</span>
|
||||
</div>
|
||||
<i class="pi pi-chevron-down" :class="{ 'rotate-180': companyDropdownOpen }"></i>
|
||||
<span class="collapsible-label">Firma</span>
|
||||
<span v-if="companyCollapsed" class="collapsible-value">{{ selectedCompanyName }}</span>
|
||||
<i class="pi" :class="companyCollapsed ? 'pi-chevron-down' : 'pi-chevron-up'"></i>
|
||||
</button>
|
||||
<!-- 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>
|
||||
|
||||
<!-- Expanded Content -->
|
||||
<div v-if="!companyCollapsed" class="collapsible-content">
|
||||
<button
|
||||
class="selector-trigger"
|
||||
@click="toggleCompanyDropdown"
|
||||
:aria-expanded="companyDropdownOpen"
|
||||
>
|
||||
<div class="selector-value">
|
||||
<span class="selector-main">{{ selectedCompanyName }}</span>
|
||||
<span v-if="selectedCompanyCode" class="selector-sub">{{ selectedCompanyCode }}</span>
|
||||
</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>
|
||||
<i class="pi pi-chevron-down" :class="{ 'rotate-180': companyDropdownOpen }"></i>
|
||||
</button>
|
||||
<!-- 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>
|
||||
|
||||
<!-- Period Selector -->
|
||||
<div v-if="periodStore && companiesStore.selectedCompany" class="selector-group">
|
||||
<label class="selector-label">Perioada</label>
|
||||
<!-- Period Selector (Collapsible) -->
|
||||
<div v-if="periodStore && companiesStore.selectedCompany" class="selector-group" :class="{ 'is-collapsed': periodCollapsed }">
|
||||
<!-- Collapsible Header -->
|
||||
<button
|
||||
class="selector-trigger"
|
||||
@click="togglePeriodDropdown"
|
||||
:aria-expanded="periodDropdownOpen"
|
||||
class="collapsible-header"
|
||||
@click="togglePeriodCollapsed"
|
||||
:aria-expanded="!periodCollapsed"
|
||||
>
|
||||
<div class="selector-value">
|
||||
<span class="selector-main">{{ selectedPeriodDisplay }}</span>
|
||||
</div>
|
||||
<i class="pi pi-chevron-down" :class="{ 'rotate-180': periodDropdownOpen }"></i>
|
||||
<span class="collapsible-label">Perioada</span>
|
||||
<span v-if="periodCollapsed" class="collapsible-value">{{ selectedPeriodDisplay }}</span>
|
||||
<i class="pi" :class="periodCollapsed ? 'pi-chevron-down' : 'pi-chevron-up'"></i>
|
||||
</button>
|
||||
<!-- 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>
|
||||
|
||||
<!-- Expanded Content -->
|
||||
<div v-if="!periodCollapsed" class="collapsible-content">
|
||||
<button
|
||||
class="selector-trigger"
|
||||
@click="togglePeriodDropdown"
|
||||
:aria-expanded="periodDropdownOpen"
|
||||
>
|
||||
<div class="selector-value">
|
||||
<span class="selector-main">{{ selectedPeriodDisplay }}</span>
|
||||
</div>
|
||||
<div v-if="availablePeriods.length === 0" class="selector-empty">
|
||||
<i class="pi pi-info-circle"></i>
|
||||
<span>Nu sunt perioade disponibile</span>
|
||||
<i class="pi pi-chevron-down" :class="{ 'rotate-180': periodDropdownOpen }"></i>
|
||||
</button>
|
||||
<!-- 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>
|
||||
@@ -231,6 +258,7 @@ import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
/**
|
||||
* MobileDrawerMenu - Material Design 3 inspired navigation drawer for mobile (v3)
|
||||
* US-605: Added collapsible Firma/Perioada sections
|
||||
*
|
||||
* Props:
|
||||
* - modelValue (v-model): Controls visibility of the drawer
|
||||
@@ -318,6 +346,56 @@ const companySearchInput = ref(null)
|
||||
// Period selector state
|
||||
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
|
||||
const selectedCompanyName = computed(() => {
|
||||
return props.companiesStore?.selectedCompany?.name || 'Selectare firmă'
|
||||
@@ -780,6 +858,68 @@ onMounted(() => {
|
||||
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)
|
||||
================================================ */
|
||||
@@ -1113,6 +1253,23 @@ onMounted(() => {
|
||||
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 {
|
||||
color: var(--text-color);
|
||||
}
|
||||
@@ -1293,6 +1450,23 @@ onMounted(() => {
|
||||
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 {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user