+
-
+
-
{
value = null;
+ vnInputRef.focus();
emit('remove');
}
"
- />
+ >
+
+
+ {{ t('Convert to uppercase') }}
+
+
+
+
+
{{ info }}
@@ -120,20 +197,44 @@ const inputRules = [
+
+
en-US:
inputMin: Must be more than {value}
+ maxLength: The value exceeds {value} characters
inputMax: Must be less than {value}
+ Convert to uppercase: Convert to uppercase
es-ES:
inputMin: Debe ser mayor a {value}
+ maxLength: El valor excede los {value} carácteres
inputMax: Debe ser menor a {value}
+ Convert to uppercase: Convertir a mayúsculas
ca-ES:
inputMin: Ha de ser més gran que {value}
- inputMax: Ha de ser menys que {value}
+ maxLength: El valor excedeix els {value} caràcters
+ inputMax: Ha de ser menor que {value}
+ Convert to uppercase: Convertir a majúscules
fr-FR:
inputMin: Doit être supérieur à {value}
- inputMax: Doit être supérieur à {value}
+ maxLength: La valeur dépasse {value} caractères
+ inputMax: Doit être inférieur à {value}
+ Convert to uppercase: Convertir en majuscules
pt-PT:
inputMin: Deve ser maior que {value}
- inputMax: Deve ser maior que {value}
+ maxLength: O valor excede {value} caracteres
+ inputMax: Deve ser menor que {value}
+ Convert to uppercase: Converter para maiúsculas
diff --git a/src/components/common/VnSelect.vue b/src/components/common/VnSelect.vue
index c4a3e1df..65dbb2f2 100644
--- a/src/components/common/VnSelect.vue
+++ b/src/components/common/VnSelect.vue
@@ -67,7 +67,7 @@ const $props = defineProps({
});
const { t } = useI18n();
-const requiredFieldRule = val => val ?? t('globals.fieldRequired');
+const requiredFieldRule = val => val ?? t('fieldRequired');
const { optionLabel, optionValue, options } = toRefs($props);
const myOptions = ref([]);
diff --git a/src/composables/useRequired.js b/src/composables/useRequired.js
new file mode 100644
index 00000000..a77345b5
--- /dev/null
+++ b/src/composables/useRequired.js
@@ -0,0 +1,17 @@
+import { useI18n } from 'vue-i18n';
+
+export function useRequired($attrs) {
+ const { t } = useI18n();
+
+ const isRequired =
+ typeof $attrs['required'] === 'boolean'
+ ? $attrs['required']
+ : Object.keys($attrs).includes('required');
+ const requiredFieldRule = val =>
+ isRequired ? !!val || t('fieldRequired') : null;
+
+ return {
+ isRequired,
+ requiredFieldRule
+ };
+}
diff --git a/src/i18n/ca-ES/index.js b/src/i18n/ca-ES/index.js
index 456795a5..48d214e0 100644
--- a/src/i18n/ca-ES/index.js
+++ b/src/i18n/ca-ES/index.js
@@ -134,6 +134,8 @@ export default {
introduceSearchTerm: 'Introdueix un terme de cerca',
noOrdersFound: `No s'han trobat comandes`,
send: 'Enviar',
+ fieldRequired: 'Aquest camp és obligatori',
+ noChanges: 'No s’han fet canvis',
// Image related translations
'Cant lock cache': 'No es pot bloquejar la memòria cau',
'Bad file format': 'Format de fitxer no reconegut',
diff --git a/src/i18n/en-US/index.js b/src/i18n/en-US/index.js
index 3e21b66d..80107dd5 100644
--- a/src/i18n/en-US/index.js
+++ b/src/i18n/en-US/index.js
@@ -167,6 +167,8 @@ export default {
introduceSearchTerm: 'Enter a search term',
noOrdersFound: 'No orders found',
send: 'Send',
+ fieldRequired: 'Field required',
+ noChanges: 'No changes',
// Image related translations
'Cant lock cache': 'The cache could not be blocked',
'Bad file format': 'Unrecognized file format',
diff --git a/src/i18n/es-ES/index.js b/src/i18n/es-ES/index.js
index 5034ff70..15639c2d 100644
--- a/src/i18n/es-ES/index.js
+++ b/src/i18n/es-ES/index.js
@@ -166,6 +166,8 @@ export default {
introduceSearchTerm: 'Introduce un término de búsqueda',
noOrdersFound: 'No se encontrado pedidos',
send: 'Enviar',
+ fieldRequired: 'Campo requerido',
+ noChanges: 'No se han hecho cambios',
// Image related translations
'Cant lock cache': 'La caché no pudo ser bloqueada',
'Bad file format': 'Formato de archivo no reconocido',
diff --git a/src/i18n/fr-FR/index.js b/src/i18n/fr-FR/index.js
index d30dee52..2eea9a38 100644
--- a/src/i18n/fr-FR/index.js
+++ b/src/i18n/fr-FR/index.js
@@ -134,6 +134,8 @@ export default {
introduceSearchTerm: 'Entrez un terme de recherche',
noOrdersFound: 'Aucune commande trouvée',
send: 'Envoyer',
+ fieldRequired: 'Champ obligatoire',
+ noChanges: 'Aucun changement',
// Image related translations
'Cant lock cache': "Le cache n'a pas pu être verrouillé",
'Bad file format': 'Format de fichier non reconnu',
diff --git a/src/i18n/pt-PT/index.js b/src/i18n/pt-PT/index.js
index 39bbe5de..460335e0 100644
--- a/src/i18n/pt-PT/index.js
+++ b/src/i18n/pt-PT/index.js
@@ -133,6 +133,8 @@ export default {
introduceSearchTerm: 'Digite um termo de pesquisa',
noOrdersFound: 'Nenhum pedido encontrado',
send: 'Enviar',
+ fieldRequired: 'Campo obrigatório',
+ noChanges: 'Sem alterações',
// Image related translations
'Cant lock cache': 'O cache não pôde ser bloqueado',
'Bad file format': 'Formato de arquivo inválido',
diff --git a/src/pages/Account/AccountConfig.vue b/src/pages/Account/AccountConfig.vue
index 0aec3240..edbb181e 100644
--- a/src/pages/Account/AccountConfig.vue
+++ b/src/pages/Account/AccountConfig.vue
@@ -3,8 +3,9 @@ import { ref, inject, onMounted, computed } from 'vue';
import { useI18n } from 'vue-i18n';
import VnInput from 'src/components/common/VnInput.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
-import VnForm from 'src/components/common/VnForm.vue';
import ChangePasswordForm from 'src/components/ui/ChangePasswordForm.vue';
+import FormModel from 'src/components/common/FormModel.vue';
+import useNotify from 'src/composables/useNotify.js';
import { useUserStore } from 'stores/user';
import { useAppStore } from 'stores/app';
@@ -12,54 +13,76 @@ import { storeToRefs } from 'pinia';
const userStore = useUserStore();
const { t } = useI18n();
-const jApi = inject('jApi');
+const api = inject('api');
const appStore = useAppStore();
const { isHeaderMounted } = storeToRefs(appStore);
const { user } = storeToRefs(userStore);
+const { notify } = useNotify();
const vnFormRef = ref(null);
-const vnFormRef2 = ref(null);
const changePasswordFormDialog = ref(null);
const showChangePasswordForm = ref(false);
const langOptions = ref([]);
-const pks = computed(() => ({ id: userStore?.user?.id }));
-const fetchConfigDataSql = {
- query: `
- SELECT u.id, u.name, u.email, u.nickname,
- u.lang, c.isToBeMailed, c.id clientFk
- FROM account.myUser u
- LEFT JOIN myClient c
- ON u.id = c.id`,
- params: {}
-};
+const formInitialData = ref({});
+const showForm = ref(false);
-const fetchLanguagesSql = async () => {
+const fetchLanguages = async () => {
try {
- const data = await jApi.query(
- 'SELECT code, name FROM language WHERE isActive'
- );
+ const filter = { fields: ['code', 'name'], where: { isActive: true } };
+ const { data } = await api.get('/languages', {
+ params: { filter: JSON.stringify(filter) }
+ });
langOptions.value = data;
} catch (error) {
console.error(error);
}
};
+const fetchFormInitialData = async () => {
+ try {
+ const filter = {
+ where: { id: user?.value?.id },
+ fields: ['id', 'name', 'isToBeMailed'],
+ include: {
+ relation: 'user',
+ scope: {
+ fields: ['nickname', 'lang', 'email']
+ }
+ }
+ };
+ const { data } = await api.get('/Clients', {
+ params: { filter: JSON.stringify(filter) }
+ });
+
+ const { user: userData, ...restOfData } = data[0];
+
+ formInitialData.value = {
+ ...restOfData,
+ nickname: userData?.nickname,
+ lang: userData?.lang,
+ email: userData?.email
+ };
+ } catch (error) {
+ console.error(error);
+ } finally {
+ showForm.value = true;
+ }
+};
+
const updateUserNickname = async nickname => {
try {
- await vnFormRef.value.submit();
+ await submitAccountData(nickname);
+ await submitNickname(nickname);
user.value.nickname = nickname;
} catch (error) {
console.error(error);
}
};
-const formatMailData = data => {
- data.isToBeMailed = Boolean(data.isToBeMailed);
-};
-
const updateConfigLang = async lang => {
try {
- await vnFormRef.value.submit();
+ if (!lang) return;
+ await submitAccountData({ lang });
userStore.updateUserLang(lang);
const siteLocaleLang = appStore.localeOptions.find(
locale => locale.value === lang
@@ -70,7 +93,32 @@ const updateConfigLang = async lang => {
}
};
-onMounted(() => fetchLanguagesSql());
+const submitAccountData = async data => {
+ try {
+ const params = {
+ ...data
+ };
+ await api.patch(`/VnUsers/${user?.value?.id}`, params);
+ notify(t('dataSaved'), 'positive');
+ } catch (error) {
+ console.error(error);
+ }
+};
+
+const submitIsToBeMailed = async isToBeMailed => {
+ try {
+ const payload = { isToBeMailed };
+ await api.patch(`/Clients/${user.value.id}`, payload);
+ notify(t('dataSaved'), 'positive');
+ } catch (error) {
+ console.error(error);
+ }
+};
+
+onMounted(async () => {
+ fetchLanguages();
+ fetchFormInitialData();
+});
@@ -92,14 +140,14 @@ onMounted(() => fetchLanguagesSql());
@click="showChangePasswordForm = true"
/>
-
fetchLanguagesSql());
fetchLanguagesSql());
@update:model-value="updateConfigLang(data.lang)"
data-cy="configViewLang"
/>
+
-
- formatMailData($event)"
- >
-
-
-
-
-
-
+
vnFormRef?.value?.formData?.countryFk,
@@ -40,23 +34,49 @@ watch(
const goBack = () => router.push({ name: 'addressesList' });
const getCountries = async () => {
- countriesOptions.value = await jApi.query(
- `SELECT id, name FROM vn.country
- ORDER BY name`
- );
+ const filter = { fields: ['id', 'name'], order: 'name' };
+ const { data } = await api.get('Countries', {
+ params: { filter: JSON.stringify(filter) }
+ });
+ countriesOptions.value = data;
};
const getProvinces = async countryFk => {
if (!countryFk) return;
- provincesOptions.value = await jApi.query(
- `SELECT id, name FROM vn.province
- WHERE countryFk = #id
- ORDER BY name`,
- { id: countryFk }
- );
+
+ const filter = {
+ where: { countryFk },
+ fields: ['id', 'name'],
+ 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;
+});
@@ -74,42 +94,49 @@ onMounted(() => getCountries());
-
getCountries());
:options="countriesOptions"
@update:model-value="data.provinceFk = null"
data-cy="addressFormCountry"
+ required
/>
-
+
diff --git a/src/pages/Account/AddressList.vue b/src/pages/Account/AddressList.vue
index a603715c..9939c926 100644
--- a/src/pages/Account/AddressList.vue
+++ b/src/pages/Account/AddressList.vue
@@ -5,19 +5,24 @@ import { useRouter } from 'vue-router';
import CardList from 'src/components/ui/CardList.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 { useVnConfirm } from 'src/composables/useVnConfirm.js';
import { useAppStore } from 'stores/app';
import { storeToRefs } from 'pinia';
+import { useUserStore } from 'stores/user';
const router = useRouter();
const jApi = inject('jApi');
+const api = inject('api');
const { notify } = useNotify();
const { t } = useI18n();
const { openConfirmationModal } = useVnConfirm();
const appStore = useAppStore();
+const userStore = useUserStore();
const { isHeaderMounted } = storeToRefs(appStore);
+const fetchAddressesRef = ref(null);
const addresses = ref([]);
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 () => {
if (!clientId.value) return;
await jApi.execQuery(
@@ -65,32 +57,46 @@ const changeDefaultAddress = async () => {
notify(t('defaultAddressModified'), 'positive');
};
-const removeAddress = async id => {
+async function removeAddress(address) {
try {
- await jApi.execQuery(
- `START TRANSACTION;
- UPDATE hedera.myAddress SET isActive = FALSE
- WHERE ((id = #id));
- SELECT isActive FROM hedera.myAddress WHERE ((id = #id));
- COMMIT`,
+ await api.patch(
+ `/Clients/${userStore?.user?.id}/updateAddress/${address.id}`,
{
- id
+ ...address,
+ isActive: false
}
);
- getActiveAddresses();
+ fetchAddressesRef.value.fetch();
notify(t('dataSaved'), 'positive');
} catch (error) {
console.error('Error removing address:', error);
}
-};
+}
onMounted(async () => {
getDefaultAddress();
- getActiveAddresses();
});
+ (addresses = data)"
+ />
{
openConfirmationModal(
null,
t('confirmDeleteAddress'),
- () => removeAddress(address.id)
+ () => removeAddress(address)
)
"
>
diff --git a/src/pages/Admin/UsersView.vue b/src/pages/Admin/UsersView.vue
index 33de8f70..c72167f2 100644
--- a/src/pages/Admin/UsersView.vue
+++ b/src/pages/Admin/UsersView.vue
@@ -4,7 +4,7 @@ import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
import CardList from 'src/components/ui/CardList.vue';
-import VnSearchBar from 'src/components/ui/VnSearchBar.vue';
+import VnSearchBar from 'src/components/ui/NewVnSearchBar.vue';
import VnList from 'src/components/ui/VnList.vue';
import { useAppStore } from 'stores/app';
@@ -16,16 +16,11 @@ const router = useRouter();
const userStore = useUserStore();
const appStore = useAppStore();
const { isHeaderMounted } = storeToRefs(appStore);
-
const loading = ref(false);
const users = ref([]);
-
-const query = `SELECT u.id, u.name, u.nickname, u.active
- FROM account.user u
- WHERE u.name LIKE CONCAT('%', #user, '%')
- OR u.nickname LIKE CONCAT('%', #user, '%')
- OR u.id = #user
- ORDER BY u.name LIMIT 200`;
+const filter = {
+ fields: ['id', 'name', 'nickname', 'active']
+};
const onSearch = data => (users.value = data || []);
@@ -38,15 +33,35 @@ const supplantUser = async user => {
console.error('Error supplanting user:', error);
}
};
+
+const usersExprBuilder = (param, value) => {
+ switch (param) {
+ case 'search':
+ return /^\d+$/.test(value)
+ ? { id: value }
+ : {
+ or: [
+ { name: { like: `%${value}%` } },
+ { nickname: { like: `%${value}%` } }
+ ]
+ };
+ case 'name':
+ case 'nickname':
+ return { [param]: { like: `%${value}%` } };
+ case 'roleFk':
+ return { [param]: value };
+ }
+};
diff --git a/src/pages/Cms/HomeView.vue b/src/pages/Cms/HomeView.vue
index 442dfbc9..7dff6165 100644
--- a/src/pages/Cms/HomeView.vue
+++ b/src/pages/Cms/HomeView.vue
@@ -1,16 +1,15 @@