refactor: refs #8684 improve input handling and rendering in VnTable enhance item request logic in ItemRequest
gitea/salix-front/pipeline/pr-dev This commit is unstable Details

This commit is contained in:
Pablo Natek 2025-05-05 14:39:37 +02:00
parent cb1fa3c7f5
commit d64788732e
4 changed files with 81 additions and 52 deletions

View File

@ -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',
},

View File

@ -398,8 +398,7 @@ const clickHandler = async (event) => {
if (isDateElement || isTimeElement || isQSelectDropDown) return;
if (clickedElement === null) {
console.log('destroyInput 1');
await destroyInput(editingRow.value, editingField.value);
destroyInput(editingRow.value, editingField.value);
return;
}
const rowIndex = clickedElement.getAttribute('data-row-index');
@ -409,8 +408,7 @@ const clickHandler = async (event) => {
if (editingRow.value !== null && editingField.value !== null) {
if (editingRow.value == rowIndex && editingField.value == colField) return;
console.log('destroyInput 2');
await destroyInput(editingRow.value, editingField.value);
destroyInput(editingRow.value, editingField.value);
}
if (
@ -419,18 +417,16 @@ const clickHandler = async (event) => {
CrudModelRef.value.formData[rowIndex ?? editingRow.value],
)
) {
await renderInput(Number(rowIndex), colField, clickedElement);
renderInput(Number(rowIndex), colField, clickedElement);
}
};
async function handleTabKey(event, rowIndex, colField) {
if (editingRow.value == rowIndex && editingField.value == colField) {
console.log('destroyInput 3');
await destroyInput(editingRow.value, editingField.value);
}
function handleTabKey(event, rowIndex, colField) {
if (editingRow.value == rowIndex && editingField.value == colField)
destroyInput(editingRow.value, editingField.value);
const direction = event.shiftKey ? -1 : 1;
const { nextRowIndex, nextColumnName } = await handleTabNavigation(
const { nextRowIndex, nextColumnName } = handleTabNavigation(
rowIndex,
colField,
direction,
@ -439,11 +435,10 @@ async function handleTabKey(event, rowIndex, colField) {
if (nextRowIndex < 0 || nextRowIndex >= arrayData.store.data.length) return;
event.preventDefault();
console.log('nextColumnName: ', nextColumnName);
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;
@ -480,28 +475,20 @@ async function renderInput(rowId, field, clickedElement) {
} else row[column.name] = value;
await column?.cellEvent?.['update:modelValue']?.(value, oldValue, row);
},
keyup: async (event) => {
keyup: (event) => {
if (event.key === 'Enter') {
console.log('destroyInput 4');
await destroyInput(rowId, field, clickedElement);
destroyInput(rowId, field, clickedElement);
}
},
keydown: async (event) => {
keydown: (event) => {
switch (event.key) {
case 'Tab':
console.log('field: ', field);
console.log('target: ', event.target);
await handleTabKey(event, rowId, field);
handleTabKey(event, rowId, field);
event.stopPropagation();
const column = $props.columns.find((col) => col.name === field);
if (typeof column?.beforeDestroy === 'function')
await column.beforeDestroy(
CrudModelRef.value.formData[editingRow.value],
);
break;
case 'Escape':
console.log('destroyInput 5');
await destroyInput(rowId, field, clickedElement);
destroyInput(rowId, field, clickedElement);
break;
default:
break;
@ -534,17 +521,23 @@ 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 = '';
});
});
}
@ -553,7 +546,7 @@ async function destroyInput(rowIndex, field, clickedElement) {
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);

View File

@ -1,5 +1,5 @@
<script setup>
import { ref, computed, onMounted } from 'vue';
import { ref, computed, onMounted, nextTick } from 'vue';
import { useI18n } from 'vue-i18n';
import { useStateStore } from 'stores/useStateStore';
import { toCurrency } from 'filters/index';
@ -23,7 +23,6 @@ const stateStore = useStateStore();
const denyFormRef = ref(null);
const denyRequestId = ref(null);
const userParams = {
state: 'pending',
daysOnward: 7,
};
const tableRef = ref();
@ -136,6 +135,7 @@ const columns = computed(() => [
align: 'left',
component: 'input',
columnClass: 'expand',
isEditable: ({ isOk }) => isOk === null,
},
{
label: t('item.buyRequest.achieved'),
@ -143,22 +143,35 @@ const columns = computed(() => [
align: 'left',
component: 'input',
columnClass: 'shrink',
isEditable: (row) => row?.itemFk,
beforeDestroy: async ({ id, itemFk, saleQuantity, state }) => {
if (!saleQuantity) {
await tableRef.value.reload();
return;
isEditable: ({ itemFk, isOk }) => {
if (itemFk && isOk === null) return true;
},
beforeDestroy: (row) => {
console.log('row: ', row);
if (!row.saleQuantity) {
return tableRef.value.reload();
}
try {
await axios.post(`TicketRequests/${id}/confirm`, {
id,
itemFk: parseInt(itemFk),
quantity: parseInt(saleQuantity),
});
await tableRef.value.reload();
axios
.post(`TicketRequests/${row.id}/confirm`, {
id: row.id,
itemFk: parseInt(row.itemFk),
quantity: parseInt(row.saleQuantity),
})
.then(() => {
axios
.get(`Items/findOne`, { where: { id: row.itemFk } })
.then((response) => {
console.log('response: ', response);
row.itemDescription = response.data.name;
row.state = 1;
});
notify(t('globals.dataSaved'), 'positive');
return tableRef.value.reload();
});
} catch (error) {
notify(error.response.data.error.message, 'negative');
await tableRef.value.reload();
return tableRef.value.reload();
}
},
},
@ -174,7 +187,7 @@ const columns = computed(() => [
{
label: t('globals.state'),
name: 'state',
format: (row) => getState(row.isOk),
format: ({ isOk }) => getState(isOk),
align: 'left',
},
{
@ -202,6 +215,7 @@ const columns = computed(() => [
icon: 'thumb_down',
fill: true,
isPrimary: true,
show: ({ isOk }) => isOk === null,
action: (row) => showDenyRequestForm(row.id),
},
],
@ -309,7 +323,8 @@ const showDenyRequestForm = (requestId) => {
v-model="data.observation"
fill-input
:required="true"
autogrow
auto-grow
data-cy="discardTextArea"
/>
</template>
</FormModelPopup>

View File

@ -0,0 +1,22 @@
describe('Item Request', () => {
beforeEach(() => {
cy.login('buyer');
cy.visit(`/#/item/request`);
});
it('should fill the id and quantity then check the concept was updated', async () => {
cy.get('td[data-row-index="0"][data-col-field="itemFk"]')
.should('exist')
.type('4');
cy.get('td[data-row-index="0"][data-col-field="saleQuantity"]')
.should('exist')
.type('10{esc}');
cy.checkNotification('Data saved');
});
it('should now click on the second declain request icon then type the reason', async () => {
cy.selectOption('[data-cy="State_select"]', 'Pending');
cy.get('button[title="Discard"]').eq(0).click();
cy.dataCy('discardTextArea').should('exist').type('test(enter)');
cy.checkNotification('Data saved');
});
});