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