forked from verdnatura/hedera-web
WIP
This commit is contained in:
parent
0d3da684b4
commit
ec0d783672
|
@ -38,6 +38,7 @@ export default boot(({ app }) => {
|
||||||
|
|
||||||
app.config.globalProperties.$jApi = jApi;
|
app.config.globalProperties.$jApi = jApi;
|
||||||
app.provide('jApi', jApi);
|
app.provide('jApi', jApi);
|
||||||
|
app.provide('api', api);
|
||||||
});
|
});
|
||||||
|
|
||||||
export { api, jApi };
|
export { api, jApi };
|
||||||
|
|
|
@ -0,0 +1,207 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref, inject, onMounted, computed } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import useNotify from 'src/composables/useNotify.js';
|
||||||
|
import {
|
||||||
|
generateUpdateSqlQuery,
|
||||||
|
generateInsertSqlQuery
|
||||||
|
} from 'src/js/db/sqlService.js';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
table: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
// Objeto que define las pks de la tabla. Usado para generar las queries sql correspondientes.
|
||||||
|
// Debe ser definido como un objeto de pares key-value, donde la clave es el nombre de la columna de la pk.
|
||||||
|
pks: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
createModelDefault: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({
|
||||||
|
field: '',
|
||||||
|
value: ''
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// Objeto que contiene la consulta SQL y los parámetros necesarios para obtener los datos iniciales del formulario.
|
||||||
|
// `query` debe ser una cadena de texto que representa la consulta SQL.
|
||||||
|
// `params` es un objeto que mapea los parámetros de la consulta a sus valores.
|
||||||
|
fetchFormDataSql: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({
|
||||||
|
query: '',
|
||||||
|
params: {}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// Objeto con los datos iniciales del form, si este objeto es definido, no se ejecuta la query fetchFormDataSql
|
||||||
|
formInitialData: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
// Array de columnas que no se deben actualizar
|
||||||
|
columnsToIgnoreUpdate: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
autoLoad: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
isEditMode: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
defaultActions: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
saveFn: {
|
||||||
|
type: Function,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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 updatedColumns = 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
const submit = async () => {
|
||||||
|
try {
|
||||||
|
if (props.saveFn) {
|
||||||
|
await props.saveFn();
|
||||||
|
} else {
|
||||||
|
const sqlQuery = generateSqlQuery();
|
||||||
|
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 = () => {
|
||||||
|
if (props.isEditMode) {
|
||||||
|
return generateUpdateSqlQuery(
|
||||||
|
props.schema,
|
||||||
|
props.table,
|
||||||
|
props.pks,
|
||||||
|
updatedColumns.value,
|
||||||
|
formData.value
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return generateInsertSqlQuery(
|
||||||
|
props.schema,
|
||||||
|
props.table,
|
||||||
|
formData.value,
|
||||||
|
updatedColumns.value,
|
||||||
|
props.createModelDefault
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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="!updatedColumns.length"
|
||||||
|
@click="addressFormRef.submit()"
|
||||||
|
/>
|
||||||
|
</Teleport>
|
||||||
|
<QCard class="form-container" v-bind="$attrs">
|
||||||
|
<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>
|
|
@ -0,0 +1,87 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref, inject } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
import VnForm from 'src/components/common/VnForm.vue';
|
||||||
|
|
||||||
|
import { userStore as useUserStore } from 'stores/user';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const api = inject('api');
|
||||||
|
const vnFormRef = ref(null);
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
const repeatPassword = ref('');
|
||||||
|
|
||||||
|
const formData = ref({
|
||||||
|
userId: userStore.id,
|
||||||
|
oldPassword: '',
|
||||||
|
newPassword: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
const changePassword = async () => {
|
||||||
|
try {
|
||||||
|
// TODO: Add validation
|
||||||
|
await api.patch('Accounts/change-password', formData.value);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<VnForm
|
||||||
|
ref="vnFormRef"
|
||||||
|
:title="t('changePassword')"
|
||||||
|
:formInitialData="formData"
|
||||||
|
:defaultActions="false"
|
||||||
|
style="max-width: 300px"
|
||||||
|
>
|
||||||
|
<template #form>
|
||||||
|
<VnInput
|
||||||
|
v-model="formData.oldPassword"
|
||||||
|
:label="t('oldPassword')"
|
||||||
|
type="password"
|
||||||
|
/>
|
||||||
|
<VnInput
|
||||||
|
v-model="formData.newPassword"
|
||||||
|
:label="t('newPassword')"
|
||||||
|
type="password"
|
||||||
|
/>
|
||||||
|
<VnInput
|
||||||
|
v-model="repeatPassword"
|
||||||
|
:label="t('repeatPassword')"
|
||||||
|
type="password"
|
||||||
|
/>
|
||||||
|
<QBtn @click="changePassword" label="saveeeee" />
|
||||||
|
</template>
|
||||||
|
</VnForm>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n lang="yaml">
|
||||||
|
en-US:
|
||||||
|
changePassword: Change password
|
||||||
|
newPassword: New password
|
||||||
|
oldPassword: Old password
|
||||||
|
repeatPassword: Repeat password
|
||||||
|
es-ES:
|
||||||
|
changePassword: Cambiar contraseña
|
||||||
|
newPassword: Nueva contraseña
|
||||||
|
oldPassword: Contraseña antigua
|
||||||
|
repeatPassword: Repetir contraseña
|
||||||
|
ca-ES:
|
||||||
|
changePassword: Canviar contrasenya
|
||||||
|
newPassword: Nova contrasenya
|
||||||
|
oldPassword: Contrasenya antiga
|
||||||
|
repeatPassword: Repetir contrasenya
|
||||||
|
fr-FR:
|
||||||
|
changePassword: Changer le mot de passe
|
||||||
|
newPassword: Nouveau mot de passe
|
||||||
|
oldPassword: Ancien mot de passe
|
||||||
|
repeatPassword: Répéter le mot de passe
|
||||||
|
pt-PT:
|
||||||
|
changePassword: Alterar palavra-passe
|
||||||
|
newPassword: Nova palavra-passe
|
||||||
|
oldPassword: Palavra-passe antiga
|
||||||
|
repeatPassword: Repetir palavra-passe
|
||||||
|
</i18n>
|
|
@ -0,0 +1,42 @@
|
||||||
|
export const generateUpdateSqlQuery = (
|
||||||
|
schema,
|
||||||
|
table,
|
||||||
|
pks,
|
||||||
|
columnsUpdated,
|
||||||
|
formData
|
||||||
|
) => {
|
||||||
|
const setClauses = columnsUpdated
|
||||||
|
.map(colName => `${colName} = '${formData[colName]}'`)
|
||||||
|
.join(', ');
|
||||||
|
const whereClause = Object.keys(pks)
|
||||||
|
.map(pk => `${pk} = ${pks[pk]}`)
|
||||||
|
.join(' AND ');
|
||||||
|
|
||||||
|
return `
|
||||||
|
START TRANSACTION;
|
||||||
|
UPDATE ${schema}.${table} SET ${setClauses} WHERE (${whereClause});
|
||||||
|
SELECT ${columnsUpdated.join(', ')} FROM ${schema}.${table} WHERE (${whereClause});
|
||||||
|
COMMIT;
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const generateInsertSqlQuery = (
|
||||||
|
schema,
|
||||||
|
table,
|
||||||
|
formData,
|
||||||
|
columnsUpdated,
|
||||||
|
createModelDefault
|
||||||
|
) => {
|
||||||
|
const columns = [createModelDefault.field, ...columnsUpdated].join(', ');
|
||||||
|
const values = [
|
||||||
|
createModelDefault.value,
|
||||||
|
...columnsUpdated.map(colName => `'${formData[colName]}'`)
|
||||||
|
].join(', ');
|
||||||
|
|
||||||
|
return `
|
||||||
|
START TRANSACTION;
|
||||||
|
INSERT INTO ${schema}.${table} (${columns}) VALUES (${values});
|
||||||
|
SELECT id, ${columnsUpdated.join(', ')} FROM ${schema}.${table} WHERE (id = LAST_INSERT_ID());
|
||||||
|
COMMIT;
|
||||||
|
`;
|
||||||
|
};
|
|
@ -1,9 +0,0 @@
|
||||||
<script setup></script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<QPage> // TODO: VISTA A DESARROLLAR! </QPage>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
|
||||||
|
|
||||||
<i18n lang="yaml"></i18n>
|
|
|
@ -0,0 +1,148 @@
|
||||||
|
<script setup>
|
||||||
|
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 { userStore as useUserStore } from 'stores/user';
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const jApi = inject('jApi');
|
||||||
|
|
||||||
|
const vnFormRef = ref(null);
|
||||||
|
const changePasswordFormDialog = ref(null);
|
||||||
|
const showChangePasswordForm = ref(false);
|
||||||
|
const langOptions = ref([]);
|
||||||
|
const pks = computed(() => ({ id: userStore.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 fetchLanguagesSql = async () => {
|
||||||
|
try {
|
||||||
|
const data = await jApi.query(
|
||||||
|
'SELECT code, name FROM language WHERE isActive'
|
||||||
|
);
|
||||||
|
langOptions.value = data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => fetchLanguagesSql());
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<QPage>
|
||||||
|
<QPage class="q-pa-md flex justify-center">
|
||||||
|
<Teleport :to="$actions">
|
||||||
|
<QBtn
|
||||||
|
:label="t('addresses')"
|
||||||
|
icon="location_on"
|
||||||
|
rounded
|
||||||
|
no-caps
|
||||||
|
:to="{ name: 'AddressesList' }"
|
||||||
|
/>
|
||||||
|
<QBtn
|
||||||
|
:label="t('changePassword')"
|
||||||
|
icon="lock_reset"
|
||||||
|
rounded
|
||||||
|
no-caps
|
||||||
|
@click="showChangePasswordForm = true"
|
||||||
|
/>
|
||||||
|
</Teleport>
|
||||||
|
<VnForm
|
||||||
|
ref="vnFormRef"
|
||||||
|
:title="t('personalInformation')"
|
||||||
|
:fetchFormDataSql="fetchConfigDataSql"
|
||||||
|
:pks="pks"
|
||||||
|
table="myUser"
|
||||||
|
schema="account"
|
||||||
|
:defaultActions="false"
|
||||||
|
>
|
||||||
|
<template #form="{ data }">
|
||||||
|
<VnInput
|
||||||
|
v-model="data.name"
|
||||||
|
:label="t('name')"
|
||||||
|
disable
|
||||||
|
:clearable="false"
|
||||||
|
/>
|
||||||
|
<VnInput v-model="data.email" :label="t('email')" />
|
||||||
|
<VnInput v-model="data.nickname" :label="t('nickname')" />
|
||||||
|
<VnSelect
|
||||||
|
v-model="data.lang"
|
||||||
|
:label="t('lang')"
|
||||||
|
option-label="name"
|
||||||
|
option-value="code"
|
||||||
|
:options="langOptions"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</VnForm>
|
||||||
|
</QPage>
|
||||||
|
<QDialog
|
||||||
|
ref="changePasswordFormDialog"
|
||||||
|
v-model="showChangePasswordForm"
|
||||||
|
>
|
||||||
|
<ChangePasswordForm />
|
||||||
|
</QDialog>
|
||||||
|
</QPage>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
|
|
||||||
|
<i18n lang="yaml">
|
||||||
|
en-US:
|
||||||
|
personalInformation: Personal Information
|
||||||
|
name: Name
|
||||||
|
email: Email
|
||||||
|
nickname: Display name
|
||||||
|
lang: Language
|
||||||
|
receiveInvoicesByMail: Receive invoices by mail
|
||||||
|
addresses: Addresses
|
||||||
|
changePassword: Change password
|
||||||
|
es-ES:
|
||||||
|
personalInformation: Datos personales
|
||||||
|
name: Nombre
|
||||||
|
email: Correo electrónico
|
||||||
|
nickname: Nombre a mostrar
|
||||||
|
lang: Idioma
|
||||||
|
receiveInvoicesByMail: Recibir facturas por correo
|
||||||
|
addresses: Direcciones
|
||||||
|
changePassword: Cambiar contraseña
|
||||||
|
ca-ES:
|
||||||
|
personalInformation: Dades personals
|
||||||
|
name: Nom
|
||||||
|
email: Correu electrònic
|
||||||
|
nickname: Nom a mostrar
|
||||||
|
lang: Idioma
|
||||||
|
receiveInvoicesByMail: Rebre factures per correu
|
||||||
|
addresses: Adreces
|
||||||
|
changePassword: Canviar contrasenya
|
||||||
|
fr-FR:
|
||||||
|
personalInformation: Informations personnelles
|
||||||
|
name: Nom
|
||||||
|
email: E-mail
|
||||||
|
nickname: Nom à afficher
|
||||||
|
lang: Langue
|
||||||
|
receiveInvoicesByMail: Recevoir des factures par courrier
|
||||||
|
addresses: Adresses
|
||||||
|
changePassword: Changer le mot de passe
|
||||||
|
pt-PT:
|
||||||
|
personalInformation: Dados pessoais
|
||||||
|
name: Nome
|
||||||
|
email: E-mail
|
||||||
|
nickname: Nom à afficher
|
||||||
|
lang: Língua
|
||||||
|
receiveInvoicesByMail: Receber faturas por correio
|
||||||
|
addresses: Endereços
|
||||||
|
changePassword: Alterar palavra-passe
|
||||||
|
</i18n>
|
|
@ -57,10 +57,10 @@ const routes = [
|
||||||
{
|
{
|
||||||
name: 'Account',
|
name: 'Account',
|
||||||
path: '/account/conf',
|
path: '/account/conf',
|
||||||
component: () => import('pages/Account/AccountConf.vue')
|
component: () => import('pages/Account/AccountConfig.vue')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Addresses',
|
name: 'AddressesList',
|
||||||
path: '/account/address-list',
|
path: '/account/address-list',
|
||||||
component: () => import('pages/Account/AddressList.vue')
|
component: () => import('pages/Account/AddressList.vue')
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue