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 139 additions and 35 deletions
Showing only changes of commit ff347db37a - Show all commits

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(() => {
myOptions.value = filter(val, myOptionsOriginal.value);
});
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

@ -20,7 +20,8 @@ describe('ClaimDevelopment', () => {
cy.selectOption(firstLineReason, 'Novato');
cy.saveCard();
cy.reload();
cy.login('developer');
cy.visit(`/#/claim/${claimId}/development`);
cy.getValue(firstLineReason).should('have.text', 'Novato');
//Restart data
@ -29,13 +30,16 @@ describe('ClaimDevelopment', () => {
});
it('should add and remove new line', () => {
cy.login('developer');
cy.visit(`/#/claim/${claimId}/development`);
//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

@ -52,6 +52,13 @@ Cypress.Commands.add('getValue', (selector) => {
}
// Si es un QSelect
else if ($el.find('.q-select__dropdown-icon').length) {
cy.log(
selector,
cy.get(
selector +
'> .q-field > .q-field__inner > .q-field__control > .q-field__control-container > .q-field__native'
)
);
return cy.get(
selector +
'> .q-field > .q-field__inner > .q-field__control > .q-field__control-container > .q-field__native > span'