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:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user