Merge pull request 'fix_city_from_province' (!1032) from fix_city_from_province into dev
gitea/salix-front/pipeline/head This commit looks good Details

Reviewed-on: #1032
Reviewed-by: Jon Elias <jon@verdnatura.es>
This commit is contained in:
Javier Segarra 2024-12-02 08:50:08 +00:00
commit bd95f8877f
7 changed files with 117 additions and 91 deletions

View File

@ -40,6 +40,7 @@ const onDataSaved = (...args) => {
url-create="towns" url-create="towns"
model="city" model="city"
@on-data-saved="onDataSaved" @on-data-saved="onDataSaved"
data-cy="newCityForm"
> >
<template #form-inputs="{ data, validate }"> <template #form-inputs="{ data, validate }">
<VnRow> <VnRow>
@ -48,12 +49,14 @@ const onDataSaved = (...args) => {
v-model="data.name" v-model="data.name"
:rules="validate('city.name')" :rules="validate('city.name')"
required required
data-cy="cityName"
/> />
<VnSelectProvince <VnSelectProvince
:province-selected="$props.provinceSelected" :province-selected="$props.provinceSelected"
:country-fk="$props.countryFk" :country-fk="$props.countryFk"
v-model="data.provinceFk" v-model="data.provinceFk"
required required
data-cy="provinceCity"
/> />
</VnRow> </VnRow>
</template> </template>

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { computed, reactive, ref } from 'vue'; import { reactive, ref } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue'; import FetchData from 'components/FetchData.vue';
@ -21,14 +21,14 @@ const postcodeFormData = reactive({
provinceFk: null, provinceFk: null,
townFk: null, townFk: null,
}); });
const townsFetchDataRef = ref(false); const townsFetchDataRef = ref(false);
const townFilter = ref({});
const countriesRef = ref(false); const countriesRef = ref(false);
const townsRef = ref(false);
const provincesFetchDataRef = ref(false); const provincesFetchDataRef = ref(false);
const provincesOptions = ref([]); const provincesOptions = ref([]);
const townsOptions = ref([]);
const town = ref({}); const town = ref({});
const townFilter = ref({});
const countryFilter = ref({}); const countryFilter = ref({});
function onDataSaved(formData) { function onDataSaved(formData) {
@ -48,6 +48,49 @@ function onDataSaved(formData) {
emit('onDataSaved', newPostcode); emit('onDataSaved', newPostcode);
} }
async function setCountry(countryFk, data) {
data.townFk = null;
data.provinceFk = null;
data.countryFk = countryFk;
await fetchTowns();
}
// Province
async function handleProvinces(data) {
provincesOptions.value = data;
if (postcodeFormData.countryFk) {
await fetchTowns();
}
}
async function setProvince(id, data) {
if (data.provinceFk === id) return;
const newProvince = provincesOptions.value.find((province) => province.id == id);
if (newProvince) data.countryFk = newProvince.countryFk;
postcodeFormData.provinceFk = id;
await fetchTowns();
}
async function onProvinceCreated(data) {
await provincesFetchDataRef.value.fetch({
where: { countryFk: postcodeFormData.countryFk },
});
postcodeFormData.provinceFk = data.id;
}
function provinceByCountry(countryFk = postcodeFormData.countryFk) {
return provincesOptions.value
.filter((province) => province.countryFk === countryFk)
.map(({ id }) => id);
}
// Town
async function handleTowns(data) {
townsOptions.value = data;
}
function setTown(newTown, data) {
town.value = newTown;
data.provinceFk = newTown?.provinceFk ?? newTown;
data.countryFk = newTown?.province?.countryFk ?? newTown;
}
async function onCityCreated(newTown, formData) { async function onCityCreated(newTown, formData) {
await provincesFetchDataRef.value.fetch(); await provincesFetchDataRef.value.fetch();
newTown.province = provincesOptions.value.find( newTown.province = provincesOptions.value.find(
@ -56,44 +99,29 @@ async function onCityCreated(newTown, formData) {
formData.townFk = newTown; formData.townFk = newTown;
setTown(newTown, formData); setTown(newTown, formData);
} }
async function fetchTowns(countryFk = postcodeFormData.countryFk) {
function setTown(newTown, data) { if (!countryFk) return;
town.value = newTown; const provinces = postcodeFormData.provinceFk
data.provinceFk = newTown?.provinceFk ?? newTown; ? [postcodeFormData.provinceFk]
data.countryFk = newTown?.province?.countryFk ?? newTown; : provinceByCountry();
} townFilter.value.where = {
async function setCountry(countryFk, data) {
data.townFk = null;
data.provinceFk = null;
data.countryFk = countryFk;
}
async function handleProvinces(data) {
provincesOptions.value = data;
}
async function setProvince(id, data) {
const newProvince = provincesOptions.value.find((province) => province.id == id);
if (!newProvince) return;
data.countryFk = newProvince.countryFk;
}
async function onProvinceCreated(data) {
await provincesFetchDataRef.value.fetch({
where: { countryFk: postcodeFormData.countryFk },
});
postcodeFormData.provinceFk = data.id;
}
const whereTowns = computed(() => {
return {
provinceFk: { provinceFk: {
inq: provincesOptions.value.map(({ id }) => id), inq: provinces,
}, },
}; };
}); await townsFetchDataRef.value?.fetch();
}
async function filterTowns(name) {
if (name !== '') {
townFilter.value.where = {
name: {
like: `%${name}%`,
},
};
await townsFetchDataRef.value?.fetch();
}
}
</script> </script>
<template> <template>
@ -105,6 +133,15 @@ const whereTowns = computed(() => {
auto-load auto-load
url="Provinces/location" url="Provinces/location"
/> />
<FetchData
ref="townsFetchDataRef"
:sort-by="['name ASC']"
:limit="30"
:filter="townFilter"
@on-fetch="handleTowns"
auto-load
url="Towns/location"
/>
<FormModelPopup <FormModelPopup
url-create="postcodes" url-create="postcodes"
@ -123,25 +160,22 @@ const whereTowns = computed(() => {
:rules="validate('postcode.code')" :rules="validate('postcode.code')"
clearable clearable
required required
data-cy="locationPostcode"
/> />
<VnSelectDialog <VnSelectDialog
ref="townsRef"
:sort-by="['name ASC']"
:limit="30"
auto-load
url="Towns/location"
:where="whereTowns"
:label="t('City')" :label="t('City')"
@update:model-value="(value) => setTown(value, data)" @update:model-value="(value) => setTown(value, data)"
@filter="filterTowns"
:tooltip="t('Create city')" :tooltip="t('Create city')"
v-model="data.townFk" v-model="data.townFk"
:options="townsOptions"
option-label="name" option-label="name"
option-value="id" option-value="id"
:rules="validate('postcode.city')" :rules="validate('postcode.city')"
:acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]" :acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]"
:emit-value="false" :emit-value="false"
:clearable="true"
required required
data-cy="locationTown"
> >
<template #option="{ itemProps, opt }"> <template #option="{ itemProps, opt }">
<QItem v-bind="itemProps"> <QItem v-bind="itemProps">
@ -172,7 +206,6 @@ const whereTowns = computed(() => {
:province-selected="data.provinceFk" :province-selected="data.provinceFk"
@update:model-value="(value) => setProvince(value, data)" @update:model-value="(value) => setProvince(value, data)"
v-model="data.provinceFk" v-model="data.provinceFk"
@on-province-fetched="handleProvinces"
@on-province-created="onProvinceCreated" @on-province-created="onProvinceCreated"
required required
/> />
@ -191,6 +224,7 @@ const whereTowns = computed(() => {
v-model="data.countryFk" v-model="data.countryFk"
:rules="validate('postcode.countryFk')" :rules="validate('postcode.countryFk')"
@update:model-value="(value) => setCountry(value, data)" @update:model-value="(value) => setCountry(value, data)"
data-cy="locationCountry"
/> />
</VnRow> </VnRow>
</template> </template>

View File

@ -53,8 +53,10 @@ const where = computed(() => {
v-model="data.name" v-model="data.name"
:rules="validate('province.name')" :rules="validate('province.name')"
required required
data-cy="provinceName"
/> />
<VnSelect <VnSelect
data-cy="autonomyProvince"
required required
ref="autonomiesRef" ref="autonomiesRef"
auto-load auto-load

View File

@ -64,11 +64,11 @@ watch(
auto-load auto-load
/> />
<VnSelectDialog <VnSelectDialog
data-cy="locationProvince"
:label="t('Province')" :label="t('Province')"
:options="provincesOptions" :options="provincesOptions"
:tooltip="t('Create province')" :tooltip="t('Create province')"
hide-selected hide-selected
:clearable="true"
v-model="provinceFk" v-model="provinceFk"
:rules="validate && validate('postcode.provinceFk')" :rules="validate && validate('postcode.provinceFk')"
:acls="[{ model: 'Province', props: '*', accessType: 'WRITE' }]" :acls="[{ model: 'Province', props: '*', accessType: 'WRITE' }]"

View File

@ -75,7 +75,6 @@ const handleModelValue = (data) => {
:input-debounce="300" :input-debounce="300"
:class="{ required: isRequired }" :class="{ required: isRequired }"
v-bind="$attrs" v-bind="$attrs"
clearable
:emit-value="false" :emit-value="false"
:tooltip="t('Create new location')" :tooltip="t('Create new location')"
:rules="mixinRules" :rules="mixinRules"

View File

@ -43,6 +43,7 @@ const isAllowedToCreate = computed(() => {
> >
<template v-if="isAllowedToCreate" #append> <template v-if="isAllowedToCreate" #append>
<QIcon <QIcon
:data-cy="$attrs.dataCy ?? $attrs.label + '_icon'"
@click.stop.prevent="$refs.dialog.show()" @click.stop.prevent="$refs.dialog.show()"
:name="actionIcon" :name="actionIcon"
:size="actionIcon === 'add' ? 'xs' : 'sm'" :size="actionIcon === 'add' ? 'xs' : 'sm'"

View File

@ -43,11 +43,9 @@ describe('VnLocation', () => {
province province
); );
cy.get( cy.get(
`${createForm.prefix} > :nth-child(4) > .q-select > ${createForm.sufix} > :nth-child(3) > .q-icon` `${createForm.prefix} > :nth-child(4) > .q-select > ${createForm.sufix} > :nth-child(3) `
).click(); ).click();
cy.get( cy.dataCy('locationProvince').should('have.value', province);
`#q-portal--dialog--5 > .q-dialog > ${createForm.prefix} > .vn-row > .q-select > ${createForm.sufix} > :nth-child(1) input`
).should('have.value', province);
}); });
}); });
describe('Worker Create', () => { describe('Worker Create', () => {
@ -123,32 +121,16 @@ describe('VnLocation', () => {
const province = randomString({ length: 4 }); const province = randomString({ length: 4 });
cy.get(createLocationButton).click(); cy.get(createLocationButton).click();
cy.get(dialogInputs).eq(0).type(postCode); cy.get(dialogInputs).eq(0).type(postCode);
cy.get( cy.dataCy('City_icon').click();
`${createForm.prefix} > :nth-child(4) > .q-select > ${createForm.sufix} > :nth-child(2) > .q-icon` cy.selectOption('[data-cy="locationProvince"]:last', 'Province one');
).click(); cy.dataCy('cityName').type(province);
cy.selectOption('#q-portal--dialog--3 .q-select', 'one'); cy.dataCy('FormModelPopup_save').eq(1).click();
cy.get('#q-portal--dialog--3 .q-input').type(province); cy.dataCy('FormModelPopup_save').eq(0).click();
cy.get('#q-portal--dialog--3 .q-btn--standard').click();
cy.get('#q-portal--dialog--1 .q-btn--standard').click();
cy.waitForElement('.q-form'); cy.waitForElement('.q-form');
checkVnLocation(postCode, province); checkVnLocation(postCode, province);
}); });
it('Create province without country', () => {
const provinceName = 'Saskatchew'.concat(Math.random(1 * 100));
cy.get(createLocationButton).click();
cy.get(
`${createForm.prefix} > :nth-child(5) > .q-select > ${createForm.sufix} > :nth-child(2) `
)
.eq(0)
.click();
cy.selectOption('#q-portal--dialog--3 .q-select', 'one');
cy.countSelectOptions('#q-portal--dialog--3 .q-select', 4);
cy.get('#q-portal--dialog--3 .q-input').type(provinceName);
cy.get('#q-portal--dialog--3 .q-btn--standard').click();
});
it('Create city with country', () => { it('Create city with country', () => {
const cityName = 'Saskatchew'.concat(Math.random(1 * 100)); const cityName = 'Saskatchew'.concat(Math.random(1 * 100));
cy.get(createLocationButton).click(); cy.get(createLocationButton).click();
@ -156,14 +138,23 @@ describe('VnLocation', () => {
`${createForm.prefix} > :nth-child(5) > :nth-child(3) `, `${createForm.prefix} > :nth-child(5) > :nth-child(3) `,
'Italia' 'Italia'
); );
cy.get( cy.dataCy('City_icon').click();
`${createForm.prefix} > :nth-child(4) > .q-select > ${createForm.sufix} > :nth-child(2) > .q-icon` cy.selectOption('[data-cy="locationProvince"]:last', 'Province four');
).click(); cy.countSelectOptions('[data-cy="locationProvince"]:last', 1);
cy.selectOption('#q-portal--dialog--4 .q-select', 'Province four');
cy.countSelectOptions('#q-portal--dialog--4 .q-select', 1);
cy.get('#q-portal--dialog--4 .q-input').type(cityName); cy.dataCy('cityName').type(cityName);
cy.get('#q-portal--dialog--4 .q-btn--standard').click(); cy.dataCy('FormModelPopup_save').eq(1).click();
});
it('Create province without country', () => {
const provinceName = 'Saskatchew'.concat(Math.random(1 * 100));
cy.get(createLocationButton).click();
cy.dataCy('Province_icon').click();
cy.selectOption('[data-cy="autonomyProvince"] ', 'Autonomy one');
cy.countSelectOptions('[data-cy="autonomyProvince"]', 4);
cy.dataCy('provinceName').type(provinceName);
cy.dataCy('FormModelPopup_save').eq(1).click();
}); });
it('Create province with country', () => { it('Create province with country', () => {
@ -173,17 +164,13 @@ describe('VnLocation', () => {
`${createForm.prefix} > :nth-child(5) > :nth-child(3) `, `${createForm.prefix} > :nth-child(5) > :nth-child(3) `,
'España' 'España'
); );
cy.get( cy.dataCy('Province_icon').click();
`${createForm.prefix} > :nth-child(5) > .q-select > ${createForm.sufix} > :nth-child(2) `
)
.eq(0)
.click();
cy.selectOption('#q-portal--dialog--4 .q-select', 'one'); cy.selectOption('[data-cy="autonomyProvince"] ', 'Autonomy one');
cy.countSelectOptions('#q-portal--dialog--4 .q-select', 2); cy.countSelectOptions('[data-cy="autonomyProvince"]', 2);
cy.get('#q-portal--dialog--4 .q-input').type(provinceName); cy.dataCy('provinceName').type(provinceName);
cy.get('#q-portal--dialog--4 .q-btn--standard').click(); cy.dataCy('FormModelPopup_save').eq(1).click();
}); });
function checkVnLocation(postCode, province) { function checkVnLocation(postCode, province) {