feat(unified-mobile-desktop-ui): Complete US-518 - Creare/Vizualizare/Editare Bon - Butoane Doar Sus

Implemented by Ralph autonomous loop.
Iteration: 20

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-01-12 23:39:17 +00:00
parent 817e2c7529
commit 8e74b3291f
3 changed files with 152 additions and 164 deletions

View File

@@ -378,8 +378,8 @@
"npm run build passes",
"Verify in browser: butoane doar în header pe toate cele 3 pagini"
],
"passes": false,
"notes": ""
"passes": true,
"notes": "Completed in iteration 20"
},
{
"id": "US-519",

View File

@@ -182,3 +182,9 @@ Design Reference: src/modules/reports/views/InvoicesView.vue
[2026-01-12 23:33:06] Working on story: US-517
[2026-01-12 23:33:06] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_19_US-517.log)
[2026-01-12 23:35:59] SUCCESS: Story US-517 passed!
[2026-01-12 23:35:59] Changes committed
[2026-01-12 23:35:59] Progress: 17/19 stories completed
[2026-01-12 23:36:01] === Iteration 20/100 ===
[2026-01-12 23:36:01] Working on story: US-518
[2026-01-12 23:36:01] Running Claude... (log: /workspace/roa2web/scripts/ralph/logs/iteration_20_US-518.log)
[2026-01-12 23:39:17] SUCCESS: Story US-518 passed!

View File

@@ -40,11 +40,17 @@
/>
</div>
<div class="header-right">
<!-- Create/Edit mode actions -->
<!-- US-518: Create/Edit mode actions - Salvează + Anulează (+ Trimite for edit/draft) -->
<template v-if="!isViewMode">
<Button
icon="pi pi-times"
label="Anulează"
severity="secondary"
@click="goBack"
/>
<Button
icon="pi pi-save"
label="Salveaza"
label="Salvează"
:loading="saving"
@click="saveReceipt"
/>
@@ -58,12 +64,12 @@
/>
</template>
<!-- View mode actions -->
<!-- US-518: View mode actions - Editează, Șterge, and workflow buttons -->
<template v-else>
<Button
v-if="receipt?.status === 'draft' || receipt?.status === 'rejected'"
icon="pi pi-pencil"
label="Editeaza"
label="Editează"
@click="goToEdit"
/>
<Button
@@ -77,7 +83,7 @@
<Button
v-if="receipt?.status === 'pending_review'"
icon="pi pi-check"
label="Valideaza"
label="Validează"
severity="success"
@click="approveReceipt"
:loading="approving"
@@ -89,6 +95,13 @@
severity="danger"
@click="openRejectDialog"
/>
<Button
v-if="canDelete"
icon="pi pi-trash"
label="Șterge"
severity="danger"
@click="confirmDelete"
/>
</template>
</div>
</div>
@@ -203,11 +216,7 @@
</template>
</Dialog>
<!-- US-209: Mobile Action Bar - Context-aware buttons using shared component -->
<MobileActionBar
:visible="showMobileActionBar"
:actions="mobileActionBarActions"
/>
<!-- US-518: Removed MobileActionBar - all buttons now in MobileTopBar only -->
</div>
</template>
@@ -229,7 +238,7 @@ 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 MobileActionBar from '@shared/components/mobile/MobileActionBar.vue'
// US-518: MobileActionBar removed - buttons only in MobileTopBar now
import {
getDefaultUnifiedFormState,
legacyToUnifiedForm,
@@ -316,122 +325,7 @@ const canApproveReject = computed(() => {
return true
})
// US-209: Mobile Action Bar actions based on receipt state
const mobileActionBarActions = computed(() => {
// Create/Edit mode buttons
if (!isViewMode.value) {
return [
{
label: 'Salvează Ciornă',
icon: 'pi pi-save',
severity: 'secondary',
handler: saveReceipt,
disabled: saving.value
},
{
label: 'Trimite',
icon: 'pi pi-send',
severity: 'success',
handler: submitForReviewMobile,
disabled: !canSubmit.value || submitting.value
}
]
}
// View mode - context-aware based on status
const status = receiptStatus.value
const actions = []
switch (status) {
case 'draft':
// Draft: Salvează | Submit pentru Aprobare | Șterge
actions.push({
label: 'Editează',
icon: 'pi pi-pencil',
severity: 'secondary',
handler: goToEdit,
disabled: false
})
actions.push({
label: 'Trimite',
icon: 'pi pi-send',
severity: 'success',
handler: submitReceipt,
disabled: submitting.value
})
break
case 'pending_review':
// Pending: Salvează | Aprobă | Respinge (dacă are permisiuni)
if (canApproveReject.value) {
actions.push({
label: 'Validează',
icon: 'pi pi-check',
severity: 'success',
handler: approveReceipt,
disabled: approving.value
})
actions.push({
label: 'Respinge',
icon: 'pi pi-times',
severity: 'danger',
handler: openRejectDialog,
disabled: rejecting.value
})
}
break
case 'approved':
// Approved: doar vizualizare (butoane disabled sau ascunse)
// Show cancel approval button for admin users
actions.push({
label: 'Anulează Validarea',
icon: 'pi pi-replay',
severity: 'warning',
handler: confirmCancelApproval,
disabled: cancelling.value
})
break
case 'rejected':
// Rejected: Salvează (re-edit) | Re-submit
actions.push({
label: 'Editează',
icon: 'pi pi-pencil',
severity: 'primary',
handler: goToEdit,
disabled: false
})
actions.push({
label: 'Re-trimite',
icon: 'pi pi-send',
severity: 'success',
handler: submitReceipt,
disabled: submitting.value
})
break
}
return actions
})
// US-209/US-211: Check if MobileActionBar should be visible
// Hide when dialogs/overlays are open for better UX
const showMobileActionBar = computed(() => {
if (!isMobile.value) return false
// US-211: Hide action bar when overlays are open
if (showRejectDialog.value || showCreateSupplierDialog.value) return false
// US-211: Hide action bar when virtual keyboard is visible (better form UX)
if (keyboardVisible.value) return false
// In create/edit mode, always show (unless overlay open)
if (!isViewMode.value) return true
// In view mode, show only if there are actions
return mobileActionBarActions.value.length > 0
})
// US-518: Removed mobileActionBarActions and showMobileActionBar - buttons now only in MobileTopBar
// US-042: More menu items - contextual based on status
const moreMenuItems = computed(() => {
@@ -471,42 +365,118 @@ const moreMenuItems = computed(() => {
return items
})
// US-105: Mobile top bar actions - contextual based on mode
// US-518: Mobile top bar actions - ALL buttons in header only (no bottom bar)
// Includes both icon actions and contextual workflow buttons
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'
})
// Create/Edit mode: Salvează + Trimite buttons
actions.push({
id: 'save',
icon: 'pi pi-save',
label: 'Salvează',
tooltip: 'Salvează'
tooltip: 'Salvează ciornă',
disabled: saving.value
})
// Show Trimite button for edit mode when status is draft
if (isEditMode.value && receipt.value?.status === 'draft') {
actions.push({
id: 'submit',
icon: 'pi pi-send',
label: 'Trimite',
tooltip: 'Trimite spre aprobare',
severity: 'success',
disabled: !canSubmit.value || submitting.value
})
} else if (isCreateMode.value) {
// For create mode, show Submit after save
actions.push({
id: 'submit',
icon: 'pi pi-send',
label: 'Trimite',
tooltip: 'Salvează și trimite',
severity: 'success',
disabled: !canSubmit.value || submitting.value
})
}
} 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'
})
// View mode - context-aware based on receipt status
const status = receiptStatus.value
switch (status) {
case 'draft':
// Draft: Editează + Trimite
actions.push({
id: 'edit',
icon: 'pi pi-pencil',
label: 'Editează',
tooltip: 'Editează bonul'
})
actions.push({
id: 'submit-view',
icon: 'pi pi-send',
label: 'Trimite',
tooltip: 'Trimite spre aprobare',
severity: 'success',
disabled: submitting.value
})
break
case 'pending_review':
// Pending Review: Validează + Respinge
if (canApproveReject.value) {
actions.push({
id: 'approve',
icon: 'pi pi-check',
label: 'Validează',
tooltip: 'Aprobă bonul',
severity: 'success',
disabled: approving.value
})
actions.push({
id: 'reject',
icon: 'pi pi-times',
label: 'Respinge',
tooltip: 'Respinge bonul',
severity: 'danger',
disabled: rejecting.value
})
}
break
case 'approved':
// Approved: Anulează Validarea
actions.push({
id: 'cancel-approval',
icon: 'pi pi-replay',
label: 'Anulează',
tooltip: 'Anulează validarea',
severity: 'warning',
disabled: cancelling.value
})
break
case 'rejected':
// Rejected: Editează + Re-trimite
actions.push({
id: 'edit',
icon: 'pi pi-pencil',
label: 'Editează',
tooltip: 'Editează bonul'
})
actions.push({
id: 'submit-view',
icon: 'pi pi-send',
label: 'Re-trimite',
tooltip: 'Trimite din nou',
severity: 'success',
disabled: submitting.value
})
break
}
// Add more menu at the end for additional actions (delete, share, etc.)
actions.push({
id: 'more',
icon: 'pi pi-ellipsis-v',
@@ -518,18 +488,32 @@ const mobileTopBarActions = computed(() => {
return actions
})
// US-105: Handle top bar action clicks
// US-518: Handle top bar action clicks - all actions now in header
const handleTopBarAction = (action) => {
switch (action.id) {
case 'attach':
triggerFileAttach()
break
case 'save':
saveReceipt()
break
case 'submit':
// Create/Edit mode: save and submit
submitForReviewMobile()
break
case 'submit-view':
// View mode: just submit (already saved)
submitReceipt()
break
case 'edit':
goToEdit()
break
case 'approve':
approveReceipt()
break
case 'reject':
openRejectDialog()
break
case 'cancel-approval':
confirmCancelApproval()
break
case 'delete':
confirmDelete()
break
@@ -1411,12 +1395,11 @@ const cancelApproval = async () => {
}
/* ========================================
* US-041/US-105/US-209: Mobile Layout
* Uses shared MobileTopBar + MobileActionBar components
* US-518: Mobile Layout - Buttons ONLY in MobileTopBar
* Removed MobileActionBar - all actions now in header
* PRD Mobile Layout Tokens:
* - topBarHeight: 56px
* - bottomNavHeight: 56px
* - MobileActionBar: positioned at bottom: 56px (above MobileBottomNav)
* - touchTargetMin: 48px
* ======================================== */
@@ -1424,9 +1407,8 @@ const cancelApproval = async () => {
.receipt-unified-view.mobile-compose-layout {
padding: 0;
padding-top: 56px; /* Space for fixed MobileTopBar */
/* MobileActionBar sits at bottom: 56px, so we need:
56px (action bar height ~56px) + 56px (bottom nav) = 112px */
padding-bottom: 120px; /* Space for MobileActionBar + MobileBottomNav */
/* US-518: Only need space for MobileBottomNav (no more MobileActionBar) */
padding-bottom: 64px; /* Space for MobileBottomNav + small buffer */
max-width: 100%;
min-height: 100vh;
background: var(--surface-ground);