diff --git a/CHANGELOG.md b/CHANGELOG.md index 03812d252..e110e4cd6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,87 @@ +# Version 24.50 - 2024-12-10 + +### Added 🆕 + +- feat: add reportFileName option by:Javier Segarra +- feat: all clients just with global series by:jgallego +- feat: improve Merge branch 'test' into dev by:Javier Segarra +- feat: manual invoice in two lines by:jgallego +- feat: manualInvoice with address by:jgallego +- feat: randomize functions and example by:Javier Segarra +- feat: refs #6999 added search when user tabs on a filter with value by:Jon +- feat: refs #6999 added tab to search in VnTable filter by:Jon +- feat: refs #7346 #7346 improve form by:Javier Segarra +- feat: refs #7346 address ordered by:jgallego +- feat: refs #7346 radioButton by:jgallego +- feat: refs #7346 style radioButton by:jgallego +- feat: refs #7346 traducciones en cammelCase (7346-manualInvoice) by:jgallego +- feat: refs #8038 added new functionality in VnSelect and refactor styles by:Jon +- feat: refs #8061 #8061 updates by:Javier Segarra +- feat: refs #8087 reactive data by:jorgep +- feat: refs #8087 refs#8087 Redadas en travel by:Carlos Andrés +- feat: refs #8138 add component ticket problems by:pablone +- feat: refs #8163 add max length and more tests by:wbuezas +- feat: refs #8163 add prop by:wbuezas +- feat: refs #8163 add VnInput insert functionality and e2e test by:wbuezas +- feat: refs #8163 limit with maxLength by:Javier Segarra +- feat: refs #8163 maxLength SupplierFD account by:Javier Segarra +- feat: refs #8163 maxLengthVnInput by:Javier Segarra +- feat: refs #8163 use VnAccountNumber in VnAccountNumber by:Javier Segarra +- feat: refs #8166 show notification by:jorgep + +### Changed 📦 + +- feat: refs #8038 added new functionality in VnSelect and refactor styles by:Jon +- perf: add dataCy by:Javier Segarra +- perf: refs #7346 #7346 Imrpove interface dialog by:Javier Segarra +- perf: refs #7346 #7346 use v-show instead v-if by:Javier Segarra +- perf: refs #8036 currentFilter by:alexm +- perf: refs #8061 filter autonomy by:Javier Segarra +- perf: refs #8061 solve conflicts and random posCode it by:Javier Segarra +- perf: refs #8061 use opts from VnSelect by:Javier Segarra +- perf: refs #8163 #8061 createNewPostCodeForm by:Javier Segarra +- perf: remove console by:Javier Segarra +- perf: remove timeout by:Javier Segarra +- perf: test command fillInForm by:Javier Segarra +- refactor: refs #8162 remove comment by:wbuezas +- refactor: remove unnecesary things by:wbuezas + +### Fixed 🛠️ + +- fix: #8016 fetching data by:Javier Segarra +- fix: icons by:jgallego +- fix: refs #7229 download file by:jorgep +- fix: refs #7229 remove catch by:jorgep +- fix: refs #7229 set url by:jorgep +- fix: refs #7229 test by:jorgep +- fix: refs #7229 url by:jorgep +- fix: refs #7229 url + test by:jorgep +- fix: refs #7304 7304 clean warning by:carlossa +- fix: refs #7304 fix list by:carlossa +- fix: refs #7304 fix warning by:carlossa +- fix: refs #7346 traslations by:jgallego +- fix: refs #7529 add save by:carlossa +- fix: refs #7529 fix e2e by:carlossa +- fix: refs #7529 fix front by:carlossa +- fix: refs #7529 fix scss by:carlossa +- fix: refs #7529 fix te2e by:carlossa +- fix: refs #7529 fix workerPit e2e by:carlossa +- fix: refs #7529 front by:carlossa +- fix: refs #8036 apply exprBuilder after save filters by:alexm +- fix: refs #8036 only add where when required by:alexm +- fix: refs #8038 solve conflicts by:Jon +- fix: refs #8061 improve code dependencies (origin/8061_improve_newCP) by:Javier Segarra +- fix: refs #8138 move component from ui folder by:pablone +- fix: refs #8138 sme minor issues by:pablone +- fix: refs #8163 #8061 createNewPostCodeForm by:Javier Segarra +- fix: refs #8163 minor problem when keypress by:Javier Segarra +- fix: refs #8166 show zone error by:jorgep +- fix: removed selectedClient by:jgallego +- refs #7529 fix workerPit by:carlossa +- revert: refs #8061 test #8061 updates by:Javier Segarra +- test: fix own test by:Javier Segarra +- test: refs #8162 #8162 fix TicketList spec by:Javier Segarra + # Version 24.48 - 2024-11-25 ### Added 🆕 diff --git a/Jenkinsfile b/Jenkinsfile index 1766e3aea..c20da8ab2 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -4,7 +4,8 @@ def PROTECTED_BRANCH def BRANCH_ENV = [ test: 'test', - master: 'production' + master: 'production', + beta: 'production' ] node { @@ -15,7 +16,8 @@ node { PROTECTED_BRANCH = [ 'dev', 'test', - 'master' + 'master', + 'beta' ].contains(env.BRANCH_NAME) // https://www.jenkins.io/doc/book/pipeline/jenkinsfile/#using-environment-variables diff --git a/package.json b/package.json index 39d49519b..b5e62af11 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "salix-front", - "version": "24.52.0", + "version": "25.02.0", "description": "Salix frontend", "productName": "Salix", "author": "Verdnatura", diff --git a/test/vitest/__tests__/boot/axios.spec.js b/src/boot/__tests__/axios.spec.js similarity index 97% rename from test/vitest/__tests__/boot/axios.spec.js rename to src/boot/__tests__/axios.spec.js index 19d396ec5..b3b6f98c6 100644 --- a/test/vitest/__tests__/boot/axios.spec.js +++ b/src/boot/__tests__/axios.spec.js @@ -1,4 +1,3 @@ -import { Notify } from 'quasar'; import { onRequest, onResponseError } from 'src/boot/axios'; import { describe, expect, it, vi } from 'vitest'; @@ -27,6 +26,7 @@ describe('Axios boot', () => { expect(resultConfig).toEqual( expect.objectContaining({ headers: { + 'Accept-Language': 'en-US', Authorization: 'DEFAULT_TOKEN', }, }) diff --git a/src/boot/axios.js b/src/boot/axios.js index aee38e887..3f9fadee5 100644 --- a/src/boot/axios.js +++ b/src/boot/axios.js @@ -3,12 +3,12 @@ import { useSession } from 'src/composables/useSession'; import { Router } from 'src/router'; import useNotify from 'src/composables/useNotify.js'; import { useStateQueryStore } from 'src/stores/useStateQueryStore'; +import { i18n } from 'src/boot/i18n'; const session = useSession(); const { notify } = useNotify(); const stateQuery = useStateQueryStore(); const baseUrl = '/api/'; - axios.defaults.baseURL = baseUrl; const axiosNoError = axios.create({ baseURL: baseUrl }); @@ -16,6 +16,7 @@ const onRequest = (config) => { const token = session.getToken(); if (token.length && !config.headers.Authorization) { config.headers.Authorization = token; + config.headers['Accept-Language'] = i18n.global.locale.value; } stateQuery.add(config); return config; diff --git a/src/boot/i18n.js b/src/boot/i18n.js index b23b6d5fd..85d0772a3 100644 --- a/src/boot/i18n.js +++ b/src/boot/i18n.js @@ -1,9 +1,11 @@ import { boot } from 'quasar/wrappers'; import { createI18n } from 'vue-i18n'; import messages from 'src/i18n'; +import { useState } from 'src/composables/useState'; +const user = useState().getUser(); const i18n = createI18n({ - locale: navigator.language || navigator.userLanguage, + locale: user.value.lang || navigator.language || navigator.userLanguage, fallbackLocale: 'en', globalInjection: true, messages, diff --git a/src/boot/mainShortcutMixin.js b/src/boot/mainShortcutMixin.js new file mode 100644 index 000000000..481077e37 --- /dev/null +++ b/src/boot/mainShortcutMixin.js @@ -0,0 +1,36 @@ +import routes from 'src/router/modules'; +import { useRouter } from 'vue-router'; + +let isNotified = false; + +export default { + created: function () { + const router = useRouter(); + const keyBindingMap = routes + .filter((route) => route.meta.keyBinding) + .reduce((map, route) => { + map['Key' + route.meta.keyBinding.toUpperCase()] = route.path; + return map; + }, {}); + + const handleKeyDown = (event) => { + const { ctrlKey, altKey, code } = event; + + if (ctrlKey && altKey && keyBindingMap[code] && !isNotified) { + event.preventDefault(); + router.push(keyBindingMap[code]); + isNotified = true; + } + }; + + const handleKeyUp = (event) => { + const { ctrlKey, altKey } = event; + if (!ctrlKey || !altKey) { + isNotified = false; + } + }; + + window.addEventListener('keydown', handleKeyDown); + window.addEventListener('keyup', handleKeyUp); + }, +}; diff --git a/src/boot/qformMixin.js b/src/boot/qformMixin.js index fc7852369..187ca6dbc 100644 --- a/src/boot/qformMixin.js +++ b/src/boot/qformMixin.js @@ -1,30 +1,51 @@ -import { getCurrentInstance } from 'vue'; - +function focusFirstInput(input) { + input.focus(); +} export default { mounted: function () { - const vm = getCurrentInstance(); - if (vm.type.name === 'QForm') { - if (!['searchbarForm', 'filterPanelForm'].includes(this.$el?.id)) { - // TODO: AUTOFOCUS IS NOT FOCUSING - const that = this; - this.$el.addEventListener('keyup', function (evt) { - if (evt.key === 'Enter') { - const input = evt.target; - if (input.type == 'textarea' && evt.shiftKey) { - evt.preventDefault(); - let { selectionStart, selectionEnd } = input; - input.value = - input.value.substring(0, selectionStart) + - '\n' + - input.value.substring(selectionEnd); - selectionStart = selectionEnd = selectionStart + 1; - return; - } - evt.preventDefault(); - that.onSubmit(); - } - }); + const that = this; + + const form = document.querySelector('.q-form#formModel'); + if (!form) return; + try { + const inputsFormCard = form.querySelectorAll( + `input:not([disabled]):not([type="checkbox"])` + ); + if (inputsFormCard.length) { + focusFirstInput(inputsFormCard[0]); } + const textareas = document.querySelectorAll( + 'textarea:not([disabled]), [contenteditable]:not([disabled])' + ); + if (textareas.length) { + focusFirstInput(textareas[textareas.length - 1]); + } + const inputs = document.querySelectorAll( + 'form#formModel input:not([disabled]):not([type="checkbox"])' + ); + const input = inputs[0]; + if (!input) return; + + focusFirstInput(input); + } catch (error) { + console.error(error); } + form.addEventListener('keyup', function (evt) { + if (evt.key === 'Enter') { + const input = evt.target; + if (input.type == 'textarea' && evt.shiftKey) { + evt.preventDefault(); + let { selectionStart, selectionEnd } = input; + input.value = + input.value.substring(0, selectionStart) + + '\n' + + input.value.substring(selectionEnd); + selectionStart = selectionEnd = selectionStart + 1; + return; + } + evt.preventDefault(); + that.onSubmit(); + } + }); }, }; diff --git a/src/boot/quasar.js b/src/boot/quasar.js index 01fe68d8b..547517682 100644 --- a/src/boot/quasar.js +++ b/src/boot/quasar.js @@ -1,15 +1,18 @@ +import axios from 'axios'; import { boot } from 'quasar/wrappers'; import qFormMixin from './qformMixin'; import keyShortcut from './keyShortcut'; -import useNotify from 'src/composables/useNotify.js'; -import { CanceledError } from 'axios'; - -const { notify } = useNotify(); +import { QForm } from 'quasar'; +import { QLayout } from 'quasar'; +import mainShortcutMixin from './mainShortcutMixin'; +import { useCau } from 'src/composables/useCau'; export default boot(({ app }) => { - app.mixin(qFormMixin); + QForm.mixins = [qFormMixin]; + QLayout.mixins = [mainShortcutMixin]; + app.directive('shortcut', keyShortcut); - app.config.errorHandler = (error) => { + app.config.errorHandler = async (error) => { let message; const response = error.response; const responseData = response?.data; @@ -40,12 +43,12 @@ export default boot(({ app }) => { } console.error(error); - if (error instanceof CanceledError) { + if (error instanceof axios.CanceledError) { const env = process.env.NODE_ENV; if (env && env !== 'development') return; message = 'Duplicate request'; } - notify(message ?? 'globals.error', 'negative', 'error'); + await useCau(response, message); }; }); diff --git a/src/components/CreateNewPostcodeForm.vue b/src/components/CreateNewPostcodeForm.vue index d3d6708f0..26b79b1bc 100644 --- a/src/components/CreateNewPostcodeForm.vue +++ b/src/components/CreateNewPostcodeForm.vue @@ -25,7 +25,6 @@ const townsFetchDataRef = ref(false); const townFilter = ref({}); const countriesRef = ref(false); -const provincesFetchDataRef = ref(false); const provincesOptions = ref([]); const townsOptions = ref([]); const town = ref({}); @@ -56,13 +55,6 @@ async function setCountry(countryFk, data) { } // 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); @@ -71,9 +63,6 @@ async function setProvince(id, data) { await fetchTowns(); } async function onProvinceCreated(data) { - await provincesFetchDataRef.value.fetch({ - where: { countryFk: postcodeFormData.countryFk }, - }); postcodeFormData.provinceFk = data.id; } function provinceByCountry(countryFk = postcodeFormData.countryFk) { @@ -92,7 +81,6 @@ function setTown(newTown, data) { data.countryFk = newTown?.province?.countryFk ?? newTown; } async function onCityCreated(newTown, formData) { - await provincesFetchDataRef.value.fetch(); newTown.province = provincesOptions.value.find( (province) => province.id === newTown.provinceFk ); @@ -125,14 +113,6 @@ async function filterTowns(name) { obj.country?.name, ]; -const formatLocation = (obj, properties) => { +const formatLocation = (obj, properties = locationProperties) => { const parts = properties.map((prop) => { if (typeof prop === 'string') { return obj[prop]; @@ -43,7 +43,7 @@ const formatLocation = (obj, properties) => { return filteredParts.join(', '); }; -const modelValue = ref( +const modelValue = computed(() => props.location ? formatLocation(props.location, locationProperties) : null ); diff --git a/src/components/common/VnSectionMain.vue b/src/components/common/VnModule.vue similarity index 52% rename from src/components/common/VnSectionMain.vue rename to src/components/common/VnModule.vue index 15be6ad9a..038ee1d60 100644 --- a/src/components/common/VnSectionMain.vue +++ b/src/components/common/VnModule.vue @@ -1,8 +1,8 @@ diff --git a/src/components/common/VnSelectWorker.vue b/src/components/common/VnSelectWorker.vue new file mode 100644 index 000000000..b0fef4443 --- /dev/null +++ b/src/components/common/VnSelectWorker.vue @@ -0,0 +1,85 @@ + + + + + +es: + Responsible for approving invoices: Responsable de aprobar las facturas + diff --git a/test/vitest/__tests__/components/common/VnChangePassword.spec.js b/src/components/common/__tests__/VnChangePassword.spec.js similarity index 100% rename from test/vitest/__tests__/components/common/VnChangePassword.spec.js rename to src/components/common/__tests__/VnChangePassword.spec.js diff --git a/src/components/common/__tests__/VnDiscount.spec.js b/src/components/common/__tests__/VnDiscount.spec.js new file mode 100644 index 000000000..5d5be61ac --- /dev/null +++ b/src/components/common/__tests__/VnDiscount.spec.js @@ -0,0 +1,28 @@ +import { vi, describe, expect, it, beforeAll, afterEach } from 'vitest'; +import { createWrapper } from 'app/test/vitest/helper'; +import VnDiscount from 'components/common/vnDiscount.vue'; + +describe('VnDiscount', () => { + let vm; + + beforeAll(() => { + vm = createWrapper(VnDiscount, { + props: { + data: {}, + price: 100, + quantity: 2, + discount: 10, + } + }).vm; + }); + + afterEach(() => { + vi.clearAllMocks(); + }); + + describe('total', () => { + it('should calculate total correctly', () => { + expect(vm.total).toBe(180); + }); + }); +}); \ No newline at end of file diff --git a/src/components/common/__tests__/VnDmsList.spec.js b/src/components/common/__tests__/VnDmsList.spec.js new file mode 100644 index 000000000..9649943a2 --- /dev/null +++ b/src/components/common/__tests__/VnDmsList.spec.js @@ -0,0 +1,87 @@ +import { createWrapper, axios } from 'app/test/vitest/helper'; +import VnDmsList from 'src/components/common/VnDmsList.vue'; +import { vi, afterEach, beforeAll, describe, expect, it } from 'vitest'; + +describe('VnDmsList', () => { + let vm; + const dms = { + userFk: 1, + name: 'DMS 1' + }; + + beforeAll(() => { + vi.spyOn(axios, 'get').mockResolvedValue({ data: [] }); + vm = createWrapper(VnDmsList, { + props: { + model: 'WorkerDms/1110/filter', + defaultDmsCode: 'hhrrData', + filter: 'wd.workerFk', + updateModel: 'Workers', + deleteModel: 'WorkerDms', + downloadModel: 'WorkerDms' + } + }).vm; + }); + + afterEach(() => { + vi.clearAllMocks(); + }); + + describe('setData()', () => { + const data = [ + { + userFk: 1, + name: 'Jessica', + lastName: 'Jones', + file: '4.jpg', + created: '2021-07-28 21:00:00' + }, + { + userFk: 2, + name: 'Bruce', + lastName: 'Banner', + created: '2022-07-28 21:00:00', + dms: { + userFk: 2, + name: 'Bruce', + lastName: 'BannerDMS', + created: '2022-07-28 21:00:00', + file: '4.jpg', + } + }, + { + userFk: 3, + name: 'Natasha', + lastName: 'Romanoff', + file: '4.jpg', + created: '2021-10-28 21:00:00' + } + ] + + it('Should replace objects that contain the "dms" property with the value of the same and sort by creation date', () => { + vm.setData(data); + expect([vm.rows][0][0].lastName).toEqual('BannerDMS'); + expect([vm.rows][0][1].lastName).toEqual('Romanoff'); + + }); + }); + + describe('parseDms()', () => { + const resultDms = { ...dms, userId:1}; + + it('Should add properties that end with "Fk" by changing the suffix to "Id"', () => { + const parsedDms = vm.parseDms(dms); + expect(parsedDms).toEqual(resultDms); + }); + }); + + describe('showFormDialog()', () => { + const resultDms = { ...dms, userId:1}; + + it('should call fn parseDms() and set show true if dms is defined', () => { + vm.showFormDialog(dms); + expect(vm.formDialog.show).toEqual(true); + expect(vm.formDialog.dms).toEqual(resultDms); + }); + }); +}); \ No newline at end of file diff --git a/src/components/common/__tests__/VnLocation.spec.js b/src/components/common/__tests__/VnLocation.spec.js new file mode 100644 index 000000000..65fdae960 --- /dev/null +++ b/src/components/common/__tests__/VnLocation.spec.js @@ -0,0 +1,91 @@ +import { createWrapper } from 'app/test/vitest/helper'; +import VnLocation from 'components/common/VnLocation.vue'; +import { vi, afterEach, expect, it, beforeEach, describe } from 'vitest'; + +function buildComponent(data) { + return createWrapper(VnLocation, { + global: { + props: { + location: data + } + }, + }).vm; +} + +afterEach(() => { + vi.clearAllMocks(); +}); + +describe('formatLocation', () => { + let locationBase; + + beforeEach(() => { + locationBase = { + postcode: '46680', + city: 'Algemesi', + province: { name: 'Valencia' }, + country: { name: 'Spain' } + }; + }); + + it('should return the postcode, city, province and country', () => { + const location = { ...locationBase }; + const vm = buildComponent(location); + expect(vm.formatLocation(location)).toEqual('46680, Algemesi(Valencia), Spain'); + }); + + it('should return the postcode and country', () => { + const location = { ...locationBase, city: undefined }; + const vm = buildComponent(location); + expect(vm.formatLocation(location)).toEqual('46680, Spain'); + }); + + it('should return the city, province and country', () => { + const location = { ...locationBase, postcode: undefined }; + const vm = buildComponent(location); + expect(vm.formatLocation(location)).toEqual('Algemesi(Valencia), Spain'); + }); + + it('should return the country', () => { + const location = { ...locationBase, postcode: undefined, city: undefined, province: undefined }; + const vm = buildComponent(location); + expect(vm.formatLocation(location)).toEqual('Spain'); + }); +}); + +describe('showLabel', () => { + let locationBase; + + beforeEach(() => { + locationBase = { + code: '46680', + town: 'Algemesi', + province: 'Valencia', + country: 'Spain' + }; + }); + + it('should show the label with postcode, city, province and country', () => { + const location = { ...locationBase }; + const vm = buildComponent(location); + expect(vm.showLabel(location)).toEqual('46680, Algemesi(Valencia), Spain'); + }); + + it('should show the label with postcode and country', () => { + const location = { ...locationBase, town: undefined }; + const vm = buildComponent(location); + expect(vm.showLabel(location)).toEqual('46680, Spain'); + }); + + it('should show the label with city, province and country', () => { + const location = { ...locationBase, code: undefined }; + const vm = buildComponent(location); + expect(vm.showLabel(location)).toEqual('Algemesi(Valencia), Spain'); + }); + + it('should show the label with country', () => { + const location = { ...locationBase, code: undefined, town: undefined, province: undefined }; + const vm = buildComponent(location); + expect(vm.showLabel(location)).toEqual('Spain'); + }); +}); \ No newline at end of file diff --git a/test/vitest/__tests__/components/common/VnLog.spec.js b/src/components/common/__tests__/VnLog.spec.js similarity index 100% rename from test/vitest/__tests__/components/common/VnLog.spec.js rename to src/components/common/__tests__/VnLog.spec.js diff --git a/test/vitest/__tests__/components/common/VnSmsDialog.spec.js b/src/components/common/__tests__/VnSmsDialog.spec.js similarity index 100% rename from test/vitest/__tests__/components/common/VnSmsDialog.spec.js rename to src/components/common/__tests__/VnSmsDialog.spec.js diff --git a/src/components/ui/CardDescriptor.vue b/src/components/ui/CardDescriptor.vue index 83af77442..f4c0091d2 100644 --- a/src/components/ui/CardDescriptor.vue +++ b/src/components/ui/CardDescriptor.vue @@ -222,8 +222,8 @@ const toModule = computed(() => /> - - diff --git a/src/components/ui/VnConfirm.vue b/src/components/ui/VnConfirm.vue index 0b1913383..a02b56bdb 100644 --- a/src/components/ui/VnConfirm.vue +++ b/src/components/ui/VnConfirm.vue @@ -98,6 +98,7 @@ function cancel() { /> -import { onMounted, ref, computed, watch } from 'vue'; +import { ref, computed } from 'vue'; import { useI18n } from 'vue-i18n'; import { useArrayData } from 'composables/useArrayData'; -import { useRoute } from 'vue-router'; import toDate from 'filters/toDate'; import VnFilterPanelChip from 'components/ui/VnFilterPanelChip.vue'; +import { useFilterParams } from 'src/composables/useFilterParams'; +import { useRoute } from 'vue-router'; -const { t } = useI18n(); +const { t, te } = useI18n(); +const route = useRoute(); const $props = defineProps({ modelValue: { type: Object, @@ -55,62 +57,35 @@ const $props = defineProps({ type: Boolean, default: true, }, + arrayData: { + type: Object, + default: null, + }, }); const emit = defineEmits([ 'update:modelValue', 'refresh', 'clear', + 'search', 'init', 'remove', 'setUserParams', ]); -const arrayData = useArrayData($props.dataKey, { - exprBuilder: $props.exprBuilder, - searchUrl: $props.searchUrl, - navigate: $props.redirect ? {} : null, -}); -const route = useRoute(); +const arrayData = + $props.arrayData ?? + useArrayData($props.dataKey, { + exprBuilder: $props.exprBuilder, + searchUrl: $props.searchUrl, + navigate: $props.redirect ? {} : null, + }); + const store = arrayData.store; -const userParams = ref({}); +const userParams = ref(useFilterParams($props.dataKey).params); +const userOrders = ref(useFilterParams($props.dataKey).orders); -defineExpose({ search, sanitizer, params: userParams }); - -onMounted(() => { - userParams.value = $props.modelValue ?? {}; - emit('init', { params: userParams.value }); -}); - -function setUserParams(watchedParams) { - if (!watchedParams || Object.keys(watchedParams).length == 0) return; - - if (typeof watchedParams == 'string') watchedParams = JSON.parse(watchedParams); - if (typeof watchedParams?.filter == 'string') - watchedParams.filter = JSON.parse(watchedParams.filter); - - watchedParams = { ...watchedParams, ...watchedParams.filter?.where }; - const order = watchedParams.filter?.order; - - delete watchedParams.filter; - userParams.value = sanitizer(watchedParams); - emit('setUserParams', userParams.value, order); -} - -watch( - () => route.query[$props.searchUrl], - (val, oldValue) => (val || oldValue) && setUserParams(val) -); - -watch( - () => arrayData.store.userParams, - (val, oldValue) => (val || oldValue) && setUserParams(val) -); - -watch( - () => $props.modelValue, - (val) => (userParams.value = val ?? {}) -); +defineExpose({ search, params: userParams, remove }); const isLoading = ref(false); async function search(evt) { @@ -121,10 +96,9 @@ async function search(evt) { isLoading.value = true; const filter = { ...userParams.value, ...$props.modelValue }; store.userParamsChanged = true; - const { params: newParams } = await arrayData.addFilter({ + await arrayData.addFilter({ params: filter, }); - userParams.value = newParams; if (!$props.showAll && !Object.values(filter).length) store.data = []; emit('search'); @@ -137,7 +111,7 @@ async function clearFilters() { try { isLoading.value = true; store.userParamsChanged = true; - arrayData.reset(['skip', 'filter.skip', 'page']); + arrayData.resetPagination(); // Filtrar los params no removibles const removableFilters = Object.keys(userParams.value).filter((param) => $props.unremovableParams.includes(param) @@ -147,9 +121,8 @@ async function clearFilters() { for (const key of removableFilters) { newParams[key] = userParams.value[key]; } - userParams.value = {}; - userParams.value = { ...newParams }; // Actualizar los params con los removibles - await arrayData.applyFilter({ params: userParams.value }); + + await arrayData.applyFilter({ params: { ...newParams } }); if (!$props.showAll) { store.data = []; @@ -212,20 +185,13 @@ function formatValue(value) { return `"${value}"`; } -function sanitizer(params) { - for (const [key, value] of Object.entries(params)) { - if (key === 'and' && Array.isArray(value)) { - value.forEach((item) => { - Object.assign(params, item); - }); - delete params[key]; - } else if (value && typeof value === 'object') { - const param = Object.values(value)[0]; - if (typeof param == 'string') params[key] = param.replaceAll('%', ''); - } - } - return params; -} +const getLocale = (label) => { + const param = label.split('.').at(-1); + const globalLocale = `globals.params.${param}`; + if (te(globalLocale)) return t(globalLocale); + else if (te(t(`params.${param}`))); + else return t(`${route.meta.moduleName}.params.${param}`); +};