diff --git a/scripts/ralph/prd.json b/scripts/ralph/prd.json
index 6c3c1dc..d2d02b4 100644
--- a/scripts/ralph/prd.json
+++ b/scripts/ralph/prd.json
@@ -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",
diff --git a/scripts/ralph/progress.txt b/scripts/ralph/progress.txt
index d37f1e8..a954336 100644
--- a/scripts/ralph/progress.txt
+++ b/scripts/ralph/progress.txt
@@ -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!
diff --git a/src/modules/data-entry/views/receipts/ReceiptCreateUnifiedView.vue b/src/modules/data-entry/views/receipts/ReceiptCreateUnifiedView.vue
index 2e17808..4e55169 100644
--- a/src/modules/data-entry/views/receipts/ReceiptCreateUnifiedView.vue
+++ b/src/modules/data-entry/views/receipts/ReceiptCreateUnifiedView.vue
@@ -40,11 +40,17 @@
/>
@@ -203,11 +216,7 @@
-
-
+
@@ -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);