diff --git a/public/no-image.png b/public/no_image.png similarity index 100% rename from public/no-image.png rename to public/no_image.png diff --git a/public/no-image-dark.png b/public/no_image_dark.png similarity index 100% rename from public/no-image-dark.png rename to public/no_image_dark.png diff --git a/src/components/VnTable/VnContextMenu.vue b/src/components/VnTable/VnContextMenu.vue index c20c213f5..a99b2c478 100644 --- a/src/components/VnTable/VnContextMenu.vue +++ b/src/components/VnTable/VnContextMenu.vue @@ -18,7 +18,7 @@ const arrayData = defineModel({ function handler(event) { const clickedElement = event.target.closest('td'); if (!clickedElement) return; - + event.preventDefault(); target.value = event.target; qmenuRef.value.show(); colField.value = clickedElement.getAttribute('data-col-field'); diff --git a/src/components/VnTable/VnFilter.vue b/src/components/VnTable/VnFilter.vue index 13a345b48..e3f49af09 100644 --- a/src/components/VnTable/VnFilter.vue +++ b/src/components/VnTable/VnFilter.vue @@ -110,7 +110,6 @@ const components = { component: markRaw(VnCheckbox), event: updateEvent, attrs: { - class: $props.showTitle ? 'q-py-sm' : 'q-px-md q-py-xs fit', 'toggle-indeterminate': true, size: 'sm', }, diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue index eeb500c6d..9c58bd7ae 100644 --- a/src/components/VnTable/VnTable.vue +++ b/src/components/VnTable/VnTable.vue @@ -222,10 +222,7 @@ onBeforeMount(() => { onMounted(async () => { if ($props.isEditable) document.addEventListener('click', clickHandler); - document.addEventListener('contextmenu', (event) => { - event.preventDefault(); - contextMenuRef.value.handler(event); - }); + document.addEventListener('contextmenu', contextMenuRef.value.handler); mode.value = quasar.platform.is.mobile && !$props.disableOption?.card ? CARD_MODE @@ -386,16 +383,20 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) { } } -function isEditableColumn(column) { - const isEditableCol = column?.isEditable ?? true; +function isEditableColumn(column, row) { + const isEditableCol = + typeof column?.isEditable == 'function' + ? column?.isEditable(row) + : (column?.isEditable ?? true); + const isVisible = column?.visible ?? true; const hasComponent = column?.component; return $props.isEditable && isVisible && hasComponent && isEditableCol; } -function hasEditableFormat(column) { - if (isEditableColumn(column)) return 'editable-text'; +function hasEditableFormat(column, row) { + if (isEditableColumn(column, row)) return 'editable-text'; } const clickHandler = async (event) => { @@ -409,7 +410,7 @@ const clickHandler = async (event) => { if (isDateElement || isTimeElement || isQSelectDropDown || isDialog) return; if (clickedElement === null) { - await destroyInput(editingRow.value, editingField.value); + destroyInput(editingRow.value, editingField.value); return; } const rowIndex = clickedElement.getAttribute('data-row-index'); @@ -419,20 +420,25 @@ const clickHandler = async (event) => { if (editingRow.value !== null && editingField.value !== null) { if (editingRow.value == rowIndex && editingField.value == colField) return; - await destroyInput(editingRow.value, editingField.value); + destroyInput(editingRow.value, editingField.value); } - if (isEditableColumn(column)) { - await renderInput(Number(rowIndex), colField, clickedElement); + if ( + isEditableColumn( + column, + CrudModelRef.value.formData[rowIndex ?? editingRow.value], + ) + ) { + renderInput(Number(rowIndex), colField, clickedElement); } }; -async function handleTabKey(event, rowIndex, colField) { +function handleTabKey(event, rowIndex, colField) { if (editingRow.value == rowIndex && editingField.value == colField) - await destroyInput(editingRow.value, editingField.value); + destroyInput(editingRow.value, editingField.value); const direction = event.shiftKey ? -1 : 1; - const { nextRowIndex, nextColumnName } = await handleTabNavigation( + const { nextRowIndex, nextColumnName } = handleTabNavigation( rowIndex, colField, direction, @@ -441,10 +447,10 @@ async function handleTabKey(event, rowIndex, colField) { if (nextRowIndex < 0 || nextRowIndex >= arrayData.store.data.length) return; event.preventDefault(); - await renderInput(nextRowIndex, nextColumnName, null); + renderInput(nextRowIndex, nextColumnName, null); } -async function renderInput(rowId, field, clickedElement) { +function renderInput(rowId, field, clickedElement) { editingField.value = field; editingRow.value = rowId; @@ -482,19 +488,22 @@ async function renderInput(rowId, field, clickedElement) { } else row[column.name] = value; await column?.cellEvent?.['update:modelValue']?.(value, oldValue, row); }, - keyup: async (event) => { - if (event.key === 'Enter') - await destroyInput(rowId, field, clickedElement); + keyup: (event) => { + if (event.key === 'Enter') { + destroyInput(rowId, field, clickedElement); + event.stopPropagation(); + } }, - keydown: async (event) => { - await column?.cellEvent?.['keydown']?.(event, row); + keydown: (event) => { + column?.cellEvent?.['keydown']?.(event, row); switch (event.key) { case 'Tab': - await handleTabKey(event, rowId, field); + handleTabKey(event, rowId, field); event.stopPropagation(); break; case 'Escape': - await destroyInput(rowId, field, clickedElement); + destroyInput(rowId, field, clickedElement); + event.stopPropagation(); break; default: break; @@ -527,25 +536,32 @@ async function updateSelectValue(value, column, row, oldValue) { await column?.cellEvent?.['update:modelValue']?.(value, oldValue, row); } -async function destroyInput(rowIndex, field, clickedElement) { +function destroyInput(rowIndex, field, clickedElement) { if (!clickedElement) clickedElement = document.querySelector( `[data-row-index="${rowIndex}"][data-col-field="${field}"]`, ); + if (clickedElement) { - await nextTick(); - render(null, clickedElement); - Array.from(clickedElement.childNodes).forEach((child) => { - child.style.visibility = 'visible'; - child.style.position = ''; + const column = $props.columns.find((col) => col.name === field); + if (typeof column?.beforeDestroy === 'function') + column.beforeDestroy(CrudModelRef.value.formData[rowIndex]); + + nextTick().then(() => { + render(null, clickedElement); + Array.from(clickedElement.childNodes).forEach((child) => { + child.style.visibility = 'visible'; + child.style.position = ''; + }); }); } + if (editingRow.value !== rowIndex || editingField.value !== field) return; editingRow.value = null; editingField.value = null; } -async function handleTabNavigation(rowIndex, colName, direction) { +function handleTabNavigation(rowIndex, colName, direction) { const columns = $props.columns; const totalColumns = columns.length; let currentColumnIndex = columns.findIndex((col) => col.name === colName); @@ -557,7 +573,13 @@ async function handleTabNavigation(rowIndex, colName, direction) { iterations++; newColumnIndex = (newColumnIndex + direction + totalColumns) % totalColumns; - if (isEditableColumn(columns[newColumnIndex])) break; + if ( + isEditableColumn( + columns[newColumnIndex], + CrudModelRef.value.formData[rowIndex], + ) + ) + break; } while (iterations < totalColumns); if (iterations >= totalColumns + 1) return; @@ -884,19 +906,19 @@ const handleHeaderSelection = (evt, data) => { : getToggleIcon(row[col?.name]) " style="color: var(--vn-text-color)" - :class="hasEditableFormat(col)" + :class="hasEditableFormat(col, row)" size="14px" /> { isLoading.value = true; await props.submitFn(newPassword, oldPassword); emit('onSubmit'); - } catch (e) { - notify('errors.writeRequest', 'negative'); } finally { changePassDialog.value.hide(); isLoading.value = false; diff --git a/src/components/common/VnInputDates.vue b/src/components/common/VnInputDates.vue new file mode 100644 index 000000000..3135d3006 --- /dev/null +++ b/src/components/common/VnInputDates.vue @@ -0,0 +1,184 @@ + + + + + es: + Open date: Abrir fecha + diff --git a/src/components/ui/CardSummary.vue b/src/components/ui/CardSummary.vue index a6c23be2e..042c89fe6 100644 --- a/src/components/ui/CardSummary.vue +++ b/src/components/ui/CardSummary.vue @@ -162,7 +162,6 @@ async function fetch() { align-items: start; .label { color: var(--vn-label-color); - width: 9em; overflow: hidden; white-space: wrap; text-overflow: ellipsis; diff --git a/src/components/ui/VnDescriptor.vue b/src/components/ui/VnDescriptor.vue index 69fd5af6b..2e032a76a 100644 --- a/src/components/ui/VnDescriptor.vue +++ b/src/components/ui/VnDescriptor.vue @@ -236,10 +236,6 @@ const toModule = computed(() => { .label { color: var(--vn-label-color); font-size: 14px; - - &:not(:has(a))::after { - content: ':'; - } } &.ellipsis > .value { text-overflow: ellipsis; diff --git a/src/components/ui/VnImg.vue b/src/components/ui/VnImg.vue index 1b57c20d0..f73f9dcc5 100644 --- a/src/components/ui/VnImg.vue +++ b/src/components/ui/VnImg.vue @@ -1,8 +1,9 @@ + + diff --git a/src/pages/InvoiceIn/locale/en.yml b/src/pages/InvoiceIn/locale/en.yml index 018550715..69d40bc58 100644 --- a/src/pages/InvoiceIn/locale/en.yml +++ b/src/pages/InvoiceIn/locale/en.yml @@ -34,14 +34,6 @@ invoiceIn: originalInvoice: Original invoice entry: Entry emailEmpty: The email can't be empty - card: - client: Client - company: Company - customerCard: Customer card - ticketList: Ticket List - vat: Vat - dueDay: Due day - intrastat: Intrastat summary: currency: Currency issued: Expedition date @@ -71,3 +63,7 @@ invoiceIn: correctingFk: Rectificative issued: Issued noMatch: No match with the vat({totalTaxableBase}) + linkVehicleToInvoiceIn: Link vehicle to invoice + unlinkedVehicle: Unlinked vehicle + unlinkVehicle: Unlink vehicle + unlinkVehicleConfirmation: This vehicle will be unlinked from this invoice! Continue anyway? diff --git a/src/pages/InvoiceIn/locale/es.yml b/src/pages/InvoiceIn/locale/es.yml index 7de984774..dfe1ec1db 100644 --- a/src/pages/InvoiceIn/locale/es.yml +++ b/src/pages/InvoiceIn/locale/es.yml @@ -33,13 +33,6 @@ invoiceIn: originalInvoice: Factura origen entry: Entrada emailEmpty: El email no puede estar vacío - card: - client: Cliente - company: Empresa - customerCard: Ficha del cliente - ticketList: Listado de tickets - vat: Iva - dueDay: Fecha de vencimiento summary: currency: Divisa docNumber: Número documento @@ -52,7 +45,7 @@ invoiceIn: expense: Gasto taxableBase: Base imp. rate: Tasa - sageTransaction: Sage transación + sageTransaction: Sage transacción dueDay: Fecha bank: Caja foreignValue: Divisa @@ -69,3 +62,7 @@ invoiceIn: correctingFk: Rectificativa issued: Fecha de emisión noMatch: No cuadra con el iva({totalTaxableBase}) + linkVehicleToInvoiceIn: Vincular vehículo a factura + unlinkedVehicle: Vehículo desvinculado + unlinkVehicle: Desvincular vehículo + unlinkVehicleConfirmation: Este vehículo se desvinculará de esta factura! ¿Continuar de todas formas? diff --git a/src/pages/Item/Card/ItemBasicData.vue b/src/pages/Item/Card/ItemBasicData.vue index df7e71684..25fede8c6 100644 --- a/src/pages/Item/Card/ItemBasicData.vue +++ b/src/pages/Item/Card/ItemBasicData.vue @@ -61,7 +61,7 @@ const onIntrastatCreated = (response, formData) => { :clear-store-on-unmount="false" >