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",
|
"npm run build passes",
|
||||||
"Verify in browser: butoane doar în header pe toate cele 3 pagini"
|
"Verify in browser: butoane doar în header pe toate cele 3 pagini"
|
||||||
],
|
],
|
||||||
"passes": false,
|
"passes": true,
|
||||||
"notes": ""
|
"notes": "Completed in iteration 20"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "US-519",
|
"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] 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: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] 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>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
<!-- Create/Edit mode actions -->
|
<!-- US-518: Create/Edit mode actions - Salvează + Anulează (+ Trimite for edit/draft) -->
|
||||||
<template v-if="!isViewMode">
|
<template v-if="!isViewMode">
|
||||||
|
<Button
|
||||||
|
icon="pi pi-times"
|
||||||
|
label="Anulează"
|
||||||
|
severity="secondary"
|
||||||
|
@click="goBack"
|
||||||
|
/>
|
||||||
<Button
|
<Button
|
||||||
icon="pi pi-save"
|
icon="pi pi-save"
|
||||||
label="Salveaza"
|
label="Salvează"
|
||||||
:loading="saving"
|
:loading="saving"
|
||||||
@click="saveReceipt"
|
@click="saveReceipt"
|
||||||
/>
|
/>
|
||||||
@@ -58,12 +64,12 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- View mode actions -->
|
<!-- US-518: View mode actions - Editează, Șterge, and workflow buttons -->
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<Button
|
<Button
|
||||||
v-if="receipt?.status === 'draft' || receipt?.status === 'rejected'"
|
v-if="receipt?.status === 'draft' || receipt?.status === 'rejected'"
|
||||||
icon="pi pi-pencil"
|
icon="pi pi-pencil"
|
||||||
label="Editeaza"
|
label="Editează"
|
||||||
@click="goToEdit"
|
@click="goToEdit"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
@@ -77,7 +83,7 @@
|
|||||||
<Button
|
<Button
|
||||||
v-if="receipt?.status === 'pending_review'"
|
v-if="receipt?.status === 'pending_review'"
|
||||||
icon="pi pi-check"
|
icon="pi pi-check"
|
||||||
label="Valideaza"
|
label="Validează"
|
||||||
severity="success"
|
severity="success"
|
||||||
@click="approveReceipt"
|
@click="approveReceipt"
|
||||||
:loading="approving"
|
:loading="approving"
|
||||||
@@ -89,6 +95,13 @@
|
|||||||
severity="danger"
|
severity="danger"
|
||||||
@click="openRejectDialog"
|
@click="openRejectDialog"
|
||||||
/>
|
/>
|
||||||
|
<Button
|
||||||
|
v-if="canDelete"
|
||||||
|
icon="pi pi-trash"
|
||||||
|
label="Șterge"
|
||||||
|
severity="danger"
|
||||||
|
@click="confirmDelete"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -203,11 +216,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
<!-- US-209: Mobile Action Bar - Context-aware buttons using shared component -->
|
<!-- US-518: Removed MobileActionBar - all buttons now in MobileTopBar only -->
|
||||||
<MobileActionBar
|
|
||||||
:visible="showMobileActionBar"
|
|
||||||
:actions="mobileActionBarActions"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -229,7 +238,7 @@ 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 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 {
|
import {
|
||||||
getDefaultUnifiedFormState,
|
getDefaultUnifiedFormState,
|
||||||
legacyToUnifiedForm,
|
legacyToUnifiedForm,
|
||||||
@@ -316,122 +325,7 @@ const canApproveReject = computed(() => {
|
|||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
// US-209: Mobile Action Bar actions based on receipt state
|
// US-518: Removed mobileActionBarActions and showMobileActionBar - buttons now only in MobileTopBar
|
||||||
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-042: More menu items - contextual based on status
|
// US-042: More menu items - contextual based on status
|
||||||
const moreMenuItems = computed(() => {
|
const moreMenuItems = computed(() => {
|
||||||
@@ -471,42 +365,118 @@ const moreMenuItems = computed(() => {
|
|||||||
return items
|
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 mobileTopBarActions = computed(() => {
|
||||||
const actions = []
|
const actions = []
|
||||||
|
|
||||||
if (!isViewMode.value) {
|
if (!isViewMode.value) {
|
||||||
// Create/Edit mode: attach file + save
|
// Create/Edit mode: Salvează + Trimite buttons
|
||||||
actions.push({
|
|
||||||
id: 'attach',
|
|
||||||
icon: 'pi pi-paperclip',
|
|
||||||
label: 'Atașează fișier',
|
|
||||||
tooltip: 'Atașează fișier'
|
|
||||||
})
|
|
||||||
actions.push({
|
actions.push({
|
||||||
id: 'save',
|
id: 'save',
|
||||||
icon: 'pi pi-save',
|
icon: 'pi pi-save',
|
||||||
label: 'Salvează',
|
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 {
|
} else {
|
||||||
// View mode: edit (if allowed), delete (if allowed), more menu
|
// View mode - context-aware based on receipt status
|
||||||
if (receipt.value?.status === 'draft' || receipt.value?.status === 'rejected') {
|
const status = receiptStatus.value
|
||||||
actions.push({
|
|
||||||
id: 'edit',
|
switch (status) {
|
||||||
icon: 'pi pi-pencil',
|
case 'draft':
|
||||||
label: 'Editează',
|
// Draft: Editează + Trimite
|
||||||
tooltip: 'Editează'
|
actions.push({
|
||||||
})
|
id: 'edit',
|
||||||
}
|
icon: 'pi pi-pencil',
|
||||||
if (canDelete.value) {
|
label: 'Editează',
|
||||||
actions.push({
|
tooltip: 'Editează bonul'
|
||||||
id: 'delete',
|
})
|
||||||
icon: 'pi pi-trash',
|
actions.push({
|
||||||
label: 'Șterge',
|
id: 'submit-view',
|
||||||
tooltip: 'Șterge'
|
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({
|
actions.push({
|
||||||
id: 'more',
|
id: 'more',
|
||||||
icon: 'pi pi-ellipsis-v',
|
icon: 'pi pi-ellipsis-v',
|
||||||
@@ -518,18 +488,32 @@ const mobileTopBarActions = computed(() => {
|
|||||||
return actions
|
return actions
|
||||||
})
|
})
|
||||||
|
|
||||||
// US-105: Handle top bar action clicks
|
// US-518: Handle top bar action clicks - all actions now in header
|
||||||
const handleTopBarAction = (action) => {
|
const handleTopBarAction = (action) => {
|
||||||
switch (action.id) {
|
switch (action.id) {
|
||||||
case 'attach':
|
|
||||||
triggerFileAttach()
|
|
||||||
break
|
|
||||||
case 'save':
|
case 'save':
|
||||||
saveReceipt()
|
saveReceipt()
|
||||||
break
|
break
|
||||||
|
case 'submit':
|
||||||
|
// Create/Edit mode: save and submit
|
||||||
|
submitForReviewMobile()
|
||||||
|
break
|
||||||
|
case 'submit-view':
|
||||||
|
// View mode: just submit (already saved)
|
||||||
|
submitReceipt()
|
||||||
|
break
|
||||||
case 'edit':
|
case 'edit':
|
||||||
goToEdit()
|
goToEdit()
|
||||||
break
|
break
|
||||||
|
case 'approve':
|
||||||
|
approveReceipt()
|
||||||
|
break
|
||||||
|
case 'reject':
|
||||||
|
openRejectDialog()
|
||||||
|
break
|
||||||
|
case 'cancel-approval':
|
||||||
|
confirmCancelApproval()
|
||||||
|
break
|
||||||
case 'delete':
|
case 'delete':
|
||||||
confirmDelete()
|
confirmDelete()
|
||||||
break
|
break
|
||||||
@@ -1411,12 +1395,11 @@ const cancelApproval = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ========================================
|
/* ========================================
|
||||||
* US-041/US-105/US-209: Mobile Layout
|
* US-518: Mobile Layout - Buttons ONLY in MobileTopBar
|
||||||
* Uses shared MobileTopBar + MobileActionBar components
|
* Removed MobileActionBar - all actions now in header
|
||||||
* PRD Mobile Layout Tokens:
|
* PRD Mobile Layout Tokens:
|
||||||
* - topBarHeight: 56px
|
* - topBarHeight: 56px
|
||||||
* - bottomNavHeight: 56px
|
* - bottomNavHeight: 56px
|
||||||
* - MobileActionBar: positioned at bottom: 56px (above MobileBottomNav)
|
|
||||||
* - touchTargetMin: 48px
|
* - touchTargetMin: 48px
|
||||||
* ======================================== */
|
* ======================================== */
|
||||||
|
|
||||||
@@ -1424,9 +1407,8 @@ const cancelApproval = async () => {
|
|||||||
.receipt-unified-view.mobile-compose-layout {
|
.receipt-unified-view.mobile-compose-layout {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
padding-top: 56px; /* Space for fixed MobileTopBar */
|
padding-top: 56px; /* Space for fixed MobileTopBar */
|
||||||
/* MobileActionBar sits at bottom: 56px, so we need:
|
/* US-518: Only need space for MobileBottomNav (no more MobileActionBar) */
|
||||||
56px (action bar height ~56px) + 56px (bottom nav) = 112px */
|
padding-bottom: 64px; /* Space for MobileBottomNav + small buffer */
|
||||||
padding-bottom: 120px; /* Space for MobileActionBar + MobileBottomNav */
|
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
background: var(--surface-ground);
|
background: var(--surface-ground);
|
||||||
|
|||||||
Reference in New Issue
Block a user