refs #6694 VnLocation #154

Merged
jsegarra merged 31 commits from 6280_vnLocation into dev 2024-01-29 13:23:04 +00:00
6 changed files with 204 additions and 228 deletions

View File

@ -58,7 +58,7 @@ module.exports = {
rules: {
'prefer-promise-reject-errors': 'off',
'no-unused-vars': 'warn',
"vue/no-multiple-template-root": "off" ,
// allow debugger during development only
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
},

View File

@ -0,0 +1,135 @@
<script setup>
import { ref, toRefs, computed, watch, onMounted } from 'vue';
import CreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue';
import VnSelectCreate from 'components/common/VnSelectCreate.vue';
import FetchData from 'components/FetchData.vue';
const emit = defineEmits(['update:modelValue', 'update:options']);
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
const postcodesOptions = ref([]);
const postcodesRef = ref(null);
const $props = defineProps({
modelValue: {
type: [String, Number, Object],
default: null,
},
options: {
type: Array,
default: () => [],
},
optionLabel: {
type: String,
default: '',
},
optionValue: {
type: String,
default: '',
},
filterOptions: {
type: Array,
default: () => [],
},
isClearable: {
type: Boolean,
default: true,
},
defaultFilter: {
type: Boolean,
default: true,
},
});
const { options } = toRefs($props);
const myOptions = ref([]);
const myOptionsOriginal = ref([]);
const value = computed({
get() {
return $props.modelValue;
},
set(value) {
emit('update:modelValue', value);
},
});
onMounted(() => {
locationFilter()
});
function setOptions(data) {
myOptions.value = JSON.parse(JSON.stringify(data));
myOptionsOriginal.value = JSON.parse(JSON.stringify(data));
}
setOptions(options.value);
watch(options, (newValue) => {
setOptions(newValue);
});
function showLabel(data) {
return `${data.code} - ${data.town}(${data.province}), ${data.country}`;
}
function locationFilter(search) {
jsegarra marked this conversation as resolved Outdated
Outdated
Review

No veo en que momento se hace el filtrado de datos a las tablas.

No veo en que momento se hace el filtrado de datos a las tablas.
let where = { search };
postcodesRef.value.fetch({filter:{ where}, limit: 30});
}
function handleFetch( data) {
postcodesOptions.value = data;
}
jsegarra marked this conversation as resolved Outdated
Outdated
Review

De esta manera te estas descargando toda la tabla

  • La tabla town tiene 71.691 registros.
  • La tabla postCode 271.259 registros.
  • ...

Por eso decia que no veia la funcion de filtrado. Yo no haría el filtrado en el front dado que te obliga a descargar todos los registros. Y en este caso lo veo inviable. (añadir limits tambien)

De esta manera te estas descargando toda la tabla - La tabla town tiene 71.691 registros. - La tabla postCode 271.259 registros. - ... Por eso decia que no veia la funcion de filtrado. Yo no haría el filtrado en el front dado que te obliga a descargar todos los registros. Y en este caso lo veo inviable. (añadir limits tambien)

Tendré en cuenta estos datos para hacer unos cambios con respecto al código original.
Gracias por la aportación.

Tendré en cuenta estos datos para hacer unos cambios con respecto al código original. Gracias por la aportación.
</script>
jsegarra marked this conversation as resolved Outdated
Outdated
Review

Si solo se usa una vez, llamar directamente a locationFilter

Si solo se usa una vez, llamar directamente a locationFilter

Por mantener el formato en el uso de este componente.
Pero se puede borrar.

Por mantener el formato en el uso de este componente. Pero se puede borrar.
<template>
<FetchData
jsegarra marked this conversation as resolved Outdated

Opcion A=> limit="30"

Opcion A=> limit="30"
ref="postcodesRef"
jsegarra marked this conversation as resolved Outdated
Outdated
Review

Sin auto-load y hacer con codigo per con limit
O con auto-load pero con limit

Sin auto-load y hacer con codigo per con **limit** O con auto-load pero con limit

El limit lo tienes definido en la línea 71, es correcto?

El limit lo tienes definido en la línea 71, es correcto?
Outdated
Review

Si y no. Eso sirve para las demás peticiones.
Pero no da más abrir la sección te carga toda la tabla (sin limit) al estar puesto el auto-load
(Lo he probado entrando en las ramas)

Si y no. Eso sirve para las demás peticiones. Pero no da más abrir la sección te carga toda la tabla (sin limit) al estar puesto el auto-load (Lo he probado entrando en las ramas)
url="Postcodes/filter"
@on-fetch="(data) =>handleFetch(data)"
/>
<VnSelectCreate
v-if="postcodesRef"
v-model="value"
:options="postcodesOptions"
:label="t('Location')"
:option-label="showLabel"
:placeholder="t('Search by postalCode, town, province or country')"
@input-value="locationFilter"
jsegarra marked this conversation as resolved Outdated
Outdated
Review

Igual se lo pondria mas alto, ver como esta en salix

Igual se lo pondria mas alto, ver como esta en salix

Corregido 6809e9bddb

Corregido 6809e9bddb37ba812c3e56a8f62f3beb255a051d
:default-filter="false"
:input-debounce="300"
:class="{ required: $attrs.required }"
v-bind="$attrs"
emit-value
map-options
use-input
clearable
hide-selected
fill-input
jsegarra marked this conversation as resolved Outdated
Outdated
Review

Com veus esta part? @jgallego
Es una reglat de filtrat que t'obliga a escriure al menys 3 caracters per a que te filtre.
No ho acabe de vore. Pq si per exemple lo que volen es filtrar per pais pq hi ha un pais q saben q soles tenen 1 codi postal o molt pocs. i fiquen per exemple "J" (pq estan buscant Japon). no els trauria res

Com veus esta part? @jgallego Es una reglat de filtrat que t'obliga a escriure al menys 3 caracters per a que te filtre. No ho acabe de vore. Pq si per exemple lo que volen es filtrar per pais pq hi ha un pais q saben q soles tenen 1 codi postal o molt pocs. i fiquen per exemple "J" (pq estan buscant Japon). no els trauria res

Si la consulta no es muy lenta/pesada yo no pondria nada.

Si la consulta no es muy lenta/pesada yo no pondria nada.

Es una reglat de filtrat que t'obliga a escriure al menys 3 caracters per a que te filtre.
No ho acabe de vore. Pq si per exemple lo que volen es filtrar per pais pq hi ha un pais q saben q soles tenen 1 codi postal o molt pocs. i fiquen per exemple "J" (pq estan buscant Japon). no els trauria res

Lo comentamos.

> Es una reglat de filtrat que t'obliga a escriure al menys 3 caracters per a que te filtre. > No ho acabe de vore. Pq si per exemple lo que volen es filtrar per pais pq hi ha un pais q saben q soles tenen 1 codi postal o molt pocs. i fiquen per exemple "J" (pq estan buscant Japon). no els trauria res > Lo comentamos.
>
<template #form>
<CreateNewPostcode @on-data-saved="locationFilter()" />
jsegarra marked this conversation as resolved Outdated
Outdated
Review

Si al final se fa la peticio en la back li possaria un delay (input-bounce) crec que se dia

Si al final se fa la peticio en la back li possaria un delay (input-bounce) crec que se dia

Lo comentamos

Lo comentamos

corregido 655c821075

corregido 655c8210751f819ad4aff1480f94e57f73e22931
</template>
<template #option="{itemProps, opt}">
<QItem v-bind="itemProps">
jsegarra marked this conversation as resolved Outdated
Outdated
Review

Y tampoco veo que se este usando el evento no?

Y tampoco veo que se este usando el evento no?

Por mantener el formato en el uso de este componente.
Diría que el valor de $event no se está gestionando de ninguna manera en el resto de instancias
En este caso se reemplaza por locationFilter() porque queremos los datos con otro formato

Por mantener el formato en el uso de este componente. Diría que el valor de $event no se está gestionando de ninguna manera en el resto de instancias En este caso se reemplaza por locationFilter() porque queremos los datos con otro formato
<QItemSection v-if="opt">
<QItemLabel>{{ opt.code }}</QItemLabel>
<QItemLabel caption>{{ showLabel(opt) }}</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelectCreate>
</template>
<style lang="scss" scoped>
.add-icon {
cursor: pointer;
background-color: $primary;
border-radius: 50px;
}
</style>
<i18n>
es:
Location: Ubicación
Search by postalcode, town, province or country: Buscar por código postal, ciudad o país
</i18n>

View File

@ -12,11 +12,11 @@ const $props = defineProps({
default: () => [],
},
optionLabel: {
type: String,
type: [String],
jsegarra marked this conversation as resolved Outdated
Outdated
Review

No veo el pq hacerlo function

No veo el pq hacerlo function
default: '',
},
filterOptions: {
type: Array,
type: [Array],
jsegarra marked this conversation as resolved Outdated
Outdated
Review

No veo el pq hacerlo function

No veo el pq hacerlo function
Outdated
Review

No veo el pq hacerlo function

No veo el pq hacerlo function
default: () => [],
},
isClearable: {
jsegarra marked this conversation as resolved Outdated
Outdated
Review

No veo que se use

No veo que se use
@ -47,6 +47,7 @@ function setOptions(data) {
myOptions.value = JSON.parse(JSON.stringify(data));
myOptionsOriginal.value = JSON.parse(JSON.stringify(data));
}
setOptions(options.value);
const filter = (val, options) => {
const search = val.toString().toLowerCase();

View File

@ -2,12 +2,11 @@
import { reactive, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import CustomerCreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue';
import FetchData from 'components/FetchData.vue';
import FormModel from 'components/FormModel.vue';
import VnRow from 'components/ui/VnRow.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
import VnSelectCreate from 'src/components/common/VnSelectCreate.vue';
import VnLocation from 'src/components/common/VnLocation.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
const { t } = useI18n();
@ -29,23 +28,18 @@ const newClientForm = reactive({
isEqualizated: false,
});
const postcodeFetchDataRef = ref(null);
const townsFetchDataRef = ref(null);
const workersOptions = ref([]);
const businessTypesOptions = ref([]);
const citiesLocationOptions = ref([]);
const provincesLocationOptions = ref([]);
const countriesOptions = ref([]);
const postcodesOptions = ref([]);
const onPostcodeCreated = async ({ code, provinceFk, townFk, countryFk }, formData) => {
await postcodeFetchDataRef.value.fetch();
await townsFetchDataRef.value.fetch();
formData.postcode = code;
formData.provinceFk = provinceFk;
formData.city = citiesLocationOptions.value.find((town) => town.id === townFk).name;
formData.countryFk = countryFk;
};
function handleLocation(data, location ) {
const { town, code, provinceFk, countryFk } = location ?? {}
data.postcode = code;
data.city = town;
data.provinceFk = provinceFk;
data.countryFk = countryFk;
}
</script>
<template>
@ -54,33 +48,11 @@ const onPostcodeCreated = async ({ code, provinceFk, townFk, countryFk }, formDa
auto-load
url="Workers/search?departmentCodes"
/>
<FetchData
ref="postcodeFetchDataRef"
url="Postcodes/location"
@on-fetch="(data) => (postcodesOptions = data)"
auto-load
/>
<FetchData
@on-fetch="(data) => (businessTypesOptions = data)"
auto-load
url="BusinessTypes"
/>
<FetchData
ref="townsFetchDataRef"
@on-fetch="(data) => (citiesLocationOptions = data)"
auto-load
url="Towns/location"
/>
<FetchData
@on-fetch="(data) => (provincesLocationOptions = data)"
auto-load
url="Provinces/location"
/>
<FetchData
@on-fetch="(data) => (countriesOptions = data)"
auto-load
url="Countries"
/>
<QPage>
<VnSubToolbar />
<FormModel
@ -139,96 +111,19 @@ const onPostcodeCreated = async ({ code, provinceFk, townFk, countryFk }, formDa
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelectCreate
v-model="data.postcode"
:label="t('Postcode')"
<VnLocation
:rules="validate('Worker.postcode')"
:roles-allowed-to-create="['deliveryAssistant']"
:options="postcodesOptions"
option-label="code"
option-value="code"
hide-selected
v-model="data.location"
@update:model-value="
(location) => handleLocation(data, location)
"
>
<template #form>
<CustomerCreateNewPostcode
@on-data-saved="onPostcodeCreated($event, data)"
/>
</template>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection v-if="scope.opt">
<QItemLabel>{{ scope.opt.code }}</QItemLabel>
<QItemLabel caption
>{{ scope.opt.code }} -
{{ scope.opt.town.name }} ({{
scope.opt.town.province.name
}},
{{
scope.opt.town.province.country.country
}})</QItemLabel
>
</QItemSection>
</QItem>
</template>
</VnSelectCreate>
</div>
<div class="col">
<!-- ciudades -->
<VnSelectFilter
:label="t('City')"
:options="citiesLocationOptions"
hide-selected
option-label="name"
option-value="name"
v-model="data.city"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>{{ scope.opt.name }}</QItemLabel>
<QItemLabel caption>
{{
`${scope.opt.name}, ${scope.opt.province.name} (${scope.opt.province.country.country})`
}}
</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelectFilter>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelectFilter
:label="t('Province')"
:options="provincesLocationOptions"
hide-selected
option-label="name"
option-value="id"
v-model="data.provinceFk"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>{{
`${scope.opt.name} (${scope.opt.country.country})`
}}</QItemLabel>
</QItemSection>
</QItem>
</template>
</VnSelectFilter>
</div>
<div class="col">
<VnSelectFilter
:label="t('Country')"
:options="countriesOptions"
hide-selected
option-label="country"
option-value="id"
v-model="data.countryFk"
/>
</VnLocation>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QInput v-model="data.userName" :label="t('Web user')" />

View File

@ -9,7 +9,8 @@ import VnInputDate from 'components/common/VnInputDate.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
import VnSelectCreate from 'src/components/common/VnSelectCreate.vue';
import CreateBankEntityForm from 'src/components/CreateBankEntityForm.vue';
import CreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue';
import CustomerCreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue';
import VnLocation from 'src/components/common/VnLocation.vue';
import VnInput from 'src/components/common/VnInput.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
@ -21,14 +22,6 @@ const workerConfigFilter = {
field: ['payMethodFk'],
};
const provincesFilter = {
fields: ['id', 'name', 'countryFk'],
};
const townsFilter = {
fields: ['id', 'name', 'provinceFk'],
};
const newWorkerForm = ref({
companyFk: null,
payMethodFk: null,
@ -49,10 +42,6 @@ const newWorkerForm = ref({
bankEntityFk: null,
});
const postcodeFetchDataRef = ref(null);
const townsFetchDataRef = ref(null);
const provincesOptions = ref([]);
const townsOptions = ref([]);
const companiesOptions = ref([]);
const workersOptions = ref([]);
const payMethodsOptions = ref([]);
@ -67,13 +56,14 @@ const onBankEntityCreated = (data) => {
bankEntitiesOptions.value.push(data);
};
const onPostcodeCreated = async ({ code, provinceFk, townFk }, formData) => {
await postcodeFetchDataRef.value.fetch();
await townsFetchDataRef.value.fetch();
formData.postcode = code;
formData.provinceFk = provinceFk;
formData.city = townsOptions.value.find((town) => town.id === townFk).name;
};
function handleLocation(data, location ) {
const { town, postcode: code, provinceFk, countryFk } = location ?? {}
data.postcode = code;
data.city = town;
data.provinceFk = provinceFk;
data.countryFk = countryFk;
}
onMounted(async () => {
const userInfo = await useUserConfig().fetch();
@ -88,25 +78,7 @@ onMounted(async () => {
:filter="workerConfigFilter"
auto-load
/>
<FetchData
ref="postcodeFetchDataRef"
url="Postcodes/location"
@on-fetch="(data) => (postcodesOptions = data)"
auto-load
/>
<FetchData
url="Provinces/location"
@on-fetch="(data) => (provincesOptions = data)"
:filter="provincesFilter"
auto-load
/>
<FetchData
ref="townsFetchDataRef"
url="Towns/location"
@on-fetch="(data) => (townsOptions = data)"
:filter="townsFilter"
auto-load
/>
<FetchData
url="Companies"
@on-fetch="(data) => (companiesOptions = data)"
@ -184,77 +156,19 @@ onMounted(async () => {
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelectCreate
v-model="data.postcode"
:label="t('worker.create.postcode')"
<VnLocation
:rules="validate('Worker.postcode')"
:roles-allowed-to-create="['deliveryAssistant']"
:options="postcodesOptions"
option-label="code"
option-value="code"
hide-selected
>
<template #form>
<CreateNewPostcode
@on-data-saved="onPostcodeCreated($event)"
/>
</template>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection v-if="scope.opt">
<QItemLabel>{{ scope.opt.code }}</QItemLabel>
<QItemLabel caption
>{{ scope.opt.code }} -
{{ scope.opt.town.name }} ({{
scope.opt.town.province.name
}},
{{
scope.opt.town.province.country.country
}})</QItemLabel
>
</QItemSection>
</QItem>
</template>
</VnSelectCreate>
</div>
<div class="col">
<VnSelectFilter
:label="t('worker.create.province')"
v-model="data.provinceFk"
:options="provincesOptions"
option-value="id"
option-label="name"
hide-selected
:rules="validate('Worker.provinceFk')"
/>
v-model="data.location"
@update:model-value="
(location) => handleLocation(data, location)
"
>
</VnLocation>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelectFilter
:label="t('worker.create.city')"
v-model="data.city"
:options="townsOptions"
option-value="name"
option-label="name"
hide-selected
:rules="validate('Worker.city')"
>
<template #option="scope">
<QItem v-bind="scope.itemProps">
<QItemSection>
<QItemLabel>{{ scope.opt.name }}</QItemLabel>
<QItemLabel caption
>{{ scope.opt.name }},
{{ scope.opt.province.name }} ({{
scope.opt.province.country.country
}})</QItemLabel
>
</QItemSection>
</QItem>
</template>
</VnSelectFilter>
</div>
<div class="col">
<VnInput
:label="t('worker.create.street')"

View File

@ -0,0 +1,31 @@
const inputLocation = ':nth-child(3) > :nth-child(1) > .q-field > .q-field__inner > .q-field__control';
const locationOptions ='[role="listbox"] > div.q-virtual-scroll__content > .q-item'
describe('VnLocation', () => {
beforeEach(() => {
cy.viewport(1280, 720);
cy.login('developer');
cy.visit('/#/worker/create');
cy.waitForElement('.q-card');
});
it('Show all options', function() {
jsegarra marked this conversation as resolved Outdated
Outdated
Review

En vez de comentarlo, quitarlo

En vez de comentarlo, quitarlo
Outdated
Review

En vez de comentarlo, quitarlo

En vez de comentarlo, quitarlo
Outdated
Review

En vez de comentarlo, quitarlo

❗En vez de comentarlo, quitarlo
cy.get(inputLocation).click();
cy.get(locationOptions).should('have.length',5);
});
it('input filter location as "al"', function() {
cy.get(inputLocation).click();
cy.get(inputLocation).clear();
cy.get(inputLocation).type('al');
cy.get(locationOptions).should('have.length',3);
});
it('input filter location as "ecuador"', function() {
cy.get(inputLocation).click();
cy.get(inputLocation).clear();
cy.get(inputLocation).type('ecuador');
cy.get(locationOptions).should('have.length',1);
cy.get(`${locationOptions}:nth-child(1)`).click();
cy.get(':nth-child(3) > :nth-child(1) > .q-field > .q-field__inner > .q-field__control > :nth-child(2) > .q-icon').click();
});
})