From cc3bc45a2c12c68dfbec3a98bd9f0eb6243e5e58 Mon Sep 17 00:00:00 2001 From: taro Date: Sat, 22 Mar 2025 12:01:11 -0300 Subject: [PATCH 01/17] . --- src/utils/onUserId.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/utils/onUserId.js diff --git a/src/utils/onUserId.js b/src/utils/onUserId.js new file mode 100644 index 00000000..28c1fcae --- /dev/null +++ b/src/utils/onUserId.js @@ -0,0 +1,19 @@ +import { watch } from 'vue'; + +import { useUserStore } from 'stores/user'; + +const userStore = useUserStore(); + +export const onUserId = (cb) => watch( + () => userStore?.user?.id, + async userId => { + if (userId) { + try { + await cb(userId); + } catch (error) { + console.error(error); + } + } + }, + { immediate: true } +); From 6cb8267a0b4ae396f69fdeba2d74bd0323d0591f Mon Sep 17 00:00:00 2001 From: wbuezas Date: Tue, 25 Mar 2025 10:53:52 +0100 Subject: [PATCH 02/17] News view refactor --- src/components/common/FormModel.vue | 14 +++++--- src/pages/Admin/NewsDetails.vue | 52 ++++++++++++++--------------- src/pages/Admin/NewsView.vue | 24 +++++-------- src/stores/user.js | 5 ++- 4 files changed, 48 insertions(+), 47 deletions(-) diff --git a/src/components/common/FormModel.vue b/src/components/common/FormModel.vue index fc5d1217..99ab9da1 100644 --- a/src/components/common/FormModel.vue +++ b/src/components/common/FormModel.vue @@ -69,6 +69,10 @@ const props = defineProps({ filter: { type: Object, default: null + }, + dataRequired: { + type: Object, + default: () => {} } }); @@ -122,9 +126,10 @@ onMounted(async () => { async function fetch() { try { - let { data } = await api.get(props.url, { - params: { filter: JSON.stringify(props.filter) } - }); + const params = props.filter + ? { filter: JSON.stringify(props.filter) } + : {}; + let { data } = await api.get(props.url, { params }); if (Array.isArray(data)) data = data[0] ?? {}; formData.value = { ...data }; emit('onDataFetched', formData.value); @@ -144,9 +149,10 @@ async function save() { isLoading.value = true; try { - const body = props.mapper + let body = props.mapper ? props.mapper(formData.value, originalData.value) : formData.value; + body = { ...body, ...props.dataRequired }; const method = props.urlCreate ? 'post' : 'patch'; const url = props.urlCreate || props.urlUpdate || props.url; diff --git a/src/pages/Admin/NewsDetails.vue b/src/pages/Admin/NewsDetails.vue index 92c3f797..15adb2a6 100644 --- a/src/pages/Admin/NewsDetails.vue +++ b/src/pages/Admin/NewsDetails.vue @@ -7,19 +7,21 @@ import VnImg from 'src/components/ui/VnImg.vue'; import VnForm from 'src/components/common/VnForm.vue'; import VnSelect from 'src/components/common/VnSelect.vue'; import VnInput from 'src/components/common/VnInput.vue'; +import FormModel from 'src/components/common/FormModel.vue'; import { useAppStore } from 'stores/app'; +import { useUserStore } from 'stores/user'; import { storeToRefs } from 'pinia'; -const jApi = inject('jApi'); +const api = inject('api'); const { t } = useI18n(); const route = useRoute(); const router = useRouter(); const appStore = useAppStore(); +const userStore = useUserStore(); const { isHeaderMounted } = storeToRefs(appStore); const newsTags = ref([]); -const pks = computed(() => ({ id: route.params.id })); const isEditMode = !!route.params.id; const formData = ref( !route.params.id @@ -32,22 +34,25 @@ const formData = ref( : undefined ); -const fetchNewDataSql = computed(() => { +const initialFetchUrl = computed(() => { if (!route.params.id) return undefined; - return { - query: ` - SELECT id, title, text, tag, priority, image - FROM news WHERE id = #id`, - params: { id: route.params.id } - }; + return `news/${route.params.id}`; +}); + +const urlUpdate = computed(() => { + if (!route.params.id) return undefined; + return `news/${route.params.id}`; +}); + +const urlCreate = computed(() => { + if (route.params.id) return undefined; + return 'news'; }); const getNewsTag = async () => { try { - newsTags.value = await jApi.query( - `SELECT name, description FROM newsTag - ORDER BY description` - ); + const { data } = await api.get('newsTags'); + newsTags.value = data; } catch (error) { console.error('Error getting newsTag:', error); } @@ -73,19 +78,14 @@ onMounted(async () => { {{ t('back') }} - - + diff --git a/src/pages/Admin/NewsView.vue b/src/pages/Admin/NewsView.vue index 27bef4db..f071adaf 100644 --- a/src/pages/Admin/NewsView.vue +++ b/src/pages/Admin/NewsView.vue @@ -5,13 +5,14 @@ import { useI18n } from 'vue-i18n'; import CardList from 'src/components/ui/CardList.vue'; import VnImg from 'src/components/ui/VnImg.vue'; import VnList from 'src/components/ui/VnList.vue'; +import FormModel from 'src/components/common/FormModel.vue'; import { useAppStore } from 'stores/app'; import { storeToRefs } from 'pinia'; import { useVnConfirm } from 'src/composables/useVnConfirm.js'; import useNotify from 'src/composables/useNotify.js'; -const jApi = inject('jApi'); +const api = inject('api'); const { t } = useI18n(); const appStore = useAppStore(); const { openConfirmationModal } = useVnConfirm(); @@ -24,28 +25,19 @@ const news = ref([]); const getNews = async () => { try { loading.value = true; - news.value = await jApi.query( - `SELECT n.id, u.nickname, n.priority, n.image, n.title - FROM news n - JOIN account.user u ON u.id = n.userFk - ORDER BY priority, n.created DESC` - ); - loading.value = false; + + const { data } = await api.get('news'); + news.value = data; } catch (error) { console.error('Error getting news:', error); + } finally { + loading.value = false; } }; const deleteNew = async (id, index) => { try { - await jApi.execQuery( - `START TRANSACTION; - DELETE FROM hedera.news WHERE ((id = #id)); - COMMIT`, - { - id - } - ); + await api.delete(`news/${id}`); news.value.splice(index, 1); notify(t('dataSaved'), 'positive'); } catch (error) { diff --git a/src/stores/user.js b/src/stores/user.js index 8f170752..8b75f124 100644 --- a/src/stores/user.js +++ b/src/stores/user.js @@ -296,6 +296,8 @@ export const useUserStore = defineStore('user', () => { tokenConfig.value = null; }; + const userId = computed(() => user.value?.id); + watch( [mainUser, supplantedUser], () => (user.value = supplantedUser.value || mainUser.value), @@ -336,6 +338,7 @@ export const useUserStore = defineStore('user', () => { updateUserLang, init, $reset, - onLogin + onLogin, + userId }; }); From f00bfb1311cc63029ba8667d47922f57cc6a0344 Mon Sep 17 00:00:00 2001 From: taro Date: Thu, 27 Mar 2025 19:39:55 -0300 Subject: [PATCH 03/17] refactor: use salix for myBasketDefaults --- src/pages/Ecomerce/CheckoutView.vue | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/pages/Ecomerce/CheckoutView.vue b/src/pages/Ecomerce/CheckoutView.vue index ad4eaf9e..2ffb86cd 100644 --- a/src/pages/Ecomerce/CheckoutView.vue +++ b/src/pages/Ecomerce/CheckoutView.vue @@ -11,6 +11,7 @@ import { useAppStore } from 'stores/app'; import { storeToRefs } from 'pinia'; const jApi = inject('jApi'); +const api = inject('api'); const { t } = useI18n(); const route = useRoute(); const router = useRouter(); @@ -295,10 +296,8 @@ const submit = async () => { }; const getDefaultValues = async () => { - return await jApi.query( - `SELECT deliveryMethod, agencyModeFk, addressFk, defaultAgencyFk - FROM myBasketDefaults` - ); + const { data: myBasketDefaults } = await api.get('Clients/myBasketDefaults'); + return myBasketDefaults; }; onMounted(async () => { From 809527558ed5428bc18b84e875109cbc3814ff86 Mon Sep 17 00:00:00 2001 From: taro Date: Fri, 28 Mar 2025 02:09:15 -0300 Subject: [PATCH 04/17] refactor: use Salix for myAddresses --- src/pages/Ecomerce/CheckoutView.vue | 53 +++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/src/pages/Ecomerce/CheckoutView.vue b/src/pages/Ecomerce/CheckoutView.vue index 2ffb86cd..a8471b35 100644 --- a/src/pages/Ecomerce/CheckoutView.vue +++ b/src/pages/Ecomerce/CheckoutView.vue @@ -7,6 +7,7 @@ import VnSelect from 'src/components/common/VnSelect.vue'; import { formatDateTitle, formatDate } from 'src/lib/filters.js'; import useNotify from 'src/composables/useNotify.js'; +import { onUserId } from 'src/utils/onUserId'; import { useAppStore } from 'stores/app'; import { storeToRefs } from 'pinia'; @@ -149,15 +150,47 @@ const validateStep = (formField, errorMessage) => { return validation; }; -const getAddresses = async () => { +const getAddresses = async (clientFk) => { try { - addresses.value = await jApi.query( - `SELECT a.id, a.nickname, p.name province, a.city, a.street, a.isActive, c.name - FROM myAddress a - LEFT JOIN vn.province p ON p.id = a.provinceFk - JOIN vn.country c ON c.id = p.countryFk - WHERE a.isActive` - ); + const filter = { + where: { + clientFk, + isActive: true, + }, + include: [ + { + relation: 'province', + scope: { + fields: ['name', 'countryFk'], + include: [ + 'country', + { + relation: 'country', + scope: { + fields: ['name'], + }, + }, + ], + }, + }, + ], + fields: [ + 'id', + 'nickname', + 'city', + 'street', + 'isActive', + 'provinceFk', + ] + }; + + const { data: myAddresses } = await api.get('Addresses', { + params: { + filter: JSON.stringify(filter) + } + }); + + addresses.value = myAddresses; } catch (error) { console.error('Error getting addresses:', error); } @@ -325,10 +358,10 @@ onMounted(async () => { const [_defaultValues] = await getDefaultValues(); if (_defaultValues) defaultValues.value = _defaultValues; } - - getAddresses(); }); +onUserId(getAddresses); + watch( () => orderForm.value.method, () => { From e7f4ece0a74897febd4b8cd70194ec40a120d3b2 Mon Sep 17 00:00:00 2001 From: taro Date: Mon, 31 Mar 2025 03:35:52 -0300 Subject: [PATCH 05/17] refactor(CheckoutView): use salix for getAgencies --- src/pages/Ecomerce/CheckoutView.vue | 35 ++++++++++++++++------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/pages/Ecomerce/CheckoutView.vue b/src/pages/Ecomerce/CheckoutView.vue index a8471b35..ee38062f 100644 --- a/src/pages/Ecomerce/CheckoutView.vue +++ b/src/pages/Ecomerce/CheckoutView.vue @@ -198,22 +198,25 @@ const getAddresses = async (clientFk) => { const getAgencies = async () => { try { - const { results } = await jApi.execQuery( - `CALL vn.zone_getAgency(#address, #date); - SELECT DISTINCT a.agencyModeFk id, a.description - FROM tmp.zoneGetAgency a - JOIN vn.deliveryMethod d - ON d.id = a.deliveryMethodFk - WHERE d.code IN ('AGENCY', 'DELIVERY') - AND a.isVisible - ORDER BY a.description; - DROP TEMPORARY TABLE tmp.zoneGetAgency`, - { - address: orderForm.value.address, - date: new Date(orderForm.value.date) - } - ); - agencies.value = results[1].data; + const agenciesInZone = await api.get('Agencies/landsThatDay', { + params: { + addressFk: orderForm.value.address, + landed: new Date(orderForm.value.date), + } + }); + const deliveryMethods = await api.get('DeliveryMethods'); + + const results = agenciesInZone.data + .filter(agency => agency.isVisible) + .map(agency => ({ + id: agency.agencyModeFk, + description: agency.description, + deliveryMethod: deliveryMethods.data.find(dm => dm.id === agency.deliveryMethodFk).code, + })) + .filter(agency => agency.deliveryMethod === 'AGENCY' || agency.deliveryMethod === 'DELIVERY') + .toSorted((a, b) => a.description.localeCompare(b.description)); + + agencies.value = results; if (agencies.value && agencies.value.length && defaultValues.value) { const found = agencies.value.find( From 410bf86913686c2ee31bc625b606979acf6e3e93 Mon Sep 17 00:00:00 2001 From: taro Date: Wed, 2 Apr 2025 11:31:21 -0300 Subject: [PATCH 06/17] refactor(CheckoutView): use salix for checkOrder --- src/i18n/ca-ES/index.js | 3 ++- src/i18n/en-US/index.js | 3 ++- src/i18n/es-ES/index.js | 3 ++- src/i18n/fr-FR/index.js | 3 ++- src/i18n/pt-PT/index.js | 3 ++- src/stores/app.js | 57 ++++++++++++++++++++++------------------- 6 files changed, 40 insertions(+), 32 deletions(-) diff --git a/src/i18n/ca-ES/index.js b/src/i18n/ca-ES/index.js index 48d214e0..02f060ea 100644 --- a/src/i18n/ca-ES/index.js +++ b/src/i18n/ca-ES/index.js @@ -168,6 +168,7 @@ export default { // Errors errors: { statusUnauthorized: 'Accés denegat', - tokenConfig: 'Error al obtenir la configuració del token' + tokenConfig: 'Error al obtenir la configuració del token', + orderNotOwnedByUser: 'The order belongs to another user' } }; diff --git a/src/i18n/en-US/index.js b/src/i18n/en-US/index.js index 80107dd5..fba57ab0 100644 --- a/src/i18n/en-US/index.js +++ b/src/i18n/en-US/index.js @@ -200,6 +200,7 @@ export default { // Errors errors: { statusUnauthorized: 'Access denied', - tokenConfig: 'Error fetching token config' + tokenConfig: 'Error fetching token config', + orderNotOwnedByUser: 'The order belongs to another user' } }; diff --git a/src/i18n/es-ES/index.js b/src/i18n/es-ES/index.js index 15639c2d..67248e94 100644 --- a/src/i18n/es-ES/index.js +++ b/src/i18n/es-ES/index.js @@ -200,6 +200,7 @@ export default { // Errors errors: { statusUnauthorized: 'Acceso denegado', - tokenConfig: 'Error al obtener configuración de token' + tokenConfig: 'Error al obtener configuración de token', + orderNotOwnedByUser: 'The order belongs to another user' } }; diff --git a/src/i18n/fr-FR/index.js b/src/i18n/fr-FR/index.js index 2eea9a38..9881c440 100644 --- a/src/i18n/fr-FR/index.js +++ b/src/i18n/fr-FR/index.js @@ -172,6 +172,7 @@ export default { errors: { statusUnauthorized: 'Accès refusé', tokenConfig: - 'Erreur lors de la récupération de la configuration du jeton' + 'Erreur lors de la récupération de la configuration du jeton', + orderNotOwnedByUser: 'The order belongs to another user' } }; diff --git a/src/i18n/pt-PT/index.js b/src/i18n/pt-PT/index.js index 460335e0..ecbed192 100644 --- a/src/i18n/pt-PT/index.js +++ b/src/i18n/pt-PT/index.js @@ -166,6 +166,7 @@ export default { // Errors errors: { statusUnauthorized: 'Acesso negado', - tokenConfig: 'Erro ao obter configuração do token' + tokenConfig: 'Erro ao obter configuração do token', + orderNotOwnedByUser: 'The order belongs to another user' } }; diff --git a/src/stores/app.js b/src/stores/app.js index d5183dc8..3c0b3009 100644 --- a/src/stores/app.js +++ b/src/stores/app.js @@ -81,14 +81,6 @@ export const useAppStore = defineStore('hedera', { this.basketOrderId = localStorage.getItem(storageOrderName); }, - async checkOrder(orderId) { - const resultSet = await jApi.execQuery( - 'CALL myOrder_checkConfig(#id)', - { id: orderId } - ); - resultSet.fetchValue(); - }, - async check(checkoutContinue) { if (this.basketOrderId) { return await this.checkRedirect(checkoutContinue); @@ -99,26 +91,37 @@ export const useAppStore = defineStore('hedera', { }, async checkRedirect(checkoutContinue) { - try { - await this.checkOrder(this.basketOrderId); - return true; - } catch (err) { - switch (err.code) { - case 'orderConfirmed': - case 'orderNotOwnedByUser': - this.unloadOrder(); - await this.redirect(); - break; - default: - this.router.push({ - name: 'checkout', - params: { id: this.basketOrderId }, - query: { continue: checkoutContinue } - }); - notify(err.message, 'negative'); - } - return false; + const orderId = this.basketOrderId; + // const orderId = 'nope!'; // <--- hard-coded invalid orderId to test failure case + const myOrder_checkConfig = await api.post('applications/myOrder_checkConfig/execute-proc', { + schema: 'hedera', + params: [orderId], + }, { + validateStatus: () => true, + }); + + if (myOrder_checkConfig.status >= 200 && myOrder_checkConfig.status < 300) { + return true; } + + switch (myOrder_checkConfig.data.error?.message) { + case 'orderNotOwnedByUser': + notify(t(`errors.orderNotOwnedByUser`), 'negative'); + case 'orderConfirmed': + case 'orderNotOwnedByUser': + this.unloadOrder(); + await this.redirect(); + break; + default: + this.router.push({ + name: 'checkout', + params: { id: this.basketOrderId }, + query: { continue: checkoutContinue } + }); + notify(myOrder_checkConfig.data.error.message, 'negative'); + } + + return false; }, async redirect() { From 824fa281265029b5d2940c68510d5586f5930c99 Mon Sep 17 00:00:00 2001 From: wbuezas Date: Fri, 4 Apr 2025 11:49:45 +0200 Subject: [PATCH 07/17] Use fetch data component --- src/pages/Admin/NewsView.vue | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/src/pages/Admin/NewsView.vue b/src/pages/Admin/NewsView.vue index f071adaf..73537115 100644 --- a/src/pages/Admin/NewsView.vue +++ b/src/pages/Admin/NewsView.vue @@ -5,7 +5,7 @@ import { useI18n } from 'vue-i18n'; import CardList from 'src/components/ui/CardList.vue'; import VnImg from 'src/components/ui/VnImg.vue'; import VnList from 'src/components/ui/VnList.vue'; -import FormModel from 'src/components/common/FormModel.vue'; +import FetchData from 'src/components/common/FetchData.vue'; import { useAppStore } from 'stores/app'; import { storeToRefs } from 'pinia'; @@ -19,20 +19,12 @@ const { openConfirmationModal } = useVnConfirm(); const { isHeaderMounted } = storeToRefs(appStore); const { notify } = useNotify(); -const loading = ref(false); +const loading = ref(true); const news = ref([]); -const getNews = async () => { - try { - loading.value = true; - - const { data } = await api.get('news'); - news.value = data; - } catch (error) { - console.error('Error getting news:', error); - } finally { - loading.value = false; - } +const onFetch = data => { + news.value = data; + loading.value = false; }; const deleteNew = async (id, index) => { @@ -49,6 +41,7 @@ onMounted(async () => getNews());