#8113 create arrayDataStore map #979

Open
jorgep wants to merge 16 commits from 8113-preventDuplicateRecords into dev
24 changed files with 526 additions and 91 deletions
Showing only changes of commit e1dd81bc0f - Show all commits

View File

@ -1,4 +1,7 @@
const { defineConfig } = require('cypress'); const { defineConfig } = require('cypress');
// https://docs.cypress.io/app/tooling/reporters
// https://docs.cypress.io/app/references/configuration
// https://www.npmjs.com/package/cypress-mochawesome-reporter
module.exports = defineConfig({ module.exports = defineConfig({
e2e: { e2e: {
@ -16,6 +19,7 @@ module.exports = defineConfig({
reporterOptions: { reporterOptions: {
charts: true, charts: true,
reportPageTitle: 'Cypress Inline Reporter', reportPageTitle: 'Cypress Inline Reporter',
reportFilename: '[status]_[datetime]-report',
embeddedScreenshots: true, embeddedScreenshots: true,
reportDir: 'test/cypress/reports', reportDir: 'test/cypress/reports',
inlineAssets: true, inlineAssets: true,

View File

@ -17,10 +17,6 @@ const $props = defineProps({
type: Number, type: Number,
default: null, default: null,
}, },
provinces: {
type: Array,
default: () => [],
},
}); });
const { t } = useI18n(); const { t } = useI18n();
@ -56,7 +52,6 @@ const onDataSaved = (...args) => {
:province-selected="$props.provinceSelected" :province-selected="$props.provinceSelected"
:country-fk="$props.countryFk" :country-fk="$props.countryFk"
v-model="data.provinceFk" v-model="data.provinceFk"
:provinces="$props.provinces"
/> />
</VnRow> </VnRow>
</template> </template>

View File

@ -56,10 +56,10 @@ async function onCityCreated(newTown, formData) {
} }
function setTown(newTown, data) { function setTown(newTown, data) {
if (!newTown) return;
town.value = newTown;
data.provinceFk = newTown.provinceFk; data.provinceFk = newTown.provinceFk;
data.countryFk = newTown.province.countryFk; data.countryFk = newTown.province.countryFk;
if (!newTown) return;
town.value = newTown;
} }
async function setProvince(id, data) { async function setProvince(id, data) {
@ -73,7 +73,7 @@ async function onProvinceCreated(data) {
await provincesFetchDataRef.value.fetch({ await provincesFetchDataRef.value.fetch({
where: { countryFk: postcodeFormData.countryFk }, where: { countryFk: postcodeFormData.countryFk },
}); });
postcodeFormData.provinceFk.value = data.id; postcodeFormData.provinceFk = data.id;
} }
watch( watch(
@ -90,22 +90,33 @@ watch(
postcodeFormData.townFk = null; postcodeFormData.townFk = null;
} }
if (oldValueFk !== newCountryFk) { if (oldValueFk !== newCountryFk) {
await provincesFetchDataRef.value.fetch({ await fetchProvinces(newCountryFk);
where: { await fetchTowns(newCountryFk);
countryFk: newCountryFk, }
}, }
}); );
await townsFetchDataRef.value.fetch({ async function fetchTowns(countryFk) {
const townsFilter = countryFk
? {
where: { where: {
provinceFk: { provinceFk: {
inq: provincesOptions.value.map(({ id }) => id), inq: provincesOptions.value.map(({ id }) => id),
}, },
}, },
});
} }
: {};
await townsFetchDataRef.value.fetch(townsFilter);
}
async function fetchProvinces(countryFk) {
const provincesFilter = countryFk
? {
where: {
countryFk: countryFk,
},
}
: {};
await provincesFetchDataRef.value.fetch(provincesFilter);
} }
);
watch( watch(
() => postcodeFormData.provinceFk, () => postcodeFormData.provinceFk,
async (newProvinceFk, oldValueFk) => { async (newProvinceFk, oldValueFk) => {
@ -147,6 +158,13 @@ async function handleCountries(data) {
auto-load auto-load
url="Towns/location" url="Towns/location"
/> />
<FetchData
ref="CountriesFetchDataRef"
:sort-by="['name ASC']"
@on-fetch="handleCountries"
auto-load
url="Countries"
/>
<FormModelPopup <FormModelPopup
url-create="postcodes" url-create="postcodes"
@ -193,7 +211,6 @@ async function handleCountries(data) {
<CreateNewCityForm <CreateNewCityForm
:country-fk="data.countryFk" :country-fk="data.countryFk"
:province-selected="data.provinceFk" :province-selected="data.provinceFk"
:provinces="provincesOptions"
@on-data-saved=" @on-data-saved="
(_, requestResponse) => (_, requestResponse) =>
onCityCreated(requestResponse, data) onCityCreated(requestResponse, data)
@ -209,14 +226,12 @@ async function handleCountries(data) {
@update:model-value="(value) => setProvince(value, data)" @update:model-value="(value) => setProvince(value, data)"
v-model="data.provinceFk" v-model="data.provinceFk"
:clearable="true" :clearable="true"
:provinces="provincesOptions"
@on-province-created="onProvinceCreated" @on-province-created="onProvinceCreated"
/> />
<VnSelect <VnSelect
url="Countries"
:sort-by="['name ASC']"
:label="t('Country')" :label="t('Country')"
@update:options="handleCountries" @update:options="handleCountries"
:options="countriesOptions"
hide-selected hide-selected
option-label="name" option-label="name"
option-value="id" option-value="id"

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { reactive, ref } from 'vue'; import { computed, 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';
@ -34,6 +34,12 @@ const onDataSaved = (dataSaved, requestResponse) => {
); );
emit('onDataSaved', dataSaved, requestResponse); emit('onDataSaved', dataSaved, requestResponse);
}; };
const where = computed(() => {
if (!$props.countryFk) {
return {};
}
return { countryFk: $props.countryFk };
});
</script> </script>
<template> <template>
@ -41,9 +47,7 @@ const onDataSaved = (dataSaved, requestResponse) => {
@on-fetch="(data) => (autonomiesOptions = data)" @on-fetch="(data) => (autonomiesOptions = data)"
auto-load auto-load
:filter="{ :filter="{
where: { where,
countryFk: $props.countryFk,
},
}" }"
url="Autonomies/location" url="Autonomies/location"
:sort-by="['name ASC']" :sort-by="['name ASC']"

View File

@ -91,6 +91,10 @@ const $props = defineProps({
type: Boolean, type: Boolean,
default: true, default: true,
}, },
maxWidth: {
type: [String, Boolean],
default: '800px',
},
}); });
const emit = defineEmits(['onFetch', 'onDataSaved']); const emit = defineEmits(['onFetch', 'onDataSaved']);
const modelValue = computed( const modelValue = computed(
@ -283,6 +287,7 @@ defineExpose({
@submit="save" @submit="save"
@reset="reset" @reset="reset"
class="q-pa-md" class="q-pa-md"
:style="maxWidth ? 'max-width: ' + maxWidth : ''"
id="formModel" id="formModel"
> >
<QCard> <QCard>
@ -371,7 +376,6 @@ defineExpose({
color: black; color: black;
} }
#formModel { #formModel {
max-width: 800px;
width: 100%; width: 100%;
} }

View File

@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n';
const emit = defineEmits(['onSubmit']); const emit = defineEmits(['onSubmit']);
defineProps({ const $props = defineProps({
title: { title: {
type: String, type: String,
default: '', default: '',
@ -25,16 +25,21 @@ defineProps({
type: String, type: String,
default: '', default: '',
}, },
submitOnEnter: {
type: Boolean,
default: true,
},
}); });
const { t } = useI18n(); const { t } = useI18n();
const closeButton = ref(null); const closeButton = ref(null);
const isLoading = ref(false); const isLoading = ref(false);
const onSubmit = () => { const onSubmit = () => {
if ($props.submitOnEnter) {
emit('onSubmit'); emit('onSubmit');
closeForm(); closeForm();
}
}; };
const closeForm = () => { const closeForm = () => {

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { ref } from 'vue'; import { ref, watch } from 'vue';
import { useValidator } from 'src/composables/useValidator'; import { useValidator } from 'src/composables/useValidator';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
@ -17,20 +17,23 @@ const $props = defineProps({
type: Number, type: Number,
default: null, default: null,
}, },
provinces: {
type: Array,
default: () => [],
},
}); });
const provinceFk = defineModel({ type: Number, default: null }); const provinceFk = defineModel({ type: Number, default: null });
const { validate } = useValidator(); const { validate } = useValidator();
const { t } = useI18n(); const { t } = useI18n();
const filter = ref({
include: { relation: 'country' },
where: {
countryFk: $props.countryFk,
},
});
const provincesOptions = ref($props.provinces); const provincesOptions = ref($props.provinces);
provinceFk.value = $props.provinceSelected;
const provincesFetchDataRef = ref(); const provincesFetchDataRef = ref();
provinceFk.value = $props.provinceSelected;
if (!$props.countryFk) {
filter.value.where = {};
}
async function onProvinceCreated(_, data) { async function onProvinceCreated(_, data) {
await provincesFetchDataRef.value.fetch({ where: { countryFk: $props.countryFk } }); await provincesFetchDataRef.value.fetch({ where: { countryFk: $props.countryFk } });
provinceFk.value = data.id; provinceFk.value = data.id;
@ -39,23 +42,29 @@ async function onProvinceCreated(_, data) {
async function handleProvinces(data) { async function handleProvinces(data) {
provincesOptions.value = data; provincesOptions.value = data;
} }
watch(
() => $props.countryFk,
async () => {
if ($props.countryFk) {
filter.value.where.countryFk = $props.countryFk;
} else filter.value.where = {};
await provincesFetchDataRef.value.fetch({});
}
);
</script> </script>
<template> <template>
<FetchData <FetchData
ref="provincesFetchDataRef" ref="provincesFetchDataRef"
:filter="{ :filter="filter"
include: { relation: 'country' },
where: {
countryFk: $props.countryFk,
},
}"
@on-fetch="handleProvinces" @on-fetch="handleProvinces"
url="Provinces" url="Provinces"
auto-load
/> />
<VnSelectDialog <VnSelectDialog
:label="t('Province')" :label="t('Province')"
:options="$props.provinces" :options="provincesOptions"
:tooltip="t('Create province')" :tooltip="t('Create province')"
hide-selected hide-selected
v-model="provinceFk" v-model="provinceFk"

View File

@ -93,6 +93,7 @@ const mixinRules = [
:rules="mixinRules" :rules="mixinRules"
:lazy-rules="true" :lazy-rules="true"
hide-bottom-space hide-bottom-space
:data-cy="$attrs.dataCy ?? $attrs.label + '_input'"
> >
<template v-if="$slots.prepend" #prepend> <template v-if="$slots.prepend" #prepend>
<slot name="prepend" /> <slot name="prepend" />

View File

@ -306,6 +306,7 @@ function handleKeyDown(event) {
:input-debounce="useURL ? '300' : '0'" :input-debounce="useURL ? '300' : '0'"
:loading="isLoading" :loading="isLoading"
@virtual-scroll="onScroll" @virtual-scroll="onScroll"
:data-cy="$attrs.dataCy ?? $attrs.label + '_select'"
> >
<template v-if="isClearable" #append> <template v-if="isClearable" #append>
<QIcon <QIcon

View File

@ -132,10 +132,24 @@ const addFilter = async (filter, params) => {
async function fetch(params) { async function fetch(params) {
useArrayData(props.dataKey, params); useArrayData(props.dataKey, params);
arrayData.reset(['filter.skip', 'skip']); arrayData.reset(['filter.skip', 'skip', 'page']);
await arrayData.fetch({ append: false }); await arrayData.fetch({ append: false });
if (!store.hasMoreData) isLoading.value = false; return emitStoreData();
}
async function update(params) {
useArrayData(props.dataKey, params);
const { limit, skip } = store;
store.limit = limit + skip;
store.skip = 0;
await arrayData.fetch({ append: false });
store.limit = limit;
store.skip = skip;
return emitStoreData();
}
function emitStoreData() {
if (!store.hasMoreData) isLoading.value = false;
emit('onFetch', store.data); emit('onFetch', store.data);
return store.data; return store.data;
} }
@ -181,7 +195,7 @@ async function onLoad(index, done) {
done(isDone); done(isDone);
} }
defineExpose({ fetch, addFilter, paginate }); defineExpose({ fetch, update, addFilter, paginate });
</script> </script>
<template> <template>

View File

@ -9,7 +9,7 @@ const token = getTokenMultimedia();
export async function downloadFile(id, model = 'dms', urlPath = '/downloadFile', url) { export async function downloadFile(id, model = 'dms', urlPath = '/downloadFile', url) {
const appUrl = (await getUrl('', 'lilium')).replace('/#/', ''); const appUrl = (await getUrl('', 'lilium')).replace('/#/', '');
const response = await axios.get( const response = await axios.get(
url ?? `${appUrl}/${model}/${id}${urlPath}?access_token=${token}`, url ?? `${appUrl}/api/${model}/${id}${urlPath}?access_token=${token}`,
{ responseType: 'blob' } { responseType: 'blob' }
); );

View File

@ -298,6 +298,7 @@ globals:
clientsActionsMonitor: Clients and actions clientsActionsMonitor: Clients and actions
serial: Serial serial: Serial
medical: Mutual medical: Mutual
pit: IRPF
RouteExtendedList: Router RouteExtendedList: Router
wasteRecalc: Waste recaclulate wasteRecalc: Waste recaclulate
operator: Operator operator: Operator

View File

@ -303,6 +303,7 @@ globals:
clientsActionsMonitor: Clientes y acciones clientsActionsMonitor: Clientes y acciones
serial: Facturas por serie serial: Facturas por serie
medical: Mutua medical: Mutua
pit: IRPF
wasteRecalc: Recalcular mermas wasteRecalc: Recalcular mermas
operator: Operario operator: Operario
parking: Parking parking: Parking

View File

@ -95,6 +95,7 @@ const columns = computed(() => [
optionLabel: 'description', optionLabel: 'description',
}, },
}, },
orderBy: 'priority',
}, },
{ {
align: 'right', align: 'right',

View File

@ -350,7 +350,7 @@ const openTab = (id) =>
class="q-mr-sm" class="q-mr-sm"
dense dense
flat flat
@click="$refs.tableRef.reload()" @click="tableRef.CrudModelRef.vnPaginateRef.update()"
> >
<QTooltip>{{ $t('globals.refresh') }}</QTooltip> <QTooltip>{{ $t('globals.refresh') }}</QTooltip>
</QBtn> </QBtn>

View File

@ -0,0 +1,263 @@
<script setup>
import { ref } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import VnInputDate from 'src/components/common/VnInputDate.vue';
import axios from 'axios';
import FetchData from 'components/FetchData.vue';
import FormModel from 'src/components/FormModel.vue';
import VnRow from 'components/ui/VnRow.vue';
import VnInput from 'src/components/common/VnInput.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import CrudModel from 'components/CrudModel.vue';
import VnTitle from 'src/components/common/VnTitle.vue';
import { useQuasar } from 'quasar';
import VnConfirm from 'components/ui/VnConfirm.vue';
import useNotify from 'src/composables/useNotify.js';
const { notify } = useNotify();
const route = useRoute();
const { t } = useI18n();
const disabilityGradesOptions = ref();
const workerPitCrudRef = ref({});
const insertTag = () => {
workerPitCrudRef.value.insert();
};
const quasar = useQuasar();
const deleteRelative = async (id) => {
await new Promise((resolve) => {
quasar
.dialog({
component: VnConfirm,
componentProps: {
title: t('Remove Relative'),
message: t('Do you want to remove this relative?'),
},
})
.onOk(() => {
resolve(true);
})
.onCancel(() => {
resolve(false);
});
});
await axios.delete(`WorkerRelatives/${id}`);
workerPitCrudRef.value.reload();
notify('Relative removed', 'positive');
};
</script>
<template>
<FetchData
url="DisabilityGrades"
@on-fetch="(data) => (disabilityGradesOptions = data)"
auto-load
/>
<FormModel
url="WorkerIrpfs"
:filter="{ where: { workerFk: route.params.id } }"
auto-load
data-key="workerIrpfs"
:max-width="false"
>
<template #form="{ data }">
<QCard class="q-px-lg q-py-lg">
<VnTitle :text="t('IRPF')" />
<VnRow>
<VnInput
:label="t('familySituation')"
clearable
v-model="data.familySituation"
/>
<VnInput :label="t('spouseNif')" clearable v-model="data.spouseNif" />
</VnRow>
<VnRow>
<VnSelect
:label="t('disabilityGrades')"
:options="disabilityGradesOptions"
option-label="description"
option-value="id"
v-model="data.disabilityGradeFk"
id="disabilityGrades"
data-cy="disabilityGrades"
hide-selected
/>
<VnInputDate
:label="t('geographicMobilityDate')"
v-model="data.geographicMobilityDate"
/>
</VnRow>
<VnRow>
<VnInput
clearable
v-model="data.childPension"
:label="t(`childPension`)"
/>
<VnInput
clearable
v-model="data.spousePension"
:label="t(`spousePension`)"
/>
</VnRow>
<VnRow>
<QCheckbox v-model="data.isDependend" :label="t(`isDependend`)" />
<QCheckbox
v-model="data.hasHousingPaymentBefore"
:label="t(`hasHousingPaymentBefore`)"
/>
</VnRow>
<VnRow>
<QCheckbox
v-model="data.hasHousingPaymentAfter"
:label="t(`hasHousingPaymentAfter`)"
/>
<QCheckbox
v-model="data.hasExtendedWorking"
:label="t(`hasExtendedWorking`)"
/>
</VnRow>
</QCard>
<CrudModel
ref="workerPitCrudRef"
data-key="workerPit"
url="WorkerRelatives"
auto-load
:filter="{
where: { workerFk: route.params.id },
}"
:data-required="{ workerFk: route.params.id }"
:has-sub-toolbar="false"
>
<template #body="{ rows }">
<QCard class="q-px-lg q-py-lg" flat>
<div class="row no-wrap justify-between q-pb-md">
<VnTitle :text="t('Relatives')" />
<QBtnGroup push style="column-gap: 10px">
<QBtn
color="primary"
icon="restart_alt"
flat
@click="workerPitCrudRef.reset"
:disable="!workerPitCrudRef.hasChanges"
:title="t('globals.reset')"
/>
<QBtn
ref="saveButtonRef"
color="primary"
icon="save"
@click="workerPitCrudRef.onSubmit"
:disable="!workerPitCrudRef.hasChanges"
:title="t('globals.save')"
data-cy="workerPitRelativeSaveBtn"
/>
</QBtnGroup>
</div>
<div
v-for="(row, index) in rows"
:key="index"
class="row no-wrap q-mb-lg q-gutter-lg"
padding="none"
>
<VnSelect
:options="[
{ id: 0, name: 'Ascendiente' },
{ id: 1, name: 'Descendiente' },
]"
:label="t('isDescendant')"
v-model="row.isDescendant"
class="q-gutter-xs q-mb-xs"
/>
<VnSelect
:label="t('disabilityGrades')"
:options="disabilityGradesOptions"
option-label="description"
option-value="id"
v-model="row.disabilityGradeFk"
class="q-gutter-xs q-mb-xs"
/>
<VnInput
type="number"
v-model="row.birthed"
:label="t(`birthed`)"
/>
<VnInput
type="number"
v-model="row.adoptionYear"
:label="t(`adoptionYear`)"
/>
<QCheckbox
v-model="row.isDependend"
:label="t(`isDependend`)"
/>
<QCheckbox
v-model="row.isJointCustody"
:label="t(`isJointCustody`)"
size="xs"
/>
<QBtn
@click="deleteRelative(rows[0].id)"
class="cursor-pointer"
color="primary"
flat
icon="delete"
style="flex: 0"
/>
</div>
<VnRow class="justify-left items-center">
<QBtn
@click="insertTag(rows)"
class="cursor-pointer"
color="primary"
flat
icon="add"
shortcut="+"
style="flex: 0"
data-cy="addRelative"
/>
</VnRow>
</QCard>
</template>
</CrudModel>
</template>
</FormModel>
</template>
<i18n>
es:
familySituation: Situación familiar
disabilityGrades: Discapacidad
geographicMobilityDate: Movilidad geografica
childPension: Pensión hijos
spousePension: Pensión cónyuge
isDependend: Ayuda / Movilidad reducida
spouseNif: NIF cónyuge
hasHousingPaymentBefore: Pagos vivienda anterior 2011
hasHousingPaymentAfter: Pagos vivienda posterior 2011
hasExtendedWorking: Prolongación actividad laboral
isDescendant: Descen/Ascen
disabilityGradeFk: Discapacidad
birthed: Año nacimiento
adoptionYear: Año adopción
isJointCustody: Computo por entero
Relatives: Relacionados
en:
familySituation: Family Situation
disabilityGrades: Disability Grades
geographicMobilityDate: Geographic Mobility Date
childPension: Child Pension
spousePension: Spouse Pension
isDependend: Dependent Suport / Reduced Mobility
spouseNif: Spouse NIF (Tax ID)
hasHousingPaymentBefore: Housing Payments Before 2011
hasHousingPaymentAfter: Housing Payments After 2011
hasExtendedWorking: Extended Work Activity
isDescendant: Descendant/Ascendant
disabilityGradeFk: Disability Grade
birthed: Birth Year
adoptionYear: Adoption Year
isJointCustody: Joint custody
Relatives: Relatives
</i18n>

View File

@ -43,7 +43,7 @@ const { t } = useI18n();
const { openConfirmationModal } = useVnConfirm(); const { openConfirmationModal } = useVnConfirm();
const isNew = computed(() => props.isNewMode); const isNew = computed(() => props.isNewMode);
const dated = ref(null); const dated = ref(props.date);
const tickedNodes = ref(); const tickedNodes = ref();
const _excludeType = ref('all'); const _excludeType = ref('all');
@ -73,7 +73,6 @@ const exclusionCreate = async () => {
await axios.post(`Zones/${route.params.id}/exclusions`, { await axios.post(`Zones/${route.params.id}/exclusions`, {
dated: dated.value, dated: dated.value,
}); });
await refetchEvents(); await refetchEvents();
}; };
@ -115,13 +114,14 @@ onMounted(() => {
@on-submit="onSubmit()" @on-submit="onSubmit()"
:default-cancel-button="false" :default-cancel-button="false"
:default-submit-button="false" :default-submit-button="false"
:submit-on-enter="false"
> >
<template #form-inputs> <template #form-inputs>
<VnRow class="row q-gutter-md q-mb-lg"> <VnRow class="row q-gutter-md q-mb-lg">
<VnInputDate <VnInputDate
:label="t('eventsInclusionForm.day')" :label="t('eventsInclusionForm.day')"
v-model="dated" v-model="dated"
:model-value="props.date" :required="true"
/> />
</VnRow> </VnRow>
<div class="column q-gutter-y-sm q-mb-md"> <div class="column q-gutter-y-sm q-mb-md">
@ -182,6 +182,7 @@ onMounted(() => {
:label="isNew ? t('globals.add') : t('globals.save')" :label="isNew ? t('globals.add') : t('globals.save')"
type="submit" type="submit"
color="primary" color="primary"
@click="onSubmit()"
/> />
</template> </template>
</FormPopup> </FormPopup>

View File

@ -38,6 +38,7 @@ const datakey = 'ZoneLocations';
const url = computed(() => `Zones/${route.params.id}/getLeaves`); const url = computed(() => `Zones/${route.params.id}/getLeaves`);
const arrayData = useArrayData(datakey, { const arrayData = useArrayData(datakey, {
url: url.value, url: url.value,
limit: null,
}); });
const store = arrayData.store; const store = arrayData.store;
@ -74,6 +75,7 @@ const onNodeExpanded = async (nodeKeysArray) => {
if (response.data) { if (response.data) {
node.childs = response.data.map((n) => { node.childs = response.data.map((n) => {
if (n.sons > 0) n.childs = [{}]; if (n.sons > 0) n.childs = [{}];
n.selected = isSelected(n.selected);
return n; return n;
}); });
} }
@ -90,21 +92,16 @@ const onNodeExpanded = async (nodeKeysArray) => {
previousExpandedNodes.value = nodeKeysSet; previousExpandedNodes.value = nodeKeysSet;
}; };
const formatNodeSelected = (node) => {
if (node.selected === 1) node.selected = true;
else if (node.selected === 0) node.selected = false;
if (node.sons > 0 && !node.childs) node.childs = [{}];
};
const fetchNodeLeaves = async (nodeKey) => { const fetchNodeLeaves = async (nodeKey) => {
if (!treeRef.value) return; if (!treeRef.value) return;
const node = treeRef.value?.getNodeByKey(nodeKey); const node = treeRef.value?.getNodeByKey(nodeKey);
if (node.selected === 1) node.selected = true; if (typeof node.selected === 'number') node.selected = !!node.selected;
else if (node.selected === 0) node.selected = false; if (node.sons > 0 && !node.childs) {
node.childs = [{}];
const index = expanded.value.indexOf(node.id);
expanded.value.splice(index, 1);
}
if (!node || node.sons === 0) return; if (!node || node.sons === 0) return;
state.set('Tree', node);
}; };
function getNodeIds(node) { function getNodeIds(node) {
@ -119,6 +116,10 @@ function getNodeIds(node) {
return ids; return ids;
} }
function isSelected(selected) {
if (typeof selected === 'number') return !!selected;
}
watch( watch(
() => store.data, () => store.data,
async (val) => { async (val) => {
@ -128,32 +129,21 @@ watch(
nodes.value[0].childs = [...val]; nodes.value[0].childs = [...val];
const fetchedNodeKeys = val.flatMap(getNodeIds); const fetchedNodeKeys = val.flatMap(getNodeIds);
state.set('Tree', [...fetchedNodeKeys]); state.set('Tree', [...fetchedNodeKeys]);
expanded.value = [null, ...fetchedNodeKeys];
if (!store.userParams?.search) {
val.forEach((n) => {
formatNodeSelected(n);
});
store.data = null;
expanded.value = [null];
} else {
for (let n of state.get('Tree')) { for (let n of state.get('Tree')) {
await fetchNodeLeaves(n); await fetchNodeLeaves(n);
} }
expanded.value = [null, ...fetchedNodeKeys];
}
previousExpandedNodes.value = new Set(expanded.value); previousExpandedNodes.value = new Set(expanded.value);
}, },
{ immediate: true } { immediate: true }
); );
const reFetch = async () => { const reFetch = async () => {
const { data } = await arrayData.fetch({ append: false }); await arrayData.fetch({});
nodes.value = data;
expanded.value = [null];
}; };
onMounted(async () => { onMounted(async () => {
if (store.userParams?.search) await arrayData.fetch({}); await reFetch();
}); });
onUnmounted(() => { onUnmounted(() => {
@ -167,13 +157,13 @@ onUnmounted(() => {
v-if="showSearchBar" v-if="showSearchBar"
v-model="store.userParams.search" v-model="store.userParams.search"
:placeholder="$t('globals.search')" :placeholder="$t('globals.search')"
@update:model-value="reFetch()" @keydown.enter.stop.prevent="reFetch"
> >
<template #prepend> <template #prepend>
<QBtn color="primary" icon="search" dense flat @click="reFetch()" /> <QBtn color="primary" icon="search" dense flat @click="reFetch()" />
</template> </template>
</VnInput> </VnInput>
<VnSearchbar :data-key="datakey" :url="url" :redirect="false" /> <VnSearchbar v-if="!showSearchBar" :data-key="datakey" :url="url" :redirect="false" />
<QTree <QTree
ref="treeRef" ref="treeRef"
:nodes="nodes" :nodes="nodes"

View File

@ -24,6 +24,7 @@ export default {
'WorkerDms', 'WorkerDms',
'WorkerTimeControl', 'WorkerTimeControl',
'WorkerLocker', 'WorkerLocker',
'WorkerPit',
'WorkerBalance', 'WorkerBalance',
'WorkerFormation', 'WorkerFormation',
'WorkerMedical', 'WorkerMedical',
@ -216,6 +217,15 @@ export default {
}, },
component: () => import('src/pages/Worker/Card/WorkerMedical.vue'), component: () => import('src/pages/Worker/Card/WorkerMedical.vue'),
}, },
{
name: 'WorkerPit',
path: 'pit',
meta: {
title: 'pit',
icon: 'lock',
},
component: () => import('src/pages/Worker/Card/WorkerPit.vue'),
},
{ {
name: 'WorkerOperator', name: 'WorkerOperator',
path: 'operator', path: 'operator',

View File

@ -1,3 +1,5 @@
const { randomNumber, randomString } = require('../../support');
describe('VnLocation', () => { describe('VnLocation', () => {
const locationOptions = '[role="listbox"] > div.q-virtual-scroll__content > .q-item'; const locationOptions = '[role="listbox"] > div.q-virtual-scroll__content > .q-item';
const dialogInputs = '.q-dialog label input'; const dialogInputs = '.q-dialog label input';
@ -99,7 +101,7 @@ describe('VnLocation', () => {
}); });
it('Create postCode', () => { it('Create postCode', () => {
const postCode = '1234475'; const postCode = Math.floor(100000 + Math.random() * 900000);
const province = 'Valencia'; const province = 'Valencia';
cy.get(createLocationButton).click(); cy.get(createLocationButton).click();
cy.get('.q-card > h1').should('have.text', 'New postcode'); cy.get('.q-card > h1').should('have.text', 'New postcode');
@ -115,9 +117,10 @@ describe('VnLocation', () => {
checkVnLocation(postCode, province); checkVnLocation(postCode, province);
}); });
it('Create city', () => {
const postCode = '9011'; it('Create city without country', () => {
const province = 'Saskatchew'; const postCode = randomNumber();
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.get(
@ -131,6 +134,58 @@ describe('VnLocation', () => {
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', () => {
const cityName = 'Saskatchew'.concat(Math.random(1 * 100));
cy.get(createLocationButton).click();
cy.selectOption(
`${createForm.prefix} > :nth-child(5) > :nth-child(3) `,
'Italia'
);
cy.get(
`${createForm.prefix} > :nth-child(4) > .q-select > ${createForm.sufix} > :nth-child(2) > .q-icon`
).click();
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.get('#q-portal--dialog--4 .q-btn--standard').click();
});
it('Create province with country', () => {
const provinceName = 'Saskatchew'.concat(Math.random(1 * 100));
cy.get(createLocationButton).click();
cy.selectOption(
`${createForm.prefix} > :nth-child(5) > :nth-child(3) `,
'España'
);
cy.get(
`${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.countSelectOptions('#q-portal--dialog--4 .q-select', 2);
cy.get('#q-portal--dialog--4 .q-input').type(provinceName);
cy.get('#q-portal--dialog--4 .q-btn--standard').click();
});
function checkVnLocation(postCode, province) { function checkVnLocation(postCode, province) {
cy.get(`${createForm.prefix}`).should('not.exist'); cy.get(`${createForm.prefix}`).should('not.exist');
cy.get('.q-form > .q-card > .vn-row:nth-child(6)') cy.get('.q-form > .q-card > .vn-row:nth-child(6)')

View File

@ -0,0 +1,40 @@
describe('WorkerPit', () => {
const familySituationInput = '[data-cy="Family Situation_input"]';
const familySituation = '1';
const childPensionInput = '[data-cy="Child Pension_input"]';
const childPension = '120';
const spouseNifInput = '[data-cy="Spouse Pension_input"]';
const spouseNif = '65117125P';
const spousePensionInput = '[data-cy="Spouse Pension_input"]';
const spousePension = '120';
const addRelative = '[data-cy="addRelative"]';
const isDescendantSelect = '[data-cy="Descendant/Ascendant_select"]';
const birthedInput = '[data-cy="Birth Year_input"]';
const birthed = '2002';
const adoptionYearInput = '[data-cy="Adoption Year_input"]';
const adoptionYear = '2004';
const saveRelative = '[data-cy="workerPitRelativeSaveBtn"]';
const savePIT = '#st-actions > .q-btn-group > .q-btn--standard';
beforeEach(() => {
cy.viewport(1920, 1080);
cy.login('developer');
cy.visit(`/#/worker/1107/pit`);
});
it('complete PIT', () => {
cy.get(familySituationInput).type(familySituation);
cy.get(childPensionInput).type(childPension);
cy.get(spouseNifInput).type(spouseNif);
cy.get(spousePensionInput).type(spousePension);
cy.get(savePIT).click();
});
it('complete relative', () => {
cy.get(addRelative).click();
cy.get(isDescendantSelect).type('{downArrow}{downArrow}{enter}');
cy.get(birthedInput).type(birthed);
cy.get(adoptionYearInput).type(adoptionYear);
cy.get(saveRelative).click();
});
});

View File

@ -91,6 +91,11 @@ Cypress.Commands.add('selectOption', (selector, option) => {
cy.get(selector).click(); cy.get(selector).click();
cy.get('.q-menu .q-item').contains(option).click(); cy.get('.q-menu .q-item').contains(option).click();
}); });
Cypress.Commands.add('countSelectOptions', (selector, option) => {
cy.waitForElement(selector);
cy.get(selector).click();
cy.get('.q-menu .q-item').should('have.length', option);
});
Cypress.Commands.add('fillInForm', (obj, form = '.q-form > .q-card') => { Cypress.Commands.add('fillInForm', (obj, form = '.q-form > .q-card') => {
cy.waitForElement('.q-form > .q-card'); cy.waitForElement('.q-form > .q-card');

View File

@ -15,3 +15,19 @@
import './commands'; import './commands';
function randomString(options = { length: 10 }) {
let possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
return randomizeValue(possible, options);
}
function randomNumber(options = { length: 10 }) {
let possible = '0123456789';
return randomizeValue(possible, options);
}
function randomizeValue(characterSet, options) {
return Array.from({ length: options.length }, () =>
characterSet.charAt(Math.floor(Math.random() * characterSet.length))
).join('');
}
export { randomString, randomNumber, randomizeValue };

View File

@ -29,7 +29,7 @@ describe('downloadFile', () => {
await downloadFile(1); await downloadFile(1);
expect(axios.get).toHaveBeenCalledWith( expect(axios.get).toHaveBeenCalledWith(
`${baseUrl}/dms/1/downloadFile?access_token=${token}`, `${baseUrl}/api/dms/1/downloadFile?access_token=${token}`,
{ responseType: 'blob' } { responseType: 'blob' }
); );
}); });