feat(mobile-fixes-phase3): Complete US-303 - FAB pe Pagina Bonuri (Mutare Upload)

Implemented by Ralph autonomous loop.
Iteration: 5

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-01-12 16:46:26 +00:00
parent f272d55ba4
commit 3afd3cd0ec
3 changed files with 124 additions and 6 deletions

View File

@@ -144,8 +144,8 @@
"FAB vizibil doar pe mobil (isMobile)", "FAB vizibil doar pe mobil (isMobile)",
"npm run build passes" "npm run build passes"
], ],
"passes": false, "passes": true,
"notes": "" "notes": "Completed in iteration 5"
}, },
{ {
"id": "US-306", "id": "US-306",

View File

@@ -1044,3 +1044,9 @@ User Stories: 11 (US-301 to US-311)
[2026-01-12 16:40:03] Working on story: US-302 [2026-01-12 16:40:03] Working on story: US-302
[2026-01-12 16:40:03] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_4_US-302.log) [2026-01-12 16:40:03] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_4_US-302.log)
[2026-01-12 16:42:59] SUCCESS: Story US-302 passed! [2026-01-12 16:42:59] SUCCESS: Story US-302 passed!
[2026-01-12 16:42:59] Changes committed
[2026-01-12 16:42:59] Progress: 6/11 stories completed
[2026-01-12 16:43:01] === Iteration 5/100 ===
[2026-01-12 16:43:01] Working on story: US-303
[2026-01-12 16:43:01] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_5_US-303.log)
[2026-01-12 16:46:26] SUCCESS: Story US-303 passed!

View File

@@ -545,18 +545,30 @@
/> />
</div> </div>
<!-- US-040: Mobile FAB (Floating Action Button) --> <!-- US-303: Mobile FAB (Floating Action Button) with Popup Menu -->
<Transition name="fab-slide"> <Transition name="fab-slide">
<button <button
v-if="isMobile && !mobileSelectionMode && fabVisible" v-if="isMobile && !mobileSelectionMode && fabVisible"
class="mobile-fab" class="mobile-fab"
@click="goToCreate" :class="{ 'fab-active': fabMenuOpen }"
aria-label="Adaugă bon nou" @click="toggleFabMenu"
aria-label="Meniu acțiuni"
aria-haspopup="true"
:aria-expanded="fabMenuOpen"
> >
<i class="pi pi-plus"></i> <i class="pi pi-plus" :class="{ 'fab-icon-rotate': fabMenuOpen }"></i>
</button> </button>
</Transition> </Transition>
<!-- US-303: FAB Popup Menu -->
<Menu
ref="fabMenuRef"
:model="fabMenuItems"
:popup="true"
class="fab-popup-menu"
@hide="fabMenuOpen = false"
/>
<!-- US-103: Mobile Bottom Navigation (using shared component) --> <!-- US-103: Mobile Bottom Navigation (using shared component) -->
<!-- US-307: Using default nav items (Dashboard, Bonuri, Facturi, Setări) --> <!-- US-307: Using default nav items (Dashboard, Bonuri, Facturi, Setări) -->
<MobileBottomNav v-if="isMobile && !mobileSelectionMode" /> <MobileBottomNav v-if="isMobile && !mobileSelectionMode" />
@@ -1038,6 +1050,10 @@ const handleResize = () => {
const showDrawer = ref(false) const showDrawer = ref(false)
const moreMenuRef = ref(null) const moreMenuRef = ref(null)
const fabVisible = ref(true) const fabVisible = ref(true)
// US-303: FAB Menu State
const fabMenuRef = ref(null)
const fabMenuOpen = ref(false)
let lastScrollY = 0 let lastScrollY = 0
let scrollTimeout = null let scrollTimeout = null
@@ -1075,6 +1091,32 @@ const moreMenuItems = computed(() => [
} }
]) ])
// US-303: FAB Menu Items (Bon Nou | Upload Bulk)
const fabMenuItems = [
{
label: 'Bon Nou',
icon: 'pi pi-plus',
command: () => {
fabMenuOpen.value = false
goToCreate()
}
},
{
label: 'Upload Bulk',
icon: 'pi pi-cloud-upload',
command: () => {
fabMenuOpen.value = false
openBulkFileInput()
}
}
]
// US-303: Toggle FAB Menu
const toggleFabMenu = (event) => {
fabMenuOpen.value = !fabMenuOpen.value
fabMenuRef.value?.toggle(event)
}
// US-103: Top bar actions for MobileTopBar component // US-103: Top bar actions for MobileTopBar component
const mobileTopBarActions = computed(() => { const mobileTopBarActions = computed(() => {
if (mobileSelectionMode.value) { if (mobileSelectionMode.value) {
@@ -4398,6 +4440,50 @@ const cleanupCompletedBatches = (storedBatchIds) => {
.mobile-fab i { .mobile-fab i {
font-size: var(--text-2xl); font-size: var(--text-2xl);
transition: transform var(--transition-fast);
}
/* US-303: FAB active state when menu is open */
.mobile-fab.fab-active {
background: var(--color-primary-dark);
}
.mobile-fab .fab-icon-rotate {
transform: rotate(45deg);
}
/* US-303: FAB Popup Menu positioning */
.fab-popup-menu {
/* Position above FAB, aligned to right */
position: fixed !important;
bottom: 140px !important; /* 72px FAB position + 56px FAB height + 12px spacing */
right: var(--space-md) !important;
left: auto !important;
top: auto !important;
min-width: 180px;
z-index: 1000 !important;
border-radius: var(--radius-lg);
box-shadow: var(--shadow-xl);
}
.fab-popup-menu .p-menuitem {
padding: 0;
}
.fab-popup-menu .p-menuitem-link {
padding: var(--space-md) var(--space-lg);
gap: var(--space-sm);
font-size: var(--text-base);
min-height: 48px; /* Touch-friendly */
}
.fab-popup-menu .p-menuitem-icon {
font-size: var(--text-lg);
color: var(--color-primary);
}
.fab-popup-menu .p-menuitem-text {
font-weight: var(--font-medium);
} }
/* FAB slide animation */ /* FAB slide animation */
@@ -4523,6 +4609,19 @@ const cleanupCompletedBatches = (storedBatchIds) => {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
} }
[data-theme="dark"] .mobile-fab.fab-active {
background: var(--blue-700);
}
[data-theme="dark"] .fab-popup-menu {
background: var(--surface-card);
border: 1px solid var(--surface-border);
}
[data-theme="dark"] .fab-popup-menu .p-menuitem-icon {
color: var(--blue-400);
}
[data-theme="dark"] .sidebar-item.active, [data-theme="dark"] .sidebar-item.active,
[data-theme="dark"] .sidebar-item.router-link-active { [data-theme="dark"] .sidebar-item.router-link-active {
color: var(--blue-400); color: var(--blue-400);
@@ -4568,6 +4667,19 @@ const cleanupCompletedBatches = (storedBatchIds) => {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
} }
:root:not([data-theme]) .mobile-fab.fab-active {
background: var(--blue-700);
}
:root:not([data-theme]) .fab-popup-menu {
background: var(--surface-card);
border: 1px solid var(--surface-border);
}
:root:not([data-theme]) .fab-popup-menu .p-menuitem-icon {
color: var(--blue-400);
}
:root:not([data-theme]) .sidebar-item.active, :root:not([data-theme]) .sidebar-item.active,
:root:not([data-theme]) .sidebar-item.router-link-active { :root:not([data-theme]) .sidebar-item.router-link-active {
color: var(--blue-400); color: var(--blue-400);