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ă", "Verify in browser că butonul funcționează",
"npm run build passes" "npm run build passes"
], ],
"passes": false, "passes": true,
"notes": "" "notes": "Completed in iteration 8"
}, },
{ {
"id": "US-112", "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] 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 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: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> <template>
<div class="receipt-unified-view" :class="{ 'mobile-compose-layout': isMobile }"> <div class="receipt-unified-view" :class="{ 'mobile-compose-layout': isMobile }">
<!-- US-041/US-042: Mobile Top Bar - Gmail Style --> <!-- US-105: Mobile Top Bar using shared MobileTopBar component -->
<header v-if="isMobile" class="mobile-compose-top-bar"> <MobileTopBar
<div class="top-bar-left"> v-if="isMobile"
<Button :title="mobileTitle"
:icon="isViewMode ? 'pi pi-arrow-left' : 'pi pi-times'" :show-back="true"
text :actions="mobileTopBarActions"
rounded @back-click="goBack"
class="top-bar-btn" @action-click="handleTopBarAction"
@click="goBack" />
aria-label="Înapoi"
/> <!-- US-042: More Menu for view mode (used by MobileTopBar action) -->
<span class="top-bar-title">{{ mobileTitle }}</span> <Menu
</div> v-if="isMobile"
<div class="top-bar-right"> ref="moreMenuRef"
<!-- Create/Edit mode icons --> id="more_menu"
<Button :model="moreMenuItems"
v-if="!isViewMode" :popup="true"
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>
<!-- Desktop Header --> <!-- Desktop Header -->
<div v-if="!isMobile" class="view-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 { useCompanyStore } from '@data-entry/stores/sharedStores'
import UnifiedReceiptForm from '@data-entry/components/receipts/UnifiedReceiptForm.vue' import UnifiedReceiptForm from '@data-entry/components/receipts/UnifiedReceiptForm.vue'
import MobileTopBar from '@shared/components/mobile/MobileTopBar.vue'
import { import {
getDefaultUnifiedFormState, getDefaultUnifiedFormState,
legacyToUnifiedForm, legacyToUnifiedForm,
@@ -468,6 +415,75 @@ const moreMenuItems = computed(() => {
return items 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 // Form state
const form = ref(getDefaultUnifiedFormState()) const form = ref(getDefaultUnifiedFormState())
const receipt = ref(null) const receipt = ref(null)
@@ -1339,8 +1355,8 @@ const cancelApproval = async () => {
} }
/* ======================================== /* ========================================
* US-041: Mobile Gmail Compose Layout * US-041/US-105: Mobile Layout
* Similar to Gmail's email compose interface * Uses shared MobileTopBar component
* PRD Mobile Layout Tokens: * PRD Mobile Layout Tokens:
* - topBarHeight: 56px * - topBarHeight: 56px
* - bottomNavHeight: 56px * - bottomNavHeight: 56px
@@ -1350,7 +1366,7 @@ const cancelApproval = async () => {
/* Mobile compose layout container */ /* Mobile compose layout container */
.receipt-unified-view.mobile-compose-layout { .receipt-unified-view.mobile-compose-layout {
padding: 0; 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 */ padding-bottom: 80px; /* Space for fixed bottom action bar */
max-width: 100%; max-width: 100%;
min-height: 100vh; min-height: 100vh;
@@ -1362,60 +1378,6 @@ const cancelApproval = async () => {
display: none; 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 Bottom Action Bar - Gmail Compose Style */
.mobile-form-bottom-bar { .mobile-form-bottom-bar {
position: fixed; 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 { [data-theme="dark"] .mobile-form-bottom-bar {
background: var(--surface-card); background: var(--surface-card);
border-top-color: var(--surface-border); border-top-color: var(--surface-border);
@@ -1515,23 +1461,6 @@ const cancelApproval = async () => {
* System Preference Dark Mode Support * System Preference Dark Mode Support
* ======================================== */ * ======================================== */
@media (prefers-color-scheme: dark) { @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 { :root:not([data-theme="light"]) .mobile-form-bottom-bar {
background: var(--surface-card); background: var(--surface-card);
border-top-color: var(--surface-border); border-top-color: var(--surface-border);