feat(unified-mobile-material-design): Complete US-105 - Adăugare buton Înapoi în editare bon

Implemented by Ralph autonomous loop.
Iteration: 8

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-01-12 10:03:01 +00:00
parent 45eedde9be
commit 8448f4c37e
3 changed files with 101 additions and 166 deletions

View File

@@ -136,8 +136,8 @@
"Verify in browser că butonul funcționează",
"npm run build passes"
],
"passes": false,
"notes": ""
"passes": true,
"notes": "Completed in iteration 8"
},
{
"id": "US-112",

View File

@@ -48,3 +48,9 @@ Mon Jan 12 09:44:54 AM UTC 2026
[2026-01-12 09:58:25] Working on story: US-104
[2026-01-12 09:58:25] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_7_US-104.log)
[2026-01-12 10:00:17] SUCCESS: Story US-104 passed!
[2026-01-12 10:00:18] Changes committed
[2026-01-12 10:00:18] Progress: 6/20 stories completed
[2026-01-12 10:00:20] === Iteration 8/100 ===
[2026-01-12 10:00:20] Working on story: US-105
[2026-01-12 10:00:20] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_8_US-105.log)
[2026-01-12 10:03:01] SUCCESS: Story US-105 passed!

View File

@@ -1,77 +1,23 @@
<template>
<div class="receipt-unified-view" :class="{ 'mobile-compose-layout': isMobile }">
<!-- US-041/US-042: Mobile Top Bar - Gmail Style -->
<header v-if="isMobile" class="mobile-compose-top-bar">
<div class="top-bar-left">
<Button
:icon="isViewMode ? 'pi pi-arrow-left' : 'pi pi-times'"
text
rounded
class="top-bar-btn"
@click="goBack"
aria-label="Înapoi"
/>
<span class="top-bar-title">{{ mobileTitle }}</span>
</div>
<div class="top-bar-right">
<!-- Create/Edit mode icons -->
<Button
v-if="!isViewMode"
icon="pi pi-paperclip"
text
rounded
class="top-bar-btn"
@click="triggerFileAttach"
aria-label="Atașează fișier"
/>
<Button
v-if="!isViewMode"
icon="pi pi-save"
text
rounded
class="top-bar-btn"
:loading="saving"
@click="saveReceipt"
aria-label="Salvează"
/>
<!-- US-042: View mode icons - edit, delete, more menu -->
<Button
v-if="isViewMode && (receipt?.status === 'draft' || receipt?.status === 'rejected')"
icon="pi pi-pencil"
text
rounded
class="top-bar-btn"
@click="goToEdit"
aria-label="Editează"
/>
<Button
v-if="isViewMode && canDelete"
icon="pi pi-trash"
text
rounded
class="top-bar-btn top-bar-btn-danger"
@click="confirmDelete"
aria-label="Șterge"
/>
<Button
v-if="isViewMode"
icon="pi pi-ellipsis-v"
text
rounded
class="top-bar-btn"
@click="toggleMoreMenu"
aria-label="Mai multe opțiuni"
aria-haspopup="true"
aria-controls="more_menu"
/>
<Menu
ref="moreMenuRef"
id="more_menu"
:model="moreMenuItems"
:popup="true"
/>
</div>
</header>
<!-- US-105: Mobile Top Bar using shared MobileTopBar component -->
<MobileTopBar
v-if="isMobile"
:title="mobileTitle"
:show-back="true"
:actions="mobileTopBarActions"
@back-click="goBack"
@action-click="handleTopBarAction"
/>
<!-- US-042: More Menu for view mode (used by MobileTopBar action) -->
<Menu
v-if="isMobile"
ref="moreMenuRef"
id="more_menu"
:model="moreMenuItems"
:popup="true"
/>
<!-- Desktop Header -->
<div v-if="!isMobile" class="view-header">
@@ -358,6 +304,7 @@ import { useReceiptsStore } from '@data-entry/stores/receiptsStore'
import { useCompanyStore } from '@data-entry/stores/sharedStores'
import UnifiedReceiptForm from '@data-entry/components/receipts/UnifiedReceiptForm.vue'
import MobileTopBar from '@shared/components/mobile/MobileTopBar.vue'
import {
getDefaultUnifiedFormState,
legacyToUnifiedForm,
@@ -468,6 +415,75 @@ const moreMenuItems = computed(() => {
return items
})
// US-105: Mobile top bar actions - contextual based on mode
const mobileTopBarActions = computed(() => {
const actions = []
if (!isViewMode.value) {
// Create/Edit mode: attach file + save
actions.push({
id: 'attach',
icon: 'pi pi-paperclip',
label: 'Atașează fișier',
tooltip: 'Atașează fișier'
})
actions.push({
id: 'save',
icon: 'pi pi-save',
label: 'Salvează',
tooltip: 'Salvează'
})
} else {
// View mode: edit (if allowed), delete (if allowed), more menu
if (receipt.value?.status === 'draft' || receipt.value?.status === 'rejected') {
actions.push({
id: 'edit',
icon: 'pi pi-pencil',
label: 'Editează',
tooltip: 'Editează'
})
}
if (canDelete.value) {
actions.push({
id: 'delete',
icon: 'pi pi-trash',
label: 'Șterge',
tooltip: 'Șterge'
})
}
actions.push({
id: 'more',
icon: 'pi pi-ellipsis-v',
label: 'Mai multe',
tooltip: 'Mai multe opțiuni'
})
}
return actions
})
// US-105: Handle top bar action clicks
const handleTopBarAction = (action) => {
switch (action.id) {
case 'attach':
triggerFileAttach()
break
case 'save':
saveReceipt()
break
case 'edit':
goToEdit()
break
case 'delete':
confirmDelete()
break
case 'more':
// Toggle more menu - need to pass event
moreMenuRef.value?.toggle({ currentTarget: document.querySelector('[aria-label="Mai multe opțiuni"]') })
break
}
}
// Form state
const form = ref(getDefaultUnifiedFormState())
const receipt = ref(null)
@@ -1339,8 +1355,8 @@ const cancelApproval = async () => {
}
/* ========================================
* US-041: Mobile Gmail Compose Layout
* Similar to Gmail's email compose interface
* US-041/US-105: Mobile Layout
* Uses shared MobileTopBar component
* PRD Mobile Layout Tokens:
* - topBarHeight: 56px
* - bottomNavHeight: 56px
@@ -1350,7 +1366,7 @@ const cancelApproval = async () => {
/* Mobile compose layout container */
.receipt-unified-view.mobile-compose-layout {
padding: 0;
padding-top: 56px; /* Space for fixed top bar */
padding-top: 56px; /* Space for fixed MobileTopBar */
padding-bottom: 80px; /* Space for fixed bottom action bar */
max-width: 100%;
min-height: 100vh;
@@ -1362,60 +1378,6 @@ const cancelApproval = async () => {
display: none;
}
/* Mobile Top Bar - Gmail Compose Style */
.mobile-compose-top-bar {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 56px;
background: var(--surface-card);
border-bottom: 1px solid var(--surface-border);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 var(--space-xs);
z-index: 1000;
box-shadow: var(--shadow-sm);
}
.mobile-compose-top-bar .top-bar-left,
.mobile-compose-top-bar .top-bar-right {
display: flex;
align-items: center;
gap: var(--space-xs);
}
.mobile-compose-top-bar .top-bar-title {
font-size: var(--text-lg);
font-weight: var(--font-semibold);
color: var(--text-color);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 180px;
}
.mobile-compose-top-bar .top-bar-btn {
width: 48px;
height: 48px;
min-width: 48px; /* Touch target minimum */
display: flex;
align-items: center;
justify-content: center;
color: var(--text-color);
transition: all var(--transition-fast);
}
.mobile-compose-top-bar .top-bar-btn:active {
background: var(--surface-hover);
border-radius: var(--radius-full);
}
.mobile-compose-top-bar .top-bar-btn-danger {
color: var(--red-500);
}
/* Mobile Bottom Action Bar - Gmail Compose Style */
.mobile-form-bottom-bar {
position: fixed;
@@ -1486,25 +1448,9 @@ const cancelApproval = async () => {
}
/* ========================================
* Dark Mode Support for Mobile Layout
* Dark Mode Support for Mobile Bottom Bar
* (MobileTopBar handles its own dark mode)
* ======================================== */
[data-theme="dark"] .mobile-compose-top-bar {
background: var(--surface-card);
border-bottom-color: var(--surface-border);
}
[data-theme="dark"] .mobile-compose-top-bar .top-bar-btn {
color: var(--text-color);
}
[data-theme="dark"] .mobile-compose-top-bar .top-bar-btn:active {
background: var(--surface-hover);
}
[data-theme="dark"] .mobile-compose-top-bar .top-bar-btn-danger {
color: var(--red-400);
}
[data-theme="dark"] .mobile-form-bottom-bar {
background: var(--surface-card);
border-top-color: var(--surface-border);
@@ -1515,23 +1461,6 @@ const cancelApproval = async () => {
* System Preference Dark Mode Support
* ======================================== */
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) .mobile-compose-top-bar {
background: var(--surface-card);
border-bottom-color: var(--surface-border);
}
:root:not([data-theme="light"]) .mobile-compose-top-bar .top-bar-btn {
color: var(--text-color);
}
:root:not([data-theme="light"]) .mobile-compose-top-bar .top-bar-btn:active {
background: var(--surface-hover);
}
:root:not([data-theme="light"]) .mobile-compose-top-bar .top-bar-btn-danger {
color: var(--red-400);
}
:root:not([data-theme="light"]) .mobile-form-bottom-bar {
background: var(--surface-card);
border-top-color: var(--surface-border);