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

This commit is contained in:
Pablo Natek 2025-05-05 08:54:38 +02:00
parent ab6dc5d2ca
commit 240b927a02
4 changed files with 65 additions and 25 deletions

View File

@ -373,16 +373,20 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
} }
} }
function isEditableColumn(column) { function isEditableColumn(column, row) {
const isEditableCol = column?.isEditable ?? true; const isEditableCol =
typeof column?.isEditable == 'function'
? column?.isEditable(row)
: (column?.isEditable ?? true);
const isVisible = column?.visible ?? true; const isVisible = column?.visible ?? true;
const hasComponent = column?.component; const hasComponent = column?.component;
return $props.isEditable && isVisible && hasComponent && isEditableCol; return $props.isEditable && isVisible && hasComponent && isEditableCol;
} }
function hasEditableFormat(column) { function hasEditableFormat(column, row) {
if (isEditableColumn(column)) return 'editable-text'; if (isEditableColumn(column, row)) return 'editable-text';
} }
const clickHandler = async (event) => { const clickHandler = async (event) => {
@ -394,6 +398,7 @@ const clickHandler = async (event) => {
if (isDateElement || isTimeElement || isQSelectDropDown) return; if (isDateElement || isTimeElement || isQSelectDropDown) return;
if (clickedElement === null) { if (clickedElement === null) {
console.log('destroyInput 1');
await destroyInput(editingRow.value, editingField.value); await destroyInput(editingRow.value, editingField.value);
return; return;
} }
@ -404,17 +409,25 @@ const clickHandler = async (event) => {
if (editingRow.value !== null && editingField.value !== null) { if (editingRow.value !== null && editingField.value !== null) {
if (editingRow.value == rowIndex && editingField.value == colField) return; if (editingRow.value == rowIndex && editingField.value == colField) return;
console.log('destroyInput 2');
await destroyInput(editingRow.value, editingField.value); await destroyInput(editingRow.value, editingField.value);
} }
if (isEditableColumn(column)) { if (
isEditableColumn(
column,
CrudModelRef.value.formData[rowIndex ?? editingRow.value],
)
) {
await renderInput(Number(rowIndex), colField, clickedElement); await renderInput(Number(rowIndex), colField, clickedElement);
} }
}; };
async function handleTabKey(event, rowIndex, colField) { async function handleTabKey(event, rowIndex, colField) {
if (editingRow.value == rowIndex && editingField.value == colField) if (editingRow.value == rowIndex && editingField.value == colField) {
console.log('destroyInput 3');
await destroyInput(editingRow.value, editingField.value); await destroyInput(editingRow.value, editingField.value);
}
const direction = event.shiftKey ? -1 : 1; const direction = event.shiftKey ? -1 : 1;
const { nextRowIndex, nextColumnName } = await handleTabNavigation( const { nextRowIndex, nextColumnName } = await handleTabNavigation(
@ -426,6 +439,7 @@ async function handleTabKey(event, rowIndex, colField) {
if (nextRowIndex < 0 || nextRowIndex >= arrayData.store.data.length) return; if (nextRowIndex < 0 || nextRowIndex >= arrayData.store.data.length) return;
event.preventDefault(); event.preventDefault();
console.log('nextColumnName: ', nextColumnName);
await renderInput(nextRowIndex, nextColumnName, null); await renderInput(nextRowIndex, nextColumnName, null);
} }
@ -467,16 +481,21 @@ async function renderInput(rowId, field, clickedElement) {
await column?.cellEvent?.['update:modelValue']?.(value, oldValue, row); await column?.cellEvent?.['update:modelValue']?.(value, oldValue, row);
}, },
keyup: async (event) => { keyup: async (event) => {
if (event.key === 'Enter') if (event.key === 'Enter') {
console.log('destroyInput 4');
await destroyInput(rowId, field, clickedElement); await destroyInput(rowId, field, clickedElement);
}
}, },
keydown: async (event) => { keydown: async (event) => {
switch (event.key) { switch (event.key) {
case 'Tab': case 'Tab':
console.log('field: ', field);
console.log('target: ', event.target);
await handleTabKey(event, rowId, field); await handleTabKey(event, rowId, field);
event.stopPropagation(); event.stopPropagation();
break; break;
case 'Escape': case 'Escape':
console.log('destroyInput 5');
await destroyInput(rowId, field, clickedElement); await destroyInput(rowId, field, clickedElement);
break; break;
default: default:
@ -511,6 +530,10 @@ async function updateSelectValue(value, column, row, oldValue) {
} }
async function destroyInput(rowIndex, field, clickedElement) { async function destroyInput(rowIndex, field, clickedElement) {
const column = $props.columns.find((col) => col.name === field);
if (typeof column?.beforeDestroy === 'function')
await column.beforeDestroy(CrudModelRef.value.formData[rowIndex]);
if (!clickedElement) if (!clickedElement)
clickedElement = document.querySelector( clickedElement = document.querySelector(
`[data-row-index="${rowIndex}"][data-col-field="${field}"]`, `[data-row-index="${rowIndex}"][data-col-field="${field}"]`,
@ -523,6 +546,7 @@ async function destroyInput(rowIndex, field, clickedElement) {
child.style.position = ''; child.style.position = '';
}); });
} }
if (editingRow.value !== rowIndex || editingField.value !== field) return; if (editingRow.value !== rowIndex || editingField.value !== field) return;
editingRow.value = null; editingRow.value = null;
editingField.value = null; editingField.value = null;
@ -540,7 +564,13 @@ async function handleTabNavigation(rowIndex, colName, direction) {
iterations++; iterations++;
newColumnIndex = (newColumnIndex + direction + totalColumns) % totalColumns; newColumnIndex = (newColumnIndex + direction + totalColumns) % totalColumns;
if (isEditableColumn(columns[newColumnIndex])) break; if (
isEditableColumn(
columns[newColumnIndex],
CrudModelRef.value.formData[rowIndex],
)
)
break;
} while (iterations < totalColumns); } while (iterations < totalColumns);
if (iterations >= totalColumns + 1) return; if (iterations >= totalColumns + 1) return;
@ -840,19 +870,19 @@ const handleHeaderSelection = (evt, data) => {
: getToggleIcon(row[col?.name]) : getToggleIcon(row[col?.name])
" "
style="color: var(--vn-text-color)" style="color: var(--vn-text-color)"
:class="hasEditableFormat(col)" :class="hasEditableFormat(col, row)"
size="14px" size="14px"
/> />
<QIcon <QIcon
v-else-if="col?.component === 'checkbox'" v-else-if="col?.component === 'checkbox'"
:name="getCheckboxIcon(row[col?.name])" :name="getCheckboxIcon(row[col?.name])"
style="color: var(--vn-text-color)" style="color: var(--vn-text-color)"
:class="hasEditableFormat(col)" :class="hasEditableFormat(col, row)"
size="14px" size="14px"
/> />
<span <span
v-else v-else
:class="hasEditableFormat(col)" :class="hasEditableFormat(col, row)"
:style=" :style="
typeof col?.style == 'function' typeof col?.style == 'function'
? col.style(row) ? col.style(row)

View File

@ -13,7 +13,6 @@ import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/Departme
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import ItemRequestFilter from './ItemRequestFilter.vue'; import ItemRequestFilter from './ItemRequestFilter.vue';
import RightMenu from 'src/components/common/RightMenu.vue'; import RightMenu from 'src/components/common/RightMenu.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import FormModelPopup from 'src/components/FormModelPopup.vue'; import FormModelPopup from 'src/components/FormModelPopup.vue';
import ItemDescriptorProxy from './Card/ItemDescriptorProxy.vue'; import ItemDescriptorProxy from './Card/ItemDescriptorProxy.vue';
import { dashIfEmpty } from 'src/filters'; import { dashIfEmpty } from 'src/filters';
@ -23,8 +22,6 @@ const { notify } = useNotify();
const stateStore = useStateStore(); const stateStore = useStateStore();
const denyFormRef = ref(null); const denyFormRef = ref(null);
const denyRequestId = ref(null); const denyRequestId = ref(null);
const denyRequestIndex = ref(null);
const itemRequestsOptions = ref([]);
const userParams = { const userParams = {
state: 'pending', state: 'pending',
daysOnward: 7, daysOnward: 7,
@ -146,7 +143,24 @@ const columns = computed(() => [
align: 'left', align: 'left',
component: 'input', component: 'input',
columnClass: 'shrink', columnClass: 'shrink',
cellEvent: {}, isEditable: (row) => row?.itemFk,
beforeDestroy: async ({ id, itemFk, saleQuantity, state }) => {
if (!saleQuantity) {
await tableRef.value.reload();
return;
}
try {
await axios.post(`TicketRequests/${id}/confirm`, {
id,
itemFk: parseInt(itemFk),
quantity: parseInt(saleQuantity),
});
await tableRef.value.reload();
} catch (error) {
notify(error.response.data.error.message, 'negative');
console.log('error: ', error);
}
},
}, },
{ {
label: t('item.buyRequest.concept'), label: t('item.buyRequest.concept'),

View File

@ -6,8 +6,7 @@ describe('Item summary', () => {
}); });
it('should clone the item', () => { it('should clone the item', () => {
cy.dataCy('descriptor-more-opts').click(); cy.selectDescriptorOption(2);
cy.get('.q-menu > .q-list > :nth-child(2) > .q-item__section').click();
cy.dataCy('VnConfirm_confirm').click(); cy.dataCy('VnConfirm_confirm').click();
cy.waitForElement('[data-cy="itemTags"]'); cy.waitForElement('[data-cy="itemTags"]');
cy.dataCy('itemTags').should('be.visible'); cy.dataCy('itemTags').should('be.visible');

View File

@ -1,15 +1,14 @@
describe('Item list', () => { describe('Item list', () => {
beforeEach(() => { beforeEach(() => {
cy.login('developer'); cy.login('buyer');
cy.visit(`/#/item/list`); cy.visit(`/#/item/list`);
}); });
it('should filter the items and redirect to the summary', () => { it('should filter the items and redirect to the summary', () => {
cy.dataCy('Category_select').type('Plant'); cy.selectOption('[data-cy="Category_select"]', 'Plant');
cy.get('.q-menu .q-item').contains('Plant').click(); cy.selectOption('[data-cy="Type_select"]', 'Anthurium');
cy.dataCy('Type_select').type('Anthurium');
cy.get('.q-menu .q-item').contains('Anthurium').click();
cy.get('.q-virtual-scroll__content > :nth-child(4) > :nth-child(4)').click(); cy.get('.q-virtual-scroll__content > :nth-child(4) > :nth-child(4)').click();
cy.url().should('include', '/summary');
}); });
it('should create an item', () => { it('should create an item', () => {
@ -24,8 +23,6 @@ describe('Item list', () => {
cy.fillInForm(data); cy.fillInForm(data);
cy.dataCy('FormModelPopup_save').click(); cy.dataCy('FormModelPopup_save').click();
cy.checkNotification('Data created'); cy.checkNotification('Data created');
cy.get( cy.url().should('include', '/basic-data');
':nth-child(2) > .q-drawer > .q-drawer__content > .q-scrollarea > .q-scrollarea__container > .q-scrollarea__content',
).should('be.visible');
}); });
}); });