Merge branch 'beta' into feature/Account-view-refactor
gitea/hedera-web/pipeline/pr-beta This commit looks good Details

This commit is contained in:
Javier Segarra 2025-03-21 14:44:11 +00:00
commit c8620a53e4
9 changed files with 195 additions and 78 deletions

View File

@ -0,0 +1,66 @@
<script setup>
import { onMounted, inject } from 'vue';
const $props = defineProps({
autoLoad: {
type: Boolean,
default: false
},
url: {
type: String,
default: ''
},
filter: {
type: Object,
default: null
},
where: {
type: Object,
default: null
},
sortBy: {
type: String,
default: ''
},
limit: {
type: [String, Number],
default: ''
},
params: {
type: Object,
default: null
}
});
const emit = defineEmits(['onFetch']);
const api = inject('api');
defineExpose({ fetch });
onMounted(async () => {
if ($props.autoLoad) {
await fetch();
}
});
async function fetch(fetchFilter = {}) {
try {
const filter = { ...fetchFilter, ...$props.filter }; // eslint-disable-line vue/no-dupe-keys
if ($props.where && !fetchFilter.where) filter.where = $props.where;
if ($props.sortBy) filter.order = $props.sortBy;
if ($props.limit) filter.limit = $props.limit;
const { data } = await api.get($props.url, {
params: { filter: JSON.stringify(filter), ...$props.params }
});
emit('onFetch', data);
return data;
} catch (e) {
//
}
}
</script>
<template>
<template></template>
</template>

View File

@ -5,32 +5,26 @@ import { useRouter, useRoute } from 'vue-router';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'src/components/common/VnInput.vue';
import VnSelect from 'src/components/common/VnSelect.vue'; import VnSelect from 'src/components/common/VnSelect.vue';
import VnForm from 'src/components/common/VnForm.vue'; import FormModel from 'src/components/common/FormModel.vue';
import { useUserStore } from 'stores/user';
import { useAppStore } from 'stores/app'; import { useAppStore } from 'stores/app';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
const { t } = useI18n(); const { t } = useI18n();
const jApi = inject('jApi'); const api = inject('api');
const appStore = useAppStore(); const appStore = useAppStore();
const userStore = useUserStore();
const { isHeaderMounted } = storeToRefs(appStore); const { isHeaderMounted } = storeToRefs(appStore);
const vnFormRef = ref(null); const vnFormRef = ref(null);
const countriesOptions = ref([]); const countriesOptions = ref([]);
const provincesOptions = ref([]); const provincesOptions = ref([]);
const pks = { id: route.params.id };
const isEditMode = route.params.id !== '0'; const isEditMode = route.params.id !== '0';
const fetchAddressDataSql = { const editAddressData = ref(null);
query: ` const showForm = ref(false);
SELECT a.id, a.street, a.nickname, a.city, a.postalCode, a.provinceFk, p.countryFk
FROM myAddress a
LEFT JOIN vn.province p ON p.id = a.provinceFk
WHERE a.id = #address
`,
params: { address: route.params.id }
};
watch( watch(
() => vnFormRef?.value?.formData?.countryFk, () => vnFormRef?.value?.formData?.countryFk,
@ -40,23 +34,49 @@ watch(
const goBack = () => router.push({ name: 'addressesList' }); const goBack = () => router.push({ name: 'addressesList' });
const getCountries = async () => { const getCountries = async () => {
countriesOptions.value = await jApi.query( const filter = { fields: ['id', 'name'], order: 'name' };
`SELECT id, name FROM vn.country const { data } = await api.get('Countries', {
ORDER BY name` params: { filter: JSON.stringify(filter) }
); });
countriesOptions.value = data;
}; };
const getProvinces = async countryFk => { const getProvinces = async countryFk => {
if (!countryFk) return; if (!countryFk) return;
provincesOptions.value = await jApi.query(
`SELECT id, name FROM vn.province const filter = {
WHERE countryFk = #id where: { countryFk },
ORDER BY name`, fields: ['id', 'name'],
{ id: countryFk } order: 'name'
); };
const { data } = await api.get('Provinces', {
params: { filter: JSON.stringify(filter) }
});
provincesOptions.value = data;
}; };
onMounted(() => getCountries()); const getAddressDetails = async () => {
const { data } = await api.get(`Addresses/${route.params.id}`);
if (!data) return;
const { nickname, street, city, postalCode, province, provinceFk } = data;
editAddressData.value = {
nickname,
street,
city,
postalCode,
countryFk: province?.countryFk,
provinceFk
};
};
onMounted(async () => {
if (isEditMode) {
await getAddressDetails();
}
getCountries();
showForm.value = true;
});
</script> </script>
<template> <template>
@ -74,42 +94,49 @@ onMounted(() => getCountries());
</QTooltip> </QTooltip>
</QBtn> </QBtn>
</Teleport> </Teleport>
<VnForm <FormModel
v-if="showForm"
ref="vnFormRef" ref="vnFormRef"
:fetch-form-data-sql="fetchAddressDataSql" :url-create="
:columns-to-ignore-update="['countryFk']" !isEditMode
:create-model-default="{ ? `Clients/${userStore?.user?.id}/createAddress`
field: 'clientFk', : ''
value: 'account.myUser_getId()' "
}" :url-update="
:pks="pks" isEditMode
:is-edit-mode="isEditMode" ? `Clients/${userStore?.user?.id}/updateAddress/${route?.params?.id}`
: ''
"
:form-initial-data="editAddressData"
:title="t(isEditMode ? 'editAddress' : 'addAddress')" :title="t(isEditMode ? 'editAddress' : 'addAddress')"
table="myAddress"
schema="hedera"
@on-data-saved="goBack()" @on-data-saved="goBack()"
:show-bottom-actions="false"
> >
<template #form="{ data }"> <template #form="{ data }">
<VnInput <VnInput
v-model="data.nickname" v-model="data.nickname"
:label="t('name')" :label="t('name')"
data-cy="addressFormNickname" data-cy="addressFormNickname"
required
/> />
<VnInput <VnInput
v-model="data.street" v-model="data.street"
:label="t('address')" :label="t('address')"
data-cy="addressFormStreet" data-cy="addressFormStreet"
required
/> />
<VnInput <VnInput
v-model="data.city" v-model="data.city"
:label="t('city')" :label="t('city')"
data-cy="addressFormCity" data-cy="addressFormCity"
required
/> />
<VnInput <VnInput
v-model="data.postalCode" v-model="data.postalCode"
type="number" type="number"
:label="t('postalCode')" :label="t('postalCode')"
data-cy="addressFormPostcode" data-cy="addressFormPostcode"
required
/> />
<VnSelect <VnSelect
v-model="data.countryFk" v-model="data.countryFk"
@ -117,15 +144,17 @@ onMounted(() => getCountries());
:options="countriesOptions" :options="countriesOptions"
@update:model-value="data.provinceFk = null" @update:model-value="data.provinceFk = null"
data-cy="addressFormCountry" data-cy="addressFormCountry"
required
/> />
<VnSelect <VnSelect
v-model="data.provinceFk" v-model="data.provinceFk"
:label="t('province')" :label="t('province')"
:options="provincesOptions" :options="provincesOptions"
data-cy="addressFormProvince" data-cy="addressFormProvince"
required
/> />
</template> </template>
</VnForm> </FormModel>
</QPage> </QPage>
</template> </template>

View File

@ -5,19 +5,24 @@ import { useRouter } from 'vue-router';
import CardList from 'src/components/ui/CardList.vue'; import CardList from 'src/components/ui/CardList.vue';
import VnList from 'src/components/ui/VnList.vue'; import VnList from 'src/components/ui/VnList.vue';
import FetchData from 'src/components/common/FetchData.vue';
import useNotify from 'src/composables/useNotify.js'; import useNotify from 'src/composables/useNotify.js';
import { useVnConfirm } from 'src/composables/useVnConfirm.js'; import { useVnConfirm } from 'src/composables/useVnConfirm.js';
import { useAppStore } from 'stores/app'; import { useAppStore } from 'stores/app';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useUserStore } from 'stores/user';
const router = useRouter(); const router = useRouter();
const jApi = inject('jApi'); const jApi = inject('jApi');
const api = inject('api');
const { notify } = useNotify(); const { notify } = useNotify();
const { t } = useI18n(); const { t } = useI18n();
const { openConfirmationModal } = useVnConfirm(); const { openConfirmationModal } = useVnConfirm();
const appStore = useAppStore(); const appStore = useAppStore();
const userStore = useUserStore();
const { isHeaderMounted } = storeToRefs(appStore); const { isHeaderMounted } = storeToRefs(appStore);
const fetchAddressesRef = ref(null);
const addresses = ref([]); const addresses = ref([]);
const defaultAddress = ref(null); const defaultAddress = ref(null);
@ -38,19 +43,6 @@ const getDefaultAddress = async () => {
} }
}; };
const getActiveAddresses = async () => {
try {
addresses.value = await jApi.query(
`SELECT a.id, a.nickname, p.name province, a.postalCode, a.city, a.street, a.isActive
FROM myAddress a
LEFT JOIN vn.province p ON p.id = a.provinceFk
WHERE a.isActive`
);
} catch (error) {
console.error('Error getting active addresses:', error);
}
};
const changeDefaultAddress = async () => { const changeDefaultAddress = async () => {
if (!clientId.value) return; if (!clientId.value) return;
await jApi.execQuery( await jApi.execQuery(
@ -65,32 +57,46 @@ const changeDefaultAddress = async () => {
notify(t('defaultAddressModified'), 'positive'); notify(t('defaultAddressModified'), 'positive');
}; };
const removeAddress = async id => { async function removeAddress(address) {
try { try {
await jApi.execQuery( await api.patch(
`START TRANSACTION; `/Clients/${userStore?.user?.id}/updateAddress/${address.id}`,
UPDATE hedera.myAddress SET isActive = FALSE
WHERE ((id = #id));
SELECT isActive FROM hedera.myAddress WHERE ((id = #id));
COMMIT`,
{ {
id ...address,
isActive: false
} }
); );
getActiveAddresses(); fetchAddressesRef.value.fetch();
notify(t('dataSaved'), 'positive'); notify(t('dataSaved'), 'positive');
} catch (error) { } catch (error) {
console.error('Error removing address:', error); console.error('Error removing address:', error);
} }
}; }
onMounted(async () => { onMounted(async () => {
getDefaultAddress(); getDefaultAddress();
getActiveAddresses();
}); });
</script> </script>
<template> <template>
<FetchData
v-if="userStore?.user?.id"
ref="fetchAddressesRef"
url="Addresses"
:filter="{
where: { clientFk: userStore.user.id, isActive: true },
fields: [
'id',
'nickname',
'postalCode',
'city',
'street',
'isActive'
]
}"
auto-load
@on-fetch="data => (addresses = data)"
/>
<Teleport v-if="isHeaderMounted" to="#actions"> <Teleport v-if="isHeaderMounted" to="#actions">
<QBtn <QBtn
:label="t('addAddress')" :label="t('addAddress')"
@ -145,7 +151,7 @@ onMounted(async () => {
openConfirmationModal( openConfirmationModal(
null, null,
t('confirmDeleteAddress'), t('confirmDeleteAddress'),
() => removeAddress(address.id) () => removeAddress(address)
) )
" "
> >

View File

@ -1,16 +1,15 @@
<script setup> <script setup>
import { ref, onMounted, inject } from 'vue'; import { ref, onMounted, inject } from 'vue';
const jApi = inject('jApi'); const jApi = inject('jApi');
const api = inject('api');
const news = ref([]); const news = ref([]);
const showPreview = ref(false); const showPreview = ref(false);
const selectedImageSrc = ref(''); const selectedImageSrc = ref('');
const fetchData = async () => { const fetchData = async () => {
news.value = await jApi.query( const newsResponse = await api.get('News');
`SELECT title, text, image, id
FROM news news.value = newsResponse.data;
ORDER BY priority, created DESC`
);
}; };
const showImagePreview = src => { const showImagePreview = src => {

View File

@ -32,7 +32,10 @@ export const useAppStore = defineStore('hedera', {
}), }),
actions: { actions: {
async getMenuLinks() { async getMenuLinks() {
const sections = await jApi.query('SELECT * FROM myMenu'); const { data: sections } = await api.get('MyMenus');
if (!sections) return;
const sectionMap = new Map(); const sectionMap = new Map();
for (const section of sections) { for (const section of sections) {
sectionMap.set(section.id, section); sectionMap.set(section.id, section);

View File

@ -248,11 +248,10 @@ export const useUserStore = defineStore('user', () => {
const fetchUser = async (userType = 'user') => { const fetchUser = async (userType = 'user') => {
try { try {
const userData = await jApi.getObject( const userData = await api.get('VnUsers/getCurrentUserData');
'SELECT id, nickname, name, lang FROM account.myUser'
); if (userType === 'user') mainUser.value = userData.data;
if (userType === 'user') mainUser.value = userData; else supplantedUser.value = userData.data;
else supplantedUser.value = userData;
} catch (error) { } catch (error) {
console.error('Error fetching user: ', error); console.error('Error fetching user: ', error);
} }

View File

@ -46,15 +46,28 @@ describe('PendingOrders', () => {
.should('contain', data.postcode); .should('contain', data.postcode);
}; };
it('should fail if we enter a wrong postcode', () => {
cy.dataCy('newAddressBtn').should('exist');
cy.dataCy('newAddressBtn').click();
cy.dataCy('formModelDefaultSaveButton').should('exist');
cy.dataCy('formModelDefaultSaveButton').should('be.disabled');
const addressFormData = getRandomAddressFormData();
fillFormWithData(addressFormData);
cy.dataCy('formModelDefaultSaveButton').should('not.be.disabled');
cy.dataCy('formModelDefaultSaveButton').click();
cy.checkNotify('negative');
});
it('should create a new address', () => { it('should create a new address', () => {
cy.dataCy('newAddressBtn').should('exist'); cy.dataCy('newAddressBtn').should('exist');
cy.dataCy('newAddressBtn').click(); cy.dataCy('newAddressBtn').click();
cy.dataCy('formDefaultSaveButton').should('exist'); cy.dataCy('formModelDefaultSaveButton').should('exist');
cy.dataCy('formDefaultSaveButton').should('be.disabled'); cy.dataCy('formModelDefaultSaveButton').should('be.disabled');
const addressFormData = getRandomAddressFormData(); const addressFormData = getRandomAddressFormData();
addressFormData.postcode = '46460'; // Usamos un postcode válido
fillFormWithData(addressFormData); fillFormWithData(addressFormData);
cy.dataCy('formDefaultSaveButton').should('not.be.disabled'); cy.dataCy('formModelDefaultSaveButton').should('not.be.disabled');
cy.dataCy('formDefaultSaveButton').click(); cy.dataCy('formModelDefaultSaveButton').click();
cy.checkNotify('positive', 'Datos guardados'); cy.checkNotify('positive', 'Datos guardados');
verifyAddressCardData(addressFormData); verifyAddressCardData(addressFormData);
}); });
@ -71,9 +84,10 @@ describe('PendingOrders', () => {
}); });
// Fill form with new data // Fill form with new data
const addressFormData = getRandomAddressFormData(); const addressFormData = getRandomAddressFormData();
addressFormData.postcode = '46460'; // Usamos un postcode válido
fillFormWithData(addressFormData); fillFormWithData(addressFormData);
cy.dataCy('formDefaultSaveButton').should('not.be.disabled'); cy.dataCy('formModelDefaultSaveButton').should('not.be.disabled');
cy.dataCy('formDefaultSaveButton').click(); cy.dataCy('formModelDefaultSaveButton').click();
cy.checkNotify('positive', 'Datos guardados'); cy.checkNotify('positive', 'Datos guardados');
verifyAddressCardData(addressFormData); verifyAddressCardData(addressFormData);
}); });

View File

@ -90,5 +90,6 @@ Cypress.Commands.add('setConfirmDialog', () => {
}); });
Cypress.Commands.add('checkNotify', (status, content) => { Cypress.Commands.add('checkNotify', (status, content) => {
cy.dataCy(`${status}Notify`).should('contain', content); if (content) cy.dataCy(`${status}Notify`).should('contain', content);
else cy.dataCy(`${status}Notify`).should('exist');
}); });