5673-hotFix_improve_crudModel_vnSelect #100

Merged
alexm merged 2 commits from 5673-hotFix_improve_crudModel_vnSelect into master 2023-10-13 07:13:27 +00:00
11 changed files with 138 additions and 41 deletions

View File

@ -9,7 +9,7 @@
"lint": "eslint --ext .js,.vue ./",
"format": "prettier --write \"**/*.{js,vue,scss,html,md,json}\" --ignore-path .gitignore",
"test:e2e": "cypress open",
"test:e2e:ci": "cypress run --browser chromium",
"test:e2e:ci": "cd ../salix && gulp docker && cd ../salix-front && cypress run --browser chromium",
Review

Aixina forcem que se fasa gulp docker quan tirem els e2e

Aixina forcem que se fasa gulp docker quan tirem els e2e
"test": "echo \"See package.json => scripts for available tests.\" && exit 0",
"test:unit": "vitest",
"test:unit:ci": "vitest run"

View File

@ -46,7 +46,7 @@ const onResponseError = (error) => {
message = responseError.message;
}
switch (response.status) {
switch (response?.status) {
case 500:
message = 'errors.statusInternalServerError';
break;
@ -58,7 +58,7 @@ const onResponseError = (error) => {
break;
}
if (session.isLoggedIn() && response.status === 401) {
if (session.isLoggedIn() && response?.status === 401) {
session.destroy();
const hash = window.location.hash;
const url = hash.slice(1);

View File

@ -8,6 +8,7 @@ import { useStateStore } from 'stores/useStateStore';
import VnPaginate from 'components/ui/VnPaginate.vue';
import VnConfirm from 'components/ui/VnConfirm.vue';
import SkeletonTable from 'components/ui/SkeletonTable.vue';
import { tMobile } from 'src/composables/tMobile';
const quasar = useQuasar();
const stateStore = useStateStore();
@ -62,9 +63,10 @@ const hasChanges = ref(false);
const originalData = ref();
const vnPaginateRef = ref();
const formData = ref();
const saveButtonRef = ref(null);
const formUrl = computed(() => $props.url);
const emit = defineEmits(['onFetch', 'update:selected']);
const emit = defineEmits(['onFetch', 'update:selected', 'saveChanges']);
defineExpose({
reload,
@ -73,12 +75,9 @@ defineExpose({
onSubmit,
reset,
hasChanges,
saveChanges,
});
function tMobile(...args) {
if (!quasar.platform.is.mobile) return t(...args);
}
async function fetch(data) {
if (data && Array.isArray(data)) {
let $index = 0;
@ -135,6 +134,7 @@ async function saveChanges(data) {
hasChanges.value = false;
isLoading.value = false;
emit('saveChanges', data);
}
async function insert() {
@ -269,7 +269,7 @@ watch(formUrl, async () => {
<SkeletonTable v-if="!formData" />
<Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown()">
<QBtnGroup push class="q-gutter-x-sm">
<slot name="moreActions" />
<slot name="moreBeforeActions" />
<QBtn
:label="tMobile('globals.remove')"
color="primary"
@ -292,6 +292,7 @@ watch(formUrl, async () => {
/>
<QBtn
:label="tMobile('globals.save')"
ref="saveButtonRef"
color="primary"
icon="save"
@click="onSubmit"
@ -299,6 +300,7 @@ watch(formUrl, async () => {
:title="t('globals.save')"
v-if="$props.defaultSave"
/>
<slot name="moreAfterActions" />
</QBtnGroup>
</Teleport>
<QInnerLoading

View File

@ -19,6 +19,8 @@ const $props = defineProps({
const { optionLabel, options } = toRefs($props);
const myOptions = ref([]);
const myOptionsOriginal = ref([]);
const vnSelectRef = ref(null);
function setOptions(data) {
myOptions.value = JSON.parse(JSON.stringify(data));
myOptionsOriginal.value = JSON.parse(JSON.stringify(data));
@ -29,6 +31,7 @@ const filter = (val, options) => {
const search = val.toLowerCase();
if (val === '') return options;
return options.filter((row) => {
const id = row.id;
const name = row[$props.optionLabel].toLowerCase();
@ -41,9 +44,17 @@ const filter = (val, options) => {
};
const filterHandler = (val, update) => {
update(() => {
update(
() => {
myOptions.value = filter(val, myOptionsOriginal.value);
});
},
(ref) => {
if (val !== '' && ref.options.length > 0) {
ref.setOptionIndex(-1);
ref.moveOptionSelection(1, true);
}
}
);
};
watch(options, (newValue) => {
@ -70,7 +81,15 @@ const value = computed({
map-options
use-input
@filter="filterHandler"
clearable
clear-icon="close"
/>
hide-selected
fill-input
ref="vnSelectRef"
>
<template #append>
<QIcon name="close" @click.stop="value = null" class="cursor-pointer" />
</template>
<template v-for="(_, slotName) in $slots" #[slotName]="slotData">
<slot :name="slotName" v-bind="slotData" />
</template>
</QSelect>
</template>

View File

@ -140,14 +140,6 @@ async function onLoad(...params) {
{{ t('No data to display') }}
</h5>
</div>
<div
v-if="store.data && store.data.length === 0 && !isLoading"
class="info-row q-pa-md text-center"
>
<h5>
{{ t('No results found') }}
</h5>
</div>
<div
v-if="props.skeleton && props.autoLoad && !store.data"
class="card-list q-gutter-y-md"

View File

@ -0,0 +1,8 @@
import { useQuasar } from 'quasar';
import { useI18n } from 'vue-i18n';
export function tMobile(...args) {
const quasar = useQuasar();
const { t } = useI18n();
if (!quasar.platform.is.mobile) return t(...args);
}

View File

@ -1,12 +1,15 @@
<script setup>
import { ref, computed } from 'vue';
import { ref, computed, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import CrudModel from 'components/CrudModel.vue';
import FetchData from 'components/FetchData.vue';
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
import { getUrl } from 'composables/getUrl';
import { tMobile } from 'composables/tMobile';
const route = useRoute();
const { t } = useI18n();
const claimDevelopmentForm = ref();
@ -16,6 +19,12 @@ const claimResponsibles = ref([]);
const claimRedeliveries = ref([]);
const workers = ref([]);
const selected = ref([]);
const insertButtonRef = ref();
let salixUrl;
onMounted(async () => {
salixUrl = await getUrl(`claim/${route.params.id}`);
});
const developmentsFilter = {
fields: [
@ -43,6 +52,7 @@ const columns = computed(() => [
model: 'claimReasonFk',
optionValue: 'id',
optionLabel: 'description',
tabIndex: 1,
},
{
name: 'claimResult',
@ -54,6 +64,7 @@ const columns = computed(() => [
model: 'claimResultFk',
optionValue: 'id',
optionLabel: 'description',
tabIndex: 2,
},
{
name: 'claimResponsible',
@ -65,6 +76,7 @@ const columns = computed(() => [
model: 'claimResponsibleFk',
optionValue: 'id',
optionLabel: 'description',
tabIndex: 3,
},
{
name: 'worker',
@ -75,6 +87,7 @@ const columns = computed(() => [
model: 'workerFk',
optionValue: 'id',
optionLabel: 'nickname',
tabIndex: 4,
},
{
name: 'claimRedelivery',
@ -86,8 +99,13 @@ const columns = computed(() => [
model: 'claimRedeliveryFk',
optionValue: 'id',
optionLabel: 'description',
tabIndex: 5,
},
]);
function goToAction() {
location.href = `${salixUrl}/action`;
}
</script>
<template>
<FetchData
@ -115,8 +133,9 @@ const columns = computed(() => [
auto-load
/>
<FetchData
url="Workers/activeWithInheritedRole"
:where="{ role: 'employee' }"
url="Workers/search"
:where="{ active: 1 }"
order="name ASC"
@on-fetch="(data) => (workers = data)"
auto-load
/>
@ -129,6 +148,8 @@ const columns = computed(() => [
:data-required="{ claimFk: route.params.id }"
v-model:selected="selected"
auto-load
@save-changes="goToAction"
:default-save="false"
>
<template #body="{ rows }">
<QTable
@ -142,19 +163,40 @@ const columns = computed(() => [
:grid="$q.screen.lt.md"
>
<template #body-cell="{ row, col }">
<QTd auto-width>
<QTd
auto-width
@keyup.ctrl.enter.stop="claimDevelopmentForm.saveChanges()"
>
<VnSelectFilter
:label="col.label"
v-model="row[col.model]"
:options="col.options"
:option-value="col.optionValue"
:option-label="col.optionLabel"
/>
:autofocus="col.tabIndex == 1"
input-debounce="0"
>
<template #option="scope" v-if="col.name == 'worker'">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>{{ scope.opt?.name }}</QItemLabel>
<QItemLabel caption>
{{ scope.opt?.nickname }}
{{ scope.opt?.code }}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelectFilter>
</QTd>
</template>
<template #item="props">
<div class="q-pa-xs col-xs-12 col-sm-6 grid-style-transition">
<QCard bordered flat>
<QCard
bordered
flat
@keyup.ctrl.enter.stop="claimDevelopmentForm?.saveChanges()"
>
<QCardSection>
<QCheckbox v-model="props.selected" dense />
</QCardSection>
@ -169,6 +211,8 @@ const columns = computed(() => [
:option-value="col.optionValue"
:option-label="col.optionLabel"
dense
input-debounce="0"
:autofocus="col.tabIndex == 1"
/>
</QItemSection>
</QItem>
@ -178,9 +222,28 @@ const columns = computed(() => [
</template>
</QTable>
</template>
<template #moreAfterActions>
<QBtn
:label="tMobile('globals.save')"
ref="saveButtonRef"
color="primary"
icon="save"
:disable="!claimDevelopmentForm?.hasChanges"
@click="claimDevelopmentForm?.onSubmit"
:title="t('globals.save')"
/>
</template>
</CrudModel>
<QPageSticky position="bottom-right" :offset="[25, 25]">
<QBtn fab color="primary" icon="add" @click="claimDevelopmentForm.insert()" />
<QBtn
ref="insertButtonRef"
fab
color="primary"
icon="add"
@click="claimDevelopmentForm.insert()"
@keydown.ctrl.enter.stop="claimDevelopmentForm.saveChanges()"
@keydown.enter.stop
/>
</QPageSticky>
</template>

View File

@ -40,7 +40,6 @@ const claimLinesForm = ref();
const claim = ref(null);
async function onFetchClaim(data) {
claim.value = data;
fetchMana();
}
@ -147,8 +146,11 @@ function showImportDialog() {
quasar
.dialog({
component: ClaimLinesImport,
componentProps: {
ticketId: claim.value.ticketFk,
},
})
.onOk(() => arrayData.refresh());
.onOk(() => claimLinesForm.value.reload());
}
</script>
<template>

View File

@ -14,6 +14,13 @@ const route = useRoute();
const quasar = useQuasar();
const { t } = useI18n();
const $props = defineProps({
ticketId: {
type: Number,
required: true,
},
});
const columns = computed(() => [
{
name: 'delivered',
@ -99,7 +106,7 @@ function cancel() {
</script>
<template>
<FetchData
url="Sales/getClaimableFromTicket?ticketFk=16"
Review

La seccio de linies anava mal pq se havia ficat un 16 ahi pq si...

La seccio de linies anava mal pq se havia ficat un 16 ahi pq si...
:url="`Sales/getClaimableFromTicket?ticketFk=${$props.ticketId}`"
@on-fetch="(data) => (claimableSales = data)"
auto-load
/>

View File

@ -13,15 +13,17 @@ describe('ClaimDevelopment', () => {
it('should reset line', () => {
cy.selectOption(firstLineReason, 'Novato');
cy.resetCard();
cy.getValue(firstLineReason).should('have.text', 'Prisas');
cy.getValue(firstLineReason).should('have.value', 'Prisas');
});
it('should edit line', () => {
cy.selectOption(firstLineReason, 'Novato');
cy.saveCard();
cy.reload();
cy.getValue(firstLineReason).should('have.text', 'Novato');
cy.saveCard();
cy.login('developer');
cy.visit(`/#/claim/${claimId}/development`);
cy.getValue(firstLineReason).should('have.value', 'Novato');
//Restart data
cy.selectOption(firstLineReason, 'Prisas');
@ -29,13 +31,16 @@ describe('ClaimDevelopment', () => {
});
it('should add and remove new line', () => {
//add row
cy.addCard();
cy.get(thirdRow).should('exist');
const rowData = [false, 'Novato', 'Roces', 'Compradores', 'employeeNick', 'Tour'];
cy.fillRow(thirdRow, rowData);
cy.saveCard();
cy.login('developer');
cy.visit(`/#/claim/${claimId}/development`);
cy.validateRow(thirdRow, rowData);
cy.reload();

View File

@ -54,7 +54,7 @@ Cypress.Commands.add('getValue', (selector) => {
else if ($el.find('.q-select__dropdown-icon').length) {
return cy.get(
selector +
'> .q-field > .q-field__inner > .q-field__control > .q-field__control-container > .q-field__native > span'
'> .q-field > .q-field__inner > .q-field__control > .q-field__control-container > .q-field__native > input'
);
} else {
// Puedes añadir un log o lanzar un error si el elemento no es reconocido
@ -76,7 +76,6 @@ Cypress.Commands.add('checkOption', (selector) => {
// Global buttons
Cypress.Commands.add('saveCard', () => {
cy.get('[title="Save"]').click();
cy.get('[title="Save"]').should('have.class', 'disabled');
});
Cypress.Commands.add('resetCard', () => {
cy.get('[title="Reset"]').click();
@ -123,7 +122,7 @@ Cypress.Commands.add('validateRow', (rowSelector, expectedValues) => {
cy.getValue(`:nth-child(${index + 1})`).should(`${prefix}be.checked`);
continue;
}
cy.getValue(`:nth-child(${index + 1})`).should('have.text', value);
cy.getValue(`:nth-child(${index + 1})`).should('have.value', value);
}
});
});