feat: Restructure receipt form UI with bon fiscal sections
- Restructure OCRPreview with 5 sections matching Romanian receipt format: FURNIZOR, DOCUMENT, TOTAL, PLATA, TVA - Add collapse button to OCRPreview header with minimize functionality - Restructure form with matching compact sections for side-by-side comparison - Add "din OCR" indicator when payment mode is auto-set from OCR - Remove auto-collapse behavior - OCRPreview stays visible after applying data - Add prominent TOTAL box styling with gradient background - Compact form layout with fields on same row where logical - Add TVA section to form with editable breakdown 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,117 +7,107 @@
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<span class="overall-confidence">
|
||||
Incredere generala:
|
||||
<OCRConfidenceIndicator
|
||||
:confidence="data.overall_confidence"
|
||||
:show-percentage="true"
|
||||
size="normal"
|
||||
/>
|
||||
</span>
|
||||
<Button
|
||||
icon="pi pi-minus"
|
||||
text
|
||||
rounded
|
||||
size="small"
|
||||
@click="$emit('collapse')"
|
||||
v-tooltip="'Minimizeaza'"
|
||||
class="collapse-btn"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="preview-content">
|
||||
<div class="preview-grid">
|
||||
<!-- Receipt Type -->
|
||||
<div class="preview-field" v-if="data.receipt_type">
|
||||
<label>Tip Document</label>
|
||||
<div class="field-value">
|
||||
<!-- SECTION: FURNIZOR -->
|
||||
<div class="ocr-section" v-if="data.partner_name || data.cui || data.address">
|
||||
<div class="ocr-section-title">FURNIZOR</div>
|
||||
<div class="ocr-section-content">
|
||||
<div class="vendor-name" v-if="data.partner_name">
|
||||
{{ data.partner_name }}
|
||||
<OCRConfidenceIndicator :confidence="data.confidence_vendor" size="small" />
|
||||
</div>
|
||||
<div class="vendor-cui" v-if="data.cui">CUI: {{ data.cui }}</div>
|
||||
<div class="vendor-address" v-if="data.address">{{ data.address }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SECTION: DOCUMENT -->
|
||||
<div class="ocr-section" v-if="data.receipt_type || data.receipt_number || data.receipt_date">
|
||||
<div class="ocr-section-title">DOCUMENT</div>
|
||||
<div class="ocr-section-content">
|
||||
<div class="document-row">
|
||||
<Tag
|
||||
v-if="data.receipt_type"
|
||||
:value="data.receipt_type === 'bon_fiscal' ? 'Bon Fiscal' : 'Chitanta'"
|
||||
:severity="data.receipt_type === 'bon_fiscal' ? 'info' : 'success'"
|
||||
/>
|
||||
<span v-if="data.receipt_number" class="doc-number">
|
||||
Nr: {{ data.receipt_series ? data.receipt_series + ' ' : '' }}{{ data.receipt_number }}
|
||||
</span>
|
||||
<span v-if="data.receipt_date" class="doc-date">
|
||||
<i class="pi pi-calendar"></i>
|
||||
{{ formatDate(data.receipt_date) }}
|
||||
<OCRConfidenceIndicator :confidence="data.confidence_date" size="small" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Amount -->
|
||||
<div class="preview-field" v-if="data.amount">
|
||||
<label>
|
||||
Suma
|
||||
<!-- SECTION: TOTAL -->
|
||||
<div class="ocr-section" v-if="data.amount">
|
||||
<div class="ocr-section-title">TOTAL</div>
|
||||
<div class="ocr-section-content">
|
||||
<div class="ocr-total-box">
|
||||
<span class="total-amount">{{ formatAmount(data.amount) }} LEI</span>
|
||||
<OCRConfidenceIndicator :confidence="data.confidence_amount" size="small" />
|
||||
</label>
|
||||
<div class="field-value amount">
|
||||
{{ formatAmount(data.amount) }} RON
|
||||
</div>
|
||||
<div v-if="data.items_count" class="items-count">
|
||||
{{ data.items_count }} articole
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Date -->
|
||||
<div class="preview-field" v-if="data.receipt_date">
|
||||
<label>
|
||||
Data
|
||||
<OCRConfidenceIndicator :confidence="data.confidence_date" size="small" />
|
||||
</label>
|
||||
<div class="field-value">
|
||||
{{ formatDate(data.receipt_date) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Receipt Number -->
|
||||
<div class="preview-field" v-if="data.receipt_number">
|
||||
<label>Numar Bon</label>
|
||||
<div class="field-value">
|
||||
{{ data.receipt_series ? data.receipt_series + ' ' : '' }}{{ data.receipt_number }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Vendor -->
|
||||
<div class="preview-field full-width" v-if="data.partner_name">
|
||||
<label>
|
||||
Furnizor
|
||||
<OCRConfidenceIndicator :confidence="data.confidence_vendor" size="small" />
|
||||
</label>
|
||||
<div class="field-value">
|
||||
{{ data.partner_name }}
|
||||
<span v-if="data.cui" class="cui-badge">CUI: {{ data.cui }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TVA Entries (multiple rates) -->
|
||||
<div class="preview-field full-width" v-if="data.tva_entries?.length > 0 || data.tva_total">
|
||||
<label>TVA</label>
|
||||
<div class="tva-breakdown">
|
||||
<div v-for="(entry, idx) in data.tva_entries" :key="idx" class="tva-entry">
|
||||
<span class="tva-code" v-if="entry.code">{{ entry.code }}</span>
|
||||
<span class="tva-percent-badge">{{ entry.percent }}%</span>
|
||||
<span class="tva-amount">{{ formatAmount(entry.amount) }} RON</span>
|
||||
</div>
|
||||
<div v-if="data.tva_total && data.tva_entries?.length > 1" class="tva-total">
|
||||
<strong>Total TVA:</strong> {{ formatAmount(data.tva_total) }} RON
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Payment Methods from OCR -->
|
||||
<div class="preview-field full-width" v-if="data.payment_methods?.length > 0">
|
||||
<label>Modalitati Plata (OCR)</label>
|
||||
<div class="payment-methods-list">
|
||||
<!-- SECTION: PLATA -->
|
||||
<div class="ocr-section" v-if="data.payment_methods?.length > 0">
|
||||
<div class="ocr-section-title">PLATA</div>
|
||||
<div class="ocr-section-content">
|
||||
<div class="ocr-payment-tags">
|
||||
<Tag
|
||||
v-for="(pm, idx) in data.payment_methods"
|
||||
:key="idx"
|
||||
:severity="pm.method === 'CARD' ? 'info' : 'success'"
|
||||
:value="`${pm.method}: ${formatAmount(pm.amount)} RON`"
|
||||
class="mr-1"
|
||||
:value="`${pm.method}: ${formatAmount(pm.amount)} LEI`"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="data.suggested_payment_mode" class="suggested-payment-mode">
|
||||
<i class="pi pi-lightbulb" style="color: #f59e0b;"></i>
|
||||
<i class="pi pi-lightbulb"></i>
|
||||
<span>Sugestie: <strong>{{ getSuggestedPaymentLabel(data.suggested_payment_mode) }}</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Items Count -->
|
||||
<div class="preview-field" v-if="data.items_count">
|
||||
<label>Nr. Articole</label>
|
||||
<div class="field-value">
|
||||
{{ data.items_count }} articole
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Address -->
|
||||
<div class="preview-field full-width" v-if="data.address">
|
||||
<label>Adresa</label>
|
||||
<div class="field-value address-text">
|
||||
{{ data.address }}
|
||||
<!-- SECTION: TVA -->
|
||||
<div class="ocr-section" v-if="data.tva_entries?.length > 0 || data.tva_total">
|
||||
<div class="ocr-section-title">TVA</div>
|
||||
<div class="ocr-section-content">
|
||||
<div class="ocr-tva-table">
|
||||
<div v-for="(entry, idx) in data.tva_entries" :key="idx" class="tva-row">
|
||||
<span class="tva-code" v-if="entry.code">{{ entry.code }}</span>
|
||||
<span class="tva-percent">({{ entry.percent }}%)</span>
|
||||
<span class="tva-amount">{{ formatAmount(entry.amount) }} LEI</span>
|
||||
</div>
|
||||
<div v-if="computedTvaTotal > 0" class="tva-row tva-total-row">
|
||||
<span class="tva-label">Total TVA:</span>
|
||||
<span class="tva-amount">{{ formatAmount(computedTvaTotal) }} LEI</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -168,9 +158,10 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { ref, computed } from 'vue'
|
||||
import OCRConfidenceIndicator from './OCRConfidenceIndicator.vue'
|
||||
import Tag from 'primevue/tag'
|
||||
import Button from 'primevue/button'
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
@@ -179,10 +170,17 @@ const props = defineProps({
|
||||
}
|
||||
})
|
||||
|
||||
defineEmits(['apply', 'dismiss'])
|
||||
defineEmits(['apply', 'dismiss', 'collapse'])
|
||||
|
||||
const showRawText = ref(false)
|
||||
|
||||
// Computed TVA total from entries
|
||||
const computedTvaTotal = computed(() => {
|
||||
if (props.data.tva_total) return parseFloat(props.data.tva_total)
|
||||
if (!props.data.tva_entries?.length) return 0
|
||||
return props.data.tva_entries.reduce((sum, e) => sum + parseFloat(e.amount || 0), 0)
|
||||
})
|
||||
|
||||
const getSuggestedPaymentLabel = (mode) => {
|
||||
const labels = {
|
||||
'casa': 'Casa (numerar firma)',
|
||||
@@ -279,6 +277,12 @@ const formatProcessingTime = (ms) => {
|
||||
color: #166534;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.overall-confidence {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -287,64 +291,145 @@ const formatProcessingTime = (ms) => {
|
||||
color: #166534;
|
||||
}
|
||||
|
||||
.collapse-btn {
|
||||
color: #166534 !important;
|
||||
}
|
||||
|
||||
.preview-content {
|
||||
padding: 1rem;
|
||||
padding: 0.75rem 1rem;
|
||||
}
|
||||
|
||||
.preview-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
gap: 1rem;
|
||||
/* Section-based layout (bon fiscal style) */
|
||||
.ocr-section {
|
||||
padding: 0.6rem 0;
|
||||
border-bottom: 1px solid #d1fae5;
|
||||
}
|
||||
|
||||
.preview-field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
.ocr-section:last-of-type {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.preview-field.full-width {
|
||||
grid-column: 1 / -1;
|
||||
.ocr-section-title {
|
||||
font-size: 0.7rem;
|
||||
font-weight: 600;
|
||||
color: #166534;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
margin-bottom: 0.35rem;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.preview-field label {
|
||||
font-size: 0.8rem;
|
||||
color: #64748b;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.field-value {
|
||||
font-weight: 500;
|
||||
.ocr-section-content {
|
||||
color: #1e293b;
|
||||
}
|
||||
|
||||
.field-value.amount {
|
||||
font-size: 1.25rem;
|
||||
/* FURNIZOR section */
|
||||
.vendor-name {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.vendor-cui {
|
||||
font-size: 0.85rem;
|
||||
color: #475569;
|
||||
margin-top: 0.15rem;
|
||||
}
|
||||
|
||||
.vendor-address {
|
||||
font-size: 0.8rem;
|
||||
color: #64748b;
|
||||
margin-top: 0.15rem;
|
||||
}
|
||||
|
||||
/* DOCUMENT section */
|
||||
.document-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.doc-number {
|
||||
font-weight: 500;
|
||||
color: #334155;
|
||||
}
|
||||
|
||||
.doc-date {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
color: #475569;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.doc-date .pi-calendar {
|
||||
font-size: 0.85rem;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
/* TOTAL section - prominent */
|
||||
.ocr-total-box {
|
||||
background: linear-gradient(135deg, #dcfce7 0%, #bbf7d0 100%);
|
||||
border: 2px solid #86efac;
|
||||
border-radius: 8px;
|
||||
padding: 0.75rem 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.total-amount {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: #166534;
|
||||
}
|
||||
|
||||
.cui-badge {
|
||||
display: inline-block;
|
||||
margin-left: 0.5rem;
|
||||
padding: 0.15rem 0.5rem;
|
||||
background: #e2e8f0;
|
||||
border-radius: 4px;
|
||||
.items-count {
|
||||
text-align: center;
|
||||
font-size: 0.8rem;
|
||||
color: #475569;
|
||||
color: #64748b;
|
||||
margin-top: 0.35rem;
|
||||
}
|
||||
|
||||
.tva-breakdown {
|
||||
/* PLATA section */
|
||||
.ocr-payment-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.suggested-payment-mode {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-top: 0.5rem;
|
||||
padding: 0.4rem 0.6rem;
|
||||
background: #fef3c7;
|
||||
border-radius: 6px;
|
||||
font-size: 0.8rem;
|
||||
color: #92400e;
|
||||
}
|
||||
|
||||
.suggested-payment-mode .pi-lightbulb {
|
||||
color: #f59e0b;
|
||||
}
|
||||
|
||||
/* TVA section */
|
||||
.ocr-tva-table {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.tva-entry {
|
||||
.tva-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.tva-code {
|
||||
@@ -353,49 +438,25 @@ const formatProcessingTime = (ms) => {
|
||||
min-width: 1rem;
|
||||
}
|
||||
|
||||
.tva-percent-badge {
|
||||
display: inline-block;
|
||||
padding: 0.15rem 0.5rem;
|
||||
background: #dbeafe;
|
||||
border-radius: 4px;
|
||||
.tva-percent {
|
||||
color: #64748b;
|
||||
font-size: 0.8rem;
|
||||
color: #1e40af;
|
||||
min-width: 2.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tva-amount {
|
||||
font-weight: 500;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.tva-total {
|
||||
.tva-total-row {
|
||||
margin-top: 0.25rem;
|
||||
padding-top: 0.25rem;
|
||||
border-top: 1px dashed #cbd5e1;
|
||||
border-top: 1px dashed #86efac;
|
||||
}
|
||||
|
||||
.payment-methods-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.suggested-payment-mode {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-top: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
background: #fef3c7;
|
||||
border-radius: 6px;
|
||||
font-size: 0.85rem;
|
||||
color: #92400e;
|
||||
}
|
||||
|
||||
.address-text {
|
||||
font-size: 0.9rem;
|
||||
color: #475569;
|
||||
.tva-label {
|
||||
font-weight: 600;
|
||||
color: #166534;
|
||||
}
|
||||
|
||||
.raw-text-section {
|
||||
|
||||
@@ -15,318 +15,352 @@
|
||||
</div>
|
||||
|
||||
<form @submit.prevent="saveReceipt">
|
||||
<!-- OCR Upload Section (for both create and edit modes) -->
|
||||
<div class="upload-section">
|
||||
<h3>
|
||||
<i class="pi pi-camera"></i>
|
||||
{{ isEditMode ? 'Re-scanare OCR (optional)' : 'Poza Bon (obligatoriu)' }}
|
||||
</h3>
|
||||
<div class="receipt-form-layout">
|
||||
<!-- COLOANA STÂNGA: Upload & OCR -->
|
||||
<div class="form-column-left">
|
||||
<div class="upload-section">
|
||||
<h3>
|
||||
<i class="pi pi-camera"></i>
|
||||
{{ isEditMode ? 'Re-scanare OCR (optional)' : 'Poza Bon' }}
|
||||
</h3>
|
||||
|
||||
<!-- OCR Upload Zone -->
|
||||
<OCRUploadZone
|
||||
ref="ocrUploadZone"
|
||||
@ocr-result="onOCRResult"
|
||||
@file-selected="onOCRFileSelected"
|
||||
@error="onOCRError"
|
||||
/>
|
||||
<!-- OCR Upload Zone -->
|
||||
<OCRUploadZone
|
||||
ref="ocrUploadZone"
|
||||
@ocr-result="onOCRResult"
|
||||
@file-selected="onOCRFileSelected"
|
||||
@error="onOCRError"
|
||||
/>
|
||||
|
||||
<!-- OCR Preview (when results are available) -->
|
||||
<OCRPreview
|
||||
v-if="ocrData"
|
||||
:data="ocrData"
|
||||
@apply="applyOCRData"
|
||||
@dismiss="dismissOCRData"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Standard Upload Section (for edit mode or additional files) -->
|
||||
<div class="upload-section" v-if="isEditMode || selectedFiles.length > 0">
|
||||
<h3 v-if="isEditMode">
|
||||
<i class="pi pi-camera"></i>
|
||||
Poza Bon (obligatoriu)
|
||||
</h3>
|
||||
<h3 v-else-if="selectedFiles.length > 0">
|
||||
<i class="pi pi-paperclip"></i>
|
||||
Fisiere Selectate
|
||||
</h3>
|
||||
|
||||
<FileUpload
|
||||
v-if="isEditMode"
|
||||
ref="fileUpload"
|
||||
mode="advanced"
|
||||
:multiple="true"
|
||||
accept="image/*,application/pdf"
|
||||
:maxFileSize="10000000"
|
||||
@select="onFileSelect"
|
||||
@remove="onFileRemove"
|
||||
:auto="false"
|
||||
:showUploadButton="false"
|
||||
:showCancelButton="false"
|
||||
>
|
||||
<template #empty>
|
||||
<div class="upload-area">
|
||||
<i class="pi pi-cloud-upload" style="font-size: 3rem; color: #667eea;"></i>
|
||||
<p>Trage fisierele aici sau click pentru a selecta</p>
|
||||
<p style="font-size: 0.8rem; color: #888;">
|
||||
Formate acceptate: JPG, PNG, PDF (max 10MB)
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</FileUpload>
|
||||
|
||||
<!-- Existing attachments (edit mode) -->
|
||||
<div v-if="existingAttachments.length" class="image-preview-grid">
|
||||
<div
|
||||
v-for="att in existingAttachments"
|
||||
:key="att.id"
|
||||
class="image-preview-item"
|
||||
>
|
||||
<img
|
||||
v-if="att.mime_type?.startsWith('image/')"
|
||||
:src="store.getAttachmentUrl(att.id)"
|
||||
:alt="att.filename"
|
||||
/>
|
||||
<div v-else class="pdf-preview">
|
||||
<i class="pi pi-file-pdf" style="font-size: 3rem;"></i>
|
||||
<span>{{ att.filename }}</span>
|
||||
</div>
|
||||
<Button
|
||||
icon="pi pi-times"
|
||||
severity="danger"
|
||||
rounded
|
||||
class="remove-btn"
|
||||
@click="removeExistingAttachment(att.id)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Selected files preview (create mode) -->
|
||||
<div v-if="!isEditMode && selectedFiles.length" class="selected-files-list">
|
||||
<div
|
||||
v-for="(file, index) in selectedFiles"
|
||||
:key="index"
|
||||
class="selected-file-item"
|
||||
>
|
||||
<i :class="file.type.startsWith('image/') ? 'pi pi-image' : 'pi pi-file-pdf'"></i>
|
||||
<span class="file-name">{{ file.name }}</span>
|
||||
<span class="file-size">{{ formatFileSize(file.size) }}</span>
|
||||
<Button
|
||||
icon="pi pi-times"
|
||||
severity="danger"
|
||||
rounded
|
||||
size="small"
|
||||
@click="removeSelectedFile(index)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
|
||||
<!-- Receipt Details -->
|
||||
<h3>
|
||||
<i class="pi pi-info-circle"></i>
|
||||
Detalii Bon
|
||||
</h3>
|
||||
|
||||
<div class="form-grid">
|
||||
<div class="form-field">
|
||||
<label>Tip Document *</label>
|
||||
<div class="radio-group">
|
||||
<div class="radio-item">
|
||||
<RadioButton
|
||||
v-model="form.receipt_type"
|
||||
value="bon_fiscal"
|
||||
inputId="type_bon"
|
||||
/>
|
||||
<label for="type_bon">Bon Fiscal</label>
|
||||
</div>
|
||||
<div class="radio-item">
|
||||
<RadioButton
|
||||
v-model="form.receipt_type"
|
||||
value="chitanta"
|
||||
inputId="type_chitanta"
|
||||
/>
|
||||
<label for="type_chitanta">Chitanta</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label>Directie *</label>
|
||||
<div class="radio-group">
|
||||
<div class="radio-item">
|
||||
<RadioButton
|
||||
v-model="form.direction"
|
||||
value="cheltuiala"
|
||||
inputId="dir_cheltuiala"
|
||||
/>
|
||||
<label for="dir_cheltuiala">Cheltuiala</label>
|
||||
</div>
|
||||
<div class="radio-item">
|
||||
<RadioButton
|
||||
v-model="form.direction"
|
||||
value="incasare"
|
||||
inputId="dir_incasare"
|
||||
/>
|
||||
<label for="dir_incasare">Incasare</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label>Data Bon *</label>
|
||||
<Calendar
|
||||
v-model="form.receipt_date"
|
||||
dateFormat="dd.mm.yy"
|
||||
showIcon
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label>Suma (RON) *</label>
|
||||
<InputNumber
|
||||
v-model="form.amount"
|
||||
mode="currency"
|
||||
currency="RON"
|
||||
locale="ro-RO"
|
||||
:minFractionDigits="2"
|
||||
:maxFractionDigits="2"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label>Furnizor</label>
|
||||
<AutoComplete
|
||||
v-model="form.partner_name"
|
||||
:suggestions="filteredPartners"
|
||||
optionLabel="name"
|
||||
field="name"
|
||||
@complete="searchPartners"
|
||||
@item-select="onPartnerSelect"
|
||||
placeholder="Cauta furnizor..."
|
||||
dropdown
|
||||
:forceSelection="false"
|
||||
/>
|
||||
<small v-if="supplierSource" class="p-text-success supplier-selected">
|
||||
<i class="pi pi-check-circle"></i>
|
||||
Validat ({{ supplierSource }})
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label>CUI (Cod Fiscal)</label>
|
||||
<InputText v-model="form.cui" placeholder="Ex: RO12345678" />
|
||||
<small v-if="supplierWarning.show" class="p-text-warning supplier-warning">
|
||||
<i class="pi pi-exclamation-triangle"></i>
|
||||
CUI {{ supplierWarning.cui }} negasit in nomenclator
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label>Tip Cheltuiala *</label>
|
||||
<Dropdown
|
||||
v-model="form.expense_type_code"
|
||||
:options="expenseTypes"
|
||||
optionLabel="name"
|
||||
optionValue="code"
|
||||
placeholder="Selecteaza tip"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label>Mod Plata</label>
|
||||
<Dropdown
|
||||
v-model="form.payment_mode"
|
||||
:options="paymentModeOptions"
|
||||
optionLabel="label"
|
||||
optionValue="value"
|
||||
placeholder="Selecteaza mod plata"
|
||||
showClear
|
||||
/>
|
||||
<small class="field-hint text-secondary" v-if="!form.payment_mode">
|
||||
Obligatoriu la trimiterea pentru aprobare
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="form-field">
|
||||
<label>Numar Bon</label>
|
||||
<InputText v-model="form.receipt_number" placeholder="Optional" />
|
||||
</div>
|
||||
|
||||
<div class="form-field form-field-full">
|
||||
<label>Descriere</label>
|
||||
<Textarea
|
||||
v-model="form.description"
|
||||
rows="3"
|
||||
placeholder="Descriere optionala..."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Detalii Suplimentare (populated from OCR) -->
|
||||
<div v-if="form.tva_breakdown?.length > 0 || form.items_count || form.vendor_address || form.payment_methods?.length > 0" class="extra-details-section">
|
||||
<h3>
|
||||
<i class="pi pi-list"></i>
|
||||
Detalii Suplimentare (din OCR)
|
||||
</h3>
|
||||
|
||||
<!-- TVA Breakdown -->
|
||||
<div class="form-field form-field-full" v-if="form.tva_breakdown?.length > 0">
|
||||
<label>Defalcare TVA</label>
|
||||
<div class="tva-table">
|
||||
<div v-for="(entry, idx) in form.tva_breakdown" :key="idx" class="tva-row">
|
||||
<span class="tva-label">TVA {{ entry.code }} ({{ entry.percent }}%):</span>
|
||||
<InputNumber
|
||||
v-model="form.tva_breakdown[idx].amount"
|
||||
mode="currency"
|
||||
currency="RON"
|
||||
locale="ro-RO"
|
||||
:minFractionDigits="2"
|
||||
class="tva-input"
|
||||
/>
|
||||
</div>
|
||||
<div class="tva-row total" v-if="form.tva_breakdown.length > 0">
|
||||
<span class="tva-label"><strong>Total TVA:</strong></span>
|
||||
<span class="tva-value">{{ formatTvaTotal() }} RON</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Payment Methods (from OCR) -->
|
||||
<div class="form-field form-field-full" v-if="form.payment_methods?.length > 0">
|
||||
<label>Modalitati Plata</label>
|
||||
<div class="payment-methods-display">
|
||||
<Tag
|
||||
v-for="pm in form.payment_methods"
|
||||
:key="pm.method"
|
||||
:severity="pm.method === 'CARD' ? 'info' : 'success'"
|
||||
:value="`${pm.method}: ${formatCurrency(pm.amount)}`"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-grid">
|
||||
<div class="form-field" v-if="form.items_count">
|
||||
<label>Nr. Articole</label>
|
||||
<InputNumber
|
||||
v-model="form.items_count"
|
||||
:min="1"
|
||||
placeholder="Ex: 17"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-field" v-if="form.vendor_address">
|
||||
<label>Adresa Furnizor</label>
|
||||
<InputText
|
||||
v-model="form.vendor_address"
|
||||
placeholder="Adresa din bon"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- OCR Applied Banner (collapsed state) -->
|
||||
<div
|
||||
v-if="ocrData && ocrCollapsed"
|
||||
class="ocr-applied-banner"
|
||||
@click="ocrCollapsed = false"
|
||||
>
|
||||
<i class="pi pi-check-circle"></i>
|
||||
<span>Date OCR aplicate</span>
|
||||
<i class="pi pi-chevron-down"></i>
|
||||
</div>
|
||||
|
||||
<!-- OCR Preview (expanded state) -->
|
||||
<OCRPreview
|
||||
v-if="ocrData && !ocrCollapsed"
|
||||
:data="ocrData"
|
||||
@apply="applyOCRData"
|
||||
@dismiss="dismissOCRData"
|
||||
@collapse="ocrCollapsed = true"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Standard Upload Section (for edit mode or additional files) -->
|
||||
<div class="upload-section" v-if="isEditMode || selectedFiles.length > 0">
|
||||
<h3 v-if="isEditMode">
|
||||
<i class="pi pi-images"></i>
|
||||
Atasamente
|
||||
</h3>
|
||||
<h3 v-else-if="selectedFiles.length > 0">
|
||||
<i class="pi pi-paperclip"></i>
|
||||
Fisiere Selectate
|
||||
</h3>
|
||||
|
||||
<FileUpload
|
||||
v-if="isEditMode"
|
||||
ref="fileUpload"
|
||||
mode="advanced"
|
||||
:multiple="true"
|
||||
accept="image/*,application/pdf"
|
||||
:maxFileSize="10000000"
|
||||
@select="onFileSelect"
|
||||
@remove="onFileRemove"
|
||||
:auto="false"
|
||||
:showUploadButton="false"
|
||||
:showCancelButton="false"
|
||||
>
|
||||
<template #empty>
|
||||
<div class="upload-area">
|
||||
<i class="pi pi-cloud-upload" style="font-size: 2rem; color: #667eea;"></i>
|
||||
<p>Trage fisierele aici</p>
|
||||
</div>
|
||||
</template>
|
||||
</FileUpload>
|
||||
|
||||
<!-- Existing attachments (edit mode) -->
|
||||
<div v-if="existingAttachments.length" class="image-preview-grid">
|
||||
<div
|
||||
v-for="att in existingAttachments"
|
||||
:key="att.id"
|
||||
class="image-preview-item"
|
||||
>
|
||||
<img
|
||||
v-if="att.mime_type?.startsWith('image/')"
|
||||
:src="store.getAttachmentUrl(att.id)"
|
||||
:alt="att.filename"
|
||||
/>
|
||||
<div v-else class="pdf-preview">
|
||||
<i class="pi pi-file-pdf" style="font-size: 2rem;"></i>
|
||||
<span>{{ att.filename }}</span>
|
||||
</div>
|
||||
<Button
|
||||
icon="pi pi-times"
|
||||
severity="danger"
|
||||
rounded
|
||||
class="remove-btn"
|
||||
size="small"
|
||||
@click="removeExistingAttachment(att.id)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Selected files preview (create mode) -->
|
||||
<div v-if="!isEditMode && selectedFiles.length" class="selected-files-list">
|
||||
<div
|
||||
v-for="(file, index) in selectedFiles"
|
||||
:key="index"
|
||||
class="selected-file-item"
|
||||
>
|
||||
<i :class="file.type.startsWith('image/') ? 'pi pi-image' : 'pi pi-file-pdf'"></i>
|
||||
<span class="file-name">{{ file.name }}</span>
|
||||
<span class="file-size">{{ formatFileSize(file.size) }}</span>
|
||||
<Button
|
||||
icon="pi pi-times"
|
||||
severity="danger"
|
||||
rounded
|
||||
size="small"
|
||||
@click="removeSelectedFile(index)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- COLOANA DREAPTA: Formular Compact -->
|
||||
<div class="form-column-right">
|
||||
<!-- SECTION: FURNIZOR -->
|
||||
<div class="form-section">
|
||||
<div class="form-section-title">FURNIZOR</div>
|
||||
<div class="form-section-content">
|
||||
<div class="form-row">
|
||||
<div class="form-field flex-2">
|
||||
<label>Furnizor</label>
|
||||
<AutoComplete
|
||||
v-model="form.partner_name"
|
||||
:suggestions="filteredPartners"
|
||||
optionLabel="name"
|
||||
field="name"
|
||||
@complete="searchPartners"
|
||||
@item-select="onPartnerSelect"
|
||||
placeholder="Cauta furnizor..."
|
||||
dropdown
|
||||
:forceSelection="false"
|
||||
/>
|
||||
<small v-if="supplierSource" class="p-text-success supplier-selected">
|
||||
<i class="pi pi-check-circle"></i>
|
||||
Validat ({{ supplierSource }})
|
||||
</small>
|
||||
</div>
|
||||
<div class="form-field flex-1">
|
||||
<label>CUI</label>
|
||||
<InputText v-model="form.cui" placeholder="RO12345678" />
|
||||
<small v-if="supplierWarning.show" class="p-text-warning supplier-warning">
|
||||
<i class="pi pi-exclamation-triangle"></i>
|
||||
Negasit
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row" v-if="form.vendor_address">
|
||||
<div class="form-field flex-1">
|
||||
<label>Adresa</label>
|
||||
<InputText v-model="form.vendor_address" placeholder="Adresa furnizor" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SECTION: DOCUMENT -->
|
||||
<div class="form-section">
|
||||
<div class="form-section-title">DOCUMENT</div>
|
||||
<div class="form-section-content">
|
||||
<div class="form-row">
|
||||
<div class="form-field">
|
||||
<div class="radio-group-inline">
|
||||
<div class="radio-item">
|
||||
<RadioButton
|
||||
v-model="form.receipt_type"
|
||||
value="bon_fiscal"
|
||||
inputId="type_bon"
|
||||
/>
|
||||
<label for="type_bon">Bon</label>
|
||||
</div>
|
||||
<div class="radio-item">
|
||||
<RadioButton
|
||||
v-model="form.receipt_type"
|
||||
value="chitanta"
|
||||
inputId="type_chitanta"
|
||||
/>
|
||||
<label for="type_chitanta">Chitanta</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label>Nr.</label>
|
||||
<InputText v-model="form.receipt_number" placeholder="12345" style="max-width: 120px;" />
|
||||
</div>
|
||||
<div class="form-field">
|
||||
<label>Data *</label>
|
||||
<Calendar
|
||||
v-model="form.receipt_date"
|
||||
dateFormat="dd.mm.yy"
|
||||
showIcon
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-field">
|
||||
<div class="radio-group-inline">
|
||||
<div class="radio-item">
|
||||
<RadioButton
|
||||
v-model="form.direction"
|
||||
value="cheltuiala"
|
||||
inputId="dir_cheltuiala"
|
||||
/>
|
||||
<label for="dir_cheltuiala">Cheltuiala</label>
|
||||
</div>
|
||||
<div class="radio-item">
|
||||
<RadioButton
|
||||
v-model="form.direction"
|
||||
value="incasare"
|
||||
inputId="dir_incasare"
|
||||
/>
|
||||
<label for="dir_incasare">Incasare</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SECTION: TOTAL -->
|
||||
<div class="form-section">
|
||||
<div class="form-section-title">TOTAL</div>
|
||||
<div class="form-section-content">
|
||||
<div class="form-row">
|
||||
<div class="form-field">
|
||||
<label>Suma *</label>
|
||||
<InputNumber
|
||||
v-model="form.amount"
|
||||
mode="currency"
|
||||
currency="RON"
|
||||
locale="ro-RO"
|
||||
:minFractionDigits="2"
|
||||
:maxFractionDigits="2"
|
||||
required
|
||||
class="amount-input"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-field flex-1">
|
||||
<label>Tip Cheltuiala *</label>
|
||||
<Dropdown
|
||||
v-model="form.expense_type_code"
|
||||
:options="expenseTypes"
|
||||
optionLabel="name"
|
||||
optionValue="code"
|
||||
placeholder="Selecteaza tip"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row" v-if="form.items_count">
|
||||
<div class="form-field">
|
||||
<label>Nr. Articole</label>
|
||||
<InputNumber
|
||||
v-model="form.items_count"
|
||||
:min="1"
|
||||
placeholder="17"
|
||||
style="max-width: 100px;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-field flex-1">
|
||||
<label>Descriere</label>
|
||||
<Textarea
|
||||
v-model="form.description"
|
||||
rows="2"
|
||||
placeholder="Descriere optionala..."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SECTION: PLATA -->
|
||||
<div class="form-section">
|
||||
<div class="form-section-title">PLATA</div>
|
||||
<div class="form-section-content">
|
||||
<div class="form-row">
|
||||
<div class="form-field flex-1">
|
||||
<label>Mod Plata</label>
|
||||
<div class="payment-field-wrapper">
|
||||
<Dropdown
|
||||
v-model="form.payment_mode"
|
||||
:options="paymentModeOptions"
|
||||
optionLabel="label"
|
||||
optionValue="value"
|
||||
placeholder="Selecteaza mod plata"
|
||||
showClear
|
||||
/>
|
||||
<span v-if="paymentSetFromOCR" class="ocr-indicator">
|
||||
<i class="pi pi-check-circle"></i>
|
||||
din OCR
|
||||
</span>
|
||||
</div>
|
||||
<small class="field-hint text-secondary" v-if="!form.payment_mode">
|
||||
Obligatoriu la trimitere
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Payment methods tags from OCR -->
|
||||
<div v-if="form.payment_methods?.length > 0" class="payment-methods-display">
|
||||
<Tag
|
||||
v-for="pm in form.payment_methods"
|
||||
:key="pm.method"
|
||||
:severity="pm.method === 'CARD' ? 'info' : 'success'"
|
||||
:value="`${pm.method}: ${formatCurrency(pm.amount)}`"
|
||||
class="mr-1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SECTION: TVA -->
|
||||
<div class="form-section" v-if="form.tva_breakdown?.length > 0">
|
||||
<div class="form-section-title">TVA</div>
|
||||
<div class="form-section-content">
|
||||
<div class="tva-edit-table">
|
||||
<div v-for="(entry, idx) in form.tva_breakdown" :key="idx" class="tva-edit-row">
|
||||
<span class="tva-label-compact">{{ entry.code }} ({{ entry.percent }}%)</span>
|
||||
<InputNumber
|
||||
v-model="form.tva_breakdown[idx].amount"
|
||||
mode="currency"
|
||||
currency="RON"
|
||||
locale="ro-RO"
|
||||
:minFractionDigits="2"
|
||||
class="tva-input-compact"
|
||||
/>
|
||||
</div>
|
||||
<div class="tva-edit-row tva-total-row" v-if="form.tva_breakdown.length > 0">
|
||||
<span class="tva-label-compact"><strong>Total TVA:</strong></span>
|
||||
<span class="tva-total-value">{{ formatTvaTotal() }} RON</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End form-column-right -->
|
||||
</div>
|
||||
<!-- End receipt-form-layout -->
|
||||
|
||||
<Divider />
|
||||
|
||||
@@ -457,12 +491,16 @@ const submitting = ref(false)
|
||||
const ocrUploadZone = ref(null)
|
||||
const ocrData = ref(null)
|
||||
const ocrFile = ref(null)
|
||||
const ocrCollapsed = ref(false)
|
||||
|
||||
// Supplier dialog refs
|
||||
const showCreateSupplierDialog = ref(false)
|
||||
const pendingSupplierData = ref(null)
|
||||
const supplierWarning = ref({ show: false, cui: '', name: '' })
|
||||
|
||||
// OCR indicator for payment mode
|
||||
const paymentSetFromOCR = ref(false)
|
||||
|
||||
// AutoComplete support
|
||||
const filteredPartners = ref([])
|
||||
const supplierSource = ref(null) // 'local', 'synced', or null
|
||||
@@ -618,6 +656,7 @@ const applyOCRData = async (data) => {
|
||||
// Auto-suggest payment_mode if OCR detected CARD
|
||||
if (data.suggested_payment_mode) {
|
||||
form.value.payment_mode = data.suggested_payment_mode
|
||||
paymentSetFromOCR.value = true // Show OCR indicator
|
||||
}
|
||||
|
||||
// Auto-search supplier by CUI if available
|
||||
@@ -695,8 +734,8 @@ const applyOCRData = async (data) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Clear OCR preview
|
||||
ocrData.value = null
|
||||
// NOTE: OCRPreview rămâne vizibil pentru comparație side-by-side
|
||||
// (NU mai colapsăm automat - utilizatorul poate compara datele)
|
||||
|
||||
toast.add({
|
||||
severity: 'success',
|
||||
@@ -811,7 +850,8 @@ const formatTvaTotal = () => {
|
||||
|
||||
const validateForm = () => {
|
||||
// Check if we have at least one file (for new receipts)
|
||||
if (!isEditMode.value && selectedFiles.value.length === 0) {
|
||||
// Also check ocrFile as a fallback (file selected for OCR processing)
|
||||
if (!isEditMode.value && selectedFiles.value.length === 0 && !ocrFile.value) {
|
||||
toast.add({
|
||||
severity: 'warn',
|
||||
summary: 'Validare',
|
||||
@@ -953,15 +993,86 @@ const submitForReview = async () => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 2-column layout */
|
||||
.receipt-form-layout {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(280px, 1fr) minmax(380px, 1.5fr);
|
||||
gap: 1.5rem;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.form-column-left {
|
||||
position: sticky;
|
||||
top: 1rem;
|
||||
}
|
||||
|
||||
.form-column-right {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.form-column-right h3 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.75rem;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.receipt-form-layout {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.form-column-left {
|
||||
position: static;
|
||||
}
|
||||
}
|
||||
|
||||
/* OCR Applied Banner (collapsed state) */
|
||||
.ocr-applied-banner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.6rem 1rem;
|
||||
background: #dcfce7;
|
||||
border: 1px solid #86efac;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
margin-top: 0.75rem;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.ocr-applied-banner:hover {
|
||||
background: #bbf7d0;
|
||||
}
|
||||
|
||||
.ocr-applied-banner .pi-check-circle {
|
||||
color: #22c55e;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.ocr-applied-banner span {
|
||||
flex: 1;
|
||||
font-weight: 500;
|
||||
color: #166534;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.ocr-applied-banner .pi-chevron-down {
|
||||
color: #166534;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.upload-section {
|
||||
margin-bottom: 1.5rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.upload-section h3 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
margin-bottom: 0.75rem;
|
||||
margin-top: 0;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.radio-group {
|
||||
@@ -1031,8 +1142,8 @@ const submitForReview = async () => {
|
||||
|
||||
/* Extra details section (TVA, items, address) */
|
||||
.extra-details-section {
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem;
|
||||
margin-top: 1rem;
|
||||
padding: 0.75rem;
|
||||
background: #f0f9ff;
|
||||
border: 1px solid #bae6fd;
|
||||
border-radius: 8px;
|
||||
@@ -1042,8 +1153,10 @@ const submitForReview = async () => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
margin-bottom: 0.75rem;
|
||||
margin-top: 0;
|
||||
color: #0284c7;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.tva-table {
|
||||
@@ -1142,4 +1255,163 @@ const submitForReview = async () => {
|
||||
font-weight: 500;
|
||||
color: #334155;
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
COMPACT FORM SECTIONS (matching OCRPreview)
|
||||
======================================== */
|
||||
|
||||
.form-section {
|
||||
padding: 0.6rem 0;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.form-section:last-of-type {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.form-section-title {
|
||||
font-size: 0.7rem;
|
||||
font-weight: 600;
|
||||
color: #64748b;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.form-section-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
/* Compact form rows */
|
||||
.form-row {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
align-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.form-row .form-field {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.form-field.flex-1 {
|
||||
flex: 1;
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
.form-field.flex-2 {
|
||||
flex: 2;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
/* Inline radio groups */
|
||||
.radio-group-inline {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
padding: 0.35rem 0;
|
||||
}
|
||||
|
||||
.radio-group-inline .radio-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
.radio-group-inline .radio-item label {
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Amount input styling */
|
||||
.amount-input {
|
||||
max-width: 150px;
|
||||
}
|
||||
|
||||
/* Payment field wrapper with OCR indicator */
|
||||
.payment-field-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.payment-field-wrapper .p-dropdown {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.ocr-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
font-size: 0.75rem;
|
||||
color: #22c55e;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ocr-indicator .pi-check-circle {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
/* TVA edit table - compact */
|
||||
.tva-edit-table {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
.tva-edit-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.tva-label-compact {
|
||||
min-width: 80px;
|
||||
font-size: 0.85rem;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.tva-input-compact {
|
||||
max-width: 120px;
|
||||
}
|
||||
|
||||
.tva-edit-row.tva-total-row {
|
||||
margin-top: 0.35rem;
|
||||
padding-top: 0.35rem;
|
||||
border-top: 1px dashed #cbd5e1;
|
||||
}
|
||||
|
||||
.tva-total-value {
|
||||
font-weight: 600;
|
||||
color: #0284c7;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* Payment methods display in form */
|
||||
.payment-methods-display {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 0.35rem;
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.form-row {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.form-field.flex-1,
|
||||
.form-field.flex-2 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.payment-field-wrapper {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user