forked from verdnatura/hedera-web
Components creation: AddressDetails, VnForm, VnInput and VnSelect
This commit is contained in:
parent
0d3da684b4
commit
dcbc154caa
|
@ -0,0 +1,214 @@
|
|||
<script setup>
|
||||
import { ref, inject, onMounted, computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
import useNotify from 'src/composables/useNotify.js';
|
||||
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
table: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
schema: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
pks: {
|
||||
type: Object,
|
||||
default: () => {} // definidas como key: value, donde key es el nombre de la pk
|
||||
},
|
||||
createModelDefault: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
field: '',
|
||||
value: ''
|
||||
})
|
||||
},
|
||||
fetchFormDataSql: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
// Objeto con query y params para obtener los datos iniciales del form
|
||||
query: '',
|
||||
params: {}
|
||||
})
|
||||
},
|
||||
formInitialData: {
|
||||
type: Object,
|
||||
default: () => {} // Objeto con los datos iniciales del form
|
||||
},
|
||||
columnsToIgnoreUpdate: {
|
||||
type: Array,
|
||||
default: () => [] // Array de columnas que no se deben actualizar
|
||||
},
|
||||
autoLoad: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'update'
|
||||
},
|
||||
defaultActions: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['onDataSaved']);
|
||||
|
||||
const { t } = useI18n();
|
||||
const jApi = inject('jApi');
|
||||
const { notify } = useNotify();
|
||||
|
||||
const loading = ref(false);
|
||||
const formData = ref({});
|
||||
const addressFormRef = ref(null);
|
||||
const modelInfo = ref(null);
|
||||
// Array de nombre de columnas de la tabla
|
||||
const tableColumns = computed(
|
||||
() => modelInfo.value?.columns.map(col => col.name) || []
|
||||
);
|
||||
// Array de nombre de columnas que fueron actualizadas y no estan en columnsToIgnoreUpdate
|
||||
const columnsUpdated = computed(() => {
|
||||
return tableColumns.value.filter(
|
||||
colName =>
|
||||
modelInfo.value?.data[0][colName] !== formData.value[colName] &&
|
||||
!props.columnsToIgnoreUpdate.includes(colName)
|
||||
);
|
||||
});
|
||||
|
||||
const fetchFormData = async () => {
|
||||
if (!props.fetchFormDataSql.query) return;
|
||||
loading.value = true;
|
||||
const { results } = await jApi.execQuery(
|
||||
props.fetchFormDataSql.query,
|
||||
props.fetchFormDataSql.params
|
||||
);
|
||||
|
||||
modelInfo.value = results[0];
|
||||
|
||||
if (!modelInfo.value.data[0]) {
|
||||
modelInfo.value.data[0] = {};
|
||||
// Si no existen datos iniciales, se inicializan con null, en base a las columnas de la tabla
|
||||
modelInfo.value.columns.forEach(
|
||||
col => (modelInfo.value.data[0][col.name] = null)
|
||||
);
|
||||
}
|
||||
|
||||
formData.value = { ...modelInfo.value.data[0] };
|
||||
loading.value = false;
|
||||
console.log('modelInfo: ', modelInfo.value);
|
||||
};
|
||||
|
||||
const submit = async () => {
|
||||
try {
|
||||
const sqlQuery = generateSqlQuery(props.table, props.schema, props.pks);
|
||||
await jApi.execQuery(sqlQuery, props.pks);
|
||||
emit('onDataSaved');
|
||||
notify(t('dataSaved'), 'positive');
|
||||
} catch (error) {
|
||||
console.error('Error updating address:', error);
|
||||
notify(t('addressNotUpdated'), 'negative');
|
||||
}
|
||||
};
|
||||
|
||||
const generateSqlQuery = (table, schema, pks) => {
|
||||
if (props.mode === 'update') {
|
||||
return generateUpdateSqlQuery(table, schema, pks);
|
||||
} else {
|
||||
return generateCreateSqlQuery(table, schema, pks);
|
||||
}
|
||||
};
|
||||
|
||||
const generateUpdateSqlQuery = (table, schema, pks) => {
|
||||
if (!columnsUpdated.value.length) return '';
|
||||
|
||||
const setClauses = columnsUpdated.value
|
||||
.map(colName => `${colName} = '${formData.value[colName]}'`)
|
||||
.join(', ');
|
||||
const whereClause = Object.keys(props.pks)
|
||||
.map(pk => `${pk} = ${pks[pk]}`)
|
||||
.join(' AND ');
|
||||
return `
|
||||
START TRANSACTION;
|
||||
UPDATE ${schema}.${table} SET ${setClauses} WHERE (${whereClause});
|
||||
SELECT ${columnsUpdated.value.join(', ')} FROM ${schema}.${table} WHERE (${whereClause});
|
||||
COMMIT;
|
||||
`;
|
||||
};
|
||||
|
||||
const generateCreateSqlQuery = (table, schema) => {
|
||||
if (!columnsUpdated.value.length) return '';
|
||||
|
||||
const columns = [
|
||||
props.createModelDefault.field,
|
||||
...columnsUpdated.value
|
||||
].join(', ');
|
||||
const values = [
|
||||
props.createModelDefault.value,
|
||||
...columnsUpdated.value.map(colName => `'${formData.value[colName]}'`)
|
||||
].join(', ');
|
||||
|
||||
return `
|
||||
START TRANSACTION;
|
||||
INSERT INTO ${schema}.${table} (${columns}) VALUES (${values});
|
||||
SELECT id, ${columnsUpdated.value.join(', ')} FROM ${schema}.${table} WHERE ((id = LAST_INSERT_ID()));
|
||||
COMMIT;
|
||||
`;
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
if (!props.formInitialData && props.autoLoad) {
|
||||
fetchFormData();
|
||||
}
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
formData,
|
||||
submit
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Teleport :to="$actions">
|
||||
<QBtn
|
||||
v-if="defaultActions"
|
||||
:label="t('save')"
|
||||
type="submit"
|
||||
icon="check"
|
||||
rounded
|
||||
no-caps
|
||||
:disabled="!columnsUpdated.length"
|
||||
@click="addressFormRef.submit()"
|
||||
/>
|
||||
</Teleport>
|
||||
<QCard class="form-container">
|
||||
<QForm
|
||||
v-if="!loading"
|
||||
ref="addressFormRef"
|
||||
class="column full-width q-gutter-y-xs"
|
||||
@submit="submit()"
|
||||
>
|
||||
<span class="text-h6 text-bold">
|
||||
{{ title }}
|
||||
</span>
|
||||
<slot name="form" :data="formData" />
|
||||
</QForm>
|
||||
<QSpinner v-else color="primary" size="3em" :thickness="2" />
|
||||
</QCard>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.form-container {
|
||||
width: 100%;
|
||||
height: max-content;
|
||||
max-width: 544px;
|
||||
padding: 32px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,124 @@
|
|||
<script setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const emit = defineEmits([
|
||||
'update:modelValue',
|
||||
'update:options',
|
||||
'keyup.enter',
|
||||
'remove'
|
||||
]);
|
||||
|
||||
const $props = defineProps({
|
||||
modelValue: {
|
||||
type: [String, Number],
|
||||
default: null
|
||||
},
|
||||
isOutlined: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
info: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
const requiredFieldRule = val => !!val || t('globals.fieldRequired');
|
||||
const vnInputRef = ref(null);
|
||||
const value = computed({
|
||||
get() {
|
||||
return $props.modelValue;
|
||||
},
|
||||
set(value) {
|
||||
emit('update:modelValue', value);
|
||||
}
|
||||
});
|
||||
const hover = ref(false);
|
||||
const styleAttrs = computed(() => {
|
||||
return $props.isOutlined
|
||||
? { dense: true, outlined: true, rounded: true }
|
||||
: {};
|
||||
});
|
||||
|
||||
const focus = () => {
|
||||
vnInputRef.value.focus();
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
focus
|
||||
});
|
||||
|
||||
const inputRules = [
|
||||
val => {
|
||||
const { min } = vnInputRef.value.$attrs;
|
||||
if (min >= 0) {
|
||||
if (Math.floor(val) < min) return t('inputMin', { value: min });
|
||||
}
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:rules="$attrs.required ? [requiredFieldRule] : null"
|
||||
@mouseover="hover = true"
|
||||
@mouseleave="hover = false"
|
||||
>
|
||||
<QInput
|
||||
ref="vnInputRef"
|
||||
v-model="value"
|
||||
v-bind="{ ...$attrs, ...styleAttrs }"
|
||||
:type="$attrs.type"
|
||||
:class="{ required: $attrs.required }"
|
||||
:clearable="false"
|
||||
:rules="inputRules"
|
||||
:lazy-rules="true"
|
||||
hide-bottom-space
|
||||
@keyup.enter="emit('keyup.enter')"
|
||||
>
|
||||
<template v-if="$slots.prepend" #prepend>
|
||||
<slot name="prepend" />
|
||||
</template>
|
||||
<template #append>
|
||||
<slot v-if="$slots.append && !$attrs.disabled" name="append" />
|
||||
<QIcon
|
||||
v-if="
|
||||
hover && value && !$attrs.disabled && $props.clearable
|
||||
"
|
||||
name="close"
|
||||
size="xs"
|
||||
@click="
|
||||
() => {
|
||||
value = null;
|
||||
emit('remove');
|
||||
}
|
||||
"
|
||||
/>
|
||||
<QIcon v-if="info" name="info">
|
||||
<QTooltip max-width="350px">
|
||||
{{ info }}
|
||||
</QTooltip>
|
||||
</QIcon>
|
||||
</template>
|
||||
</QInput>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
inputMin: Must be more than {value}
|
||||
es-ES:
|
||||
inputMin: Must be more than {value}
|
||||
ca-ES:
|
||||
inputMin: Ha de ser més gran que {value}
|
||||
fr-FR:
|
||||
inputMin: Doit être supérieur à {value}
|
||||
pt-PT:
|
||||
inputMin: Deve ser maior que {value}
|
||||
</i18n>
|
|
@ -0,0 +1,188 @@
|
|||
<script setup>
|
||||
import { ref, toRefs, computed, watch, onMounted } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
const emit = defineEmits(['update:modelValue', 'update:options']);
|
||||
|
||||
const $props = defineProps({
|
||||
modelValue: {
|
||||
type: [String, Number, Object],
|
||||
default: null
|
||||
},
|
||||
options: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
optionLabel: {
|
||||
type: [String],
|
||||
default: 'name'
|
||||
},
|
||||
optionValue: {
|
||||
type: String,
|
||||
default: 'id'
|
||||
},
|
||||
optionFilter: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
dataQuery: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
filterOptions: {
|
||||
type: [Array],
|
||||
default: () => []
|
||||
},
|
||||
isClearable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
defaultFilter: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
fields: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
where: {
|
||||
type: Object,
|
||||
default: null
|
||||
},
|
||||
sortBy: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
limit: {
|
||||
type: [Number, String],
|
||||
default: '30'
|
||||
},
|
||||
focusOnMount: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
useLike: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
const requiredFieldRule = val => val ?? t('globals.fieldRequired');
|
||||
|
||||
const { optionLabel, optionValue, options } = toRefs($props);
|
||||
const myOptions = ref([]);
|
||||
const myOptionsOriginal = ref([]);
|
||||
const vnSelectRef = ref();
|
||||
const lastVal = ref();
|
||||
|
||||
const value = computed({
|
||||
get() {
|
||||
return $props.modelValue;
|
||||
},
|
||||
set(value) {
|
||||
emit('update:modelValue', value);
|
||||
}
|
||||
});
|
||||
|
||||
watch(options, newValue => {
|
||||
setOptions(newValue);
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
setOptions(options.value);
|
||||
if ($props.focusOnMount) {
|
||||
setTimeout(() => vnSelectRef.value.showPopup(), 300);
|
||||
}
|
||||
});
|
||||
|
||||
function setOptions(data) {
|
||||
myOptions.value = JSON.parse(JSON.stringify(data));
|
||||
myOptionsOriginal.value = JSON.parse(JSON.stringify(data));
|
||||
}
|
||||
|
||||
function filter(val, options) {
|
||||
const search = val.toString().toLowerCase();
|
||||
|
||||
if (!search) return options;
|
||||
|
||||
return options.filter(row => {
|
||||
if ($props.filterOptions.length) {
|
||||
return $props.filterOptions.some(prop => {
|
||||
const propValue = String(row[prop]).toLowerCase();
|
||||
return propValue.includes(search);
|
||||
});
|
||||
}
|
||||
|
||||
const id = row.id;
|
||||
const optionLabel = String(row[$props.optionLabel]).toLowerCase();
|
||||
|
||||
return id === search || optionLabel.includes(search);
|
||||
});
|
||||
}
|
||||
|
||||
async function filterHandler(val, update) {
|
||||
if (!val && lastVal.value === val) {
|
||||
lastVal.value = val;
|
||||
return update();
|
||||
}
|
||||
lastVal.value = val;
|
||||
|
||||
if (!$props.defaultFilter) return update();
|
||||
const newOptions = filter(val, myOptionsOriginal.value);
|
||||
update(
|
||||
() => {
|
||||
myOptions.value = newOptions;
|
||||
},
|
||||
ref => {
|
||||
if (val !== '' && ref.options.length > 0) {
|
||||
ref.setOptionIndex(-1);
|
||||
ref.moveOptionSelection(1, true);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<QSelect
|
||||
v-model="value"
|
||||
:options="myOptions"
|
||||
:option-label="optionLabel"
|
||||
:option-value="optionValue"
|
||||
v-bind="$attrs"
|
||||
emit-value
|
||||
map-options
|
||||
use-input
|
||||
@filter="filterHandler"
|
||||
hide-selected
|
||||
fill-input
|
||||
ref="vnSelectRef"
|
||||
lazy-rules
|
||||
:class="{ required: $attrs.required }"
|
||||
:rules="$attrs.required ? [requiredFieldRule] : null"
|
||||
virtual-scroll-slice-size="options.length"
|
||||
>
|
||||
<template v-if="isClearable" #append>
|
||||
<QIcon
|
||||
v-show="value"
|
||||
name="close"
|
||||
@click.stop="value = null"
|
||||
class="cursor-pointer"
|
||||
size="xs"
|
||||
/>
|
||||
</template>
|
||||
<template
|
||||
v-for="(_, slotName) in $slots"
|
||||
#[slotName]="slotData"
|
||||
:key="slotName"
|
||||
>
|
||||
<slot :name="slotName" v-bind="slotData ?? {}" :key="slotName" />
|
||||
</template>
|
||||
</QSelect>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.q-field--outlined {
|
||||
max-width: 100%;
|
||||
}
|
||||
</style>
|
|
@ -74,5 +74,6 @@ export default {
|
|||
user: 'User',
|
||||
addresses: 'Addresses',
|
||||
addressEdit: 'Edit address',
|
||||
dataSaved: 'Data saved'
|
||||
dataSaved: 'Data saved',
|
||||
save: 'Save'
|
||||
};
|
||||
|
|
|
@ -74,5 +74,6 @@ export default {
|
|||
user: 'Usuario',
|
||||
addresses: 'Direcciones',
|
||||
addressEdit: 'Editar dirección',
|
||||
dataSaved: 'Datos guardados'
|
||||
dataSaved: 'Datos guardados',
|
||||
save: 'Guardar'
|
||||
};
|
||||
|
|
|
@ -1,33 +1,169 @@
|
|||
<script setup>
|
||||
import { ref, inject, onMounted, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
|
||||
import VnInput from 'src/components/common/VnInput.vue';
|
||||
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||
import VnForm from 'src/components/common/VnForm.vue';
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const { t } = useI18n();
|
||||
const jApi = inject('jApi');
|
||||
|
||||
const vnFormRef = ref(null);
|
||||
const countriesOptions = ref([]);
|
||||
const provincesOptions = ref([]);
|
||||
const pks = { id: route.params.id };
|
||||
const formMode = route.params.id === '0' ? 'create' : 'update';
|
||||
const fetchAddressDataSql = {
|
||||
query: `
|
||||
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(
|
||||
() => vnFormRef?.value?.formData?.countryFk,
|
||||
async val => await getProvinces(val)
|
||||
);
|
||||
|
||||
const goBack = () => router.push({ name: 'AddressesList' });
|
||||
|
||||
const getCountries = async () => {
|
||||
countriesOptions.value = await jApi.query(
|
||||
`SELECT id, name FROM vn.country
|
||||
ORDER BY name`
|
||||
);
|
||||
};
|
||||
|
||||
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 }
|
||||
);
|
||||
};
|
||||
|
||||
onMounted(() => getCountries());
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Teleport :to="$actions">
|
||||
<QBtn icon="close" :label="t('back')" rounded no-caps />
|
||||
<QBtn icon="check" :label="t('accept')" rounded no-caps />
|
||||
</Teleport>
|
||||
<QPage>//TODO: VISTA A DESARROLLAR!</QPage>
|
||||
<QPage class="q-pa-md flex justify-center">
|
||||
<Teleport :to="$actions">
|
||||
<QBtn
|
||||
:label="t('back')"
|
||||
icon="close"
|
||||
rounded
|
||||
no-caps
|
||||
@click="goBack()"
|
||||
/>
|
||||
</Teleport>
|
||||
<VnForm
|
||||
ref="vnFormRef"
|
||||
:fetchFormDataSql="fetchAddressDataSql"
|
||||
:columnsToIgnoreUpdate="['countryFk']"
|
||||
:createModelDefault="{
|
||||
field: 'clientFk',
|
||||
value: 'account.myUser_getId()'
|
||||
}"
|
||||
:pks="pks"
|
||||
:mode="formMode"
|
||||
:title="t('addEditAddress')"
|
||||
table="myAddress"
|
||||
schema="hedera"
|
||||
@onDataSaved="goBack()"
|
||||
>
|
||||
<template #form="{ data }">
|
||||
<VnInput v-model="data.nickname" :label="t('name')" />
|
||||
<VnInput v-model="data.street" :label="t('address')" />
|
||||
<VnInput v-model="data.city" :label="t('city')" />
|
||||
<VnInput v-model="data.postalCode" :label="t('postalCode')" />
|
||||
<VnSelect
|
||||
v-model="data.countryFk"
|
||||
:label="t('country')"
|
||||
:options="countriesOptions"
|
||||
@update:modelValue="data.provinceFk = null"
|
||||
/>
|
||||
<VnSelect
|
||||
v-model="data.provinceFk"
|
||||
:label="t('province')"
|
||||
:options="provincesOptions"
|
||||
/>
|
||||
</template>
|
||||
</VnForm>
|
||||
</QPage>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
<style lang="scss" scoped>
|
||||
.form-container {
|
||||
width: 100%;
|
||||
height: max-content;
|
||||
max-width: 544px;
|
||||
padding: 32px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<i18n lang="yaml">
|
||||
en-US:
|
||||
back: Back
|
||||
accept: Accept
|
||||
addEditAddress: Add or edit address
|
||||
name: Consignee
|
||||
address: Address
|
||||
city: City
|
||||
postalCode: Zip code
|
||||
country: Country
|
||||
province: Province
|
||||
addressChangedSuccessfully: Address changed successfully
|
||||
es-ES:
|
||||
back: Volver
|
||||
accept: Aceptar
|
||||
addEditAddress: Añadir o modificar dirección
|
||||
name: Consignatario
|
||||
address: Morada
|
||||
city: Ciudad
|
||||
postalCode: Código postal
|
||||
country: País
|
||||
province: Distrito
|
||||
addressChangedSuccessfully: Dirección modificada correctamente
|
||||
ca-ES:
|
||||
back: Tornar
|
||||
accept: Acceptar
|
||||
addEditAddress: Afegir o modificar adreça
|
||||
name: Consignatari
|
||||
address: Direcció
|
||||
city: Ciutat
|
||||
postalCode: Codi postal
|
||||
country: País
|
||||
province: Província
|
||||
addressChangedSuccessfully: Adreça modificada correctament
|
||||
fr-FR:
|
||||
back: Retour
|
||||
accept: Accepter
|
||||
addEditAddress: Ajouter ou modifier l'adresse
|
||||
name: Destinataire
|
||||
address: Numéro Rue
|
||||
city: Ville
|
||||
postalCode: Code postal
|
||||
country: Pays
|
||||
province: Province
|
||||
addressChangedSuccessfully: Adresse modifié avec succès
|
||||
pt-PT:
|
||||
back: Voltar
|
||||
accept: Aceitar
|
||||
addEditAddress: Adicionar ou modificar morada
|
||||
name: Consignatario
|
||||
address: Morada
|
||||
city: Concelho
|
||||
postalCode: Código postal
|
||||
country: País
|
||||
province: Distrito
|
||||
addressChangedSuccessfully: Morada modificada corretamente
|
||||
</i18n>
|
||||
|
|
|
@ -60,7 +60,7 @@ const routes = [
|
|||
component: () => import('pages/Account/AccountConf.vue')
|
||||
},
|
||||
{
|
||||
name: 'Addresses',
|
||||
name: 'AddressesList',
|
||||
path: '/account/address-list',
|
||||
component: () => import('pages/Account/AddressList.vue')
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue