Modulo Administración #78

Merged
jsegarra merged 19 commits from wbuezas/hedera-web-mindshore:feature/Administracion into 4922-vueMigration 2024-08-23 19:29:46 +00:00
7 changed files with 302 additions and 10 deletions
Showing only changes of commit 7837925be9 - Show all commits

View File

@ -75,6 +75,10 @@ const props = defineProps({
saveFn: { saveFn: {
type: Function, type: Function,
default: null default: null
},
separationBetweenInputs: {
type: String,
default: 'xs'
} }
}); });
@ -105,6 +109,10 @@ const updatedColumns = computed(() => {
const hasChanges = computed(() => !!updatedColumns.value.length); const hasChanges = computed(() => !!updatedColumns.value.length);
const separationBetweenInputs = computed(() => {
return `q-gutter-y-${props.separationBetweenInputs}`;
});
const fetchFormData = async () => { const fetchFormData = async () => {
if (!props.fetchFormDataSql.query) return; if (!props.fetchFormDataSql.query) return;
loading.value = true; loading.value = true;
@ -173,6 +181,8 @@ const generateSqlQuery = () => {
onMounted(async () => { onMounted(async () => {
if (!props.formInitialData && props.autoLoad) { if (!props.formInitialData && props.autoLoad) {
fetchFormData(); fetchFormData();
} else {
formData.value = { ...props.formInitialData };
} }
}); });
@ -187,10 +197,11 @@ defineExpose({
<QForm <QForm
v-if="!loading" v-if="!loading"
ref="addressFormRef" ref="addressFormRef"
class="column full-width q-gutter-y-xs" class="column full-width"
:class="separationBetweenInputs"
@submit="submit()" @submit="submit()"
> >
<span class="text-h6 text-bold"> <span v-if="title" class="text-h6 text-bold">
{{ title }} {{ title }}
</span> </span>
<slot name="form" :data="formData" /> <slot name="form" :data="formData" />
@ -220,7 +231,7 @@ defineExpose({
flat flat
:disabled="!showBottomActions && !updatedColumns.length" :disabled="!showBottomActions && !updatedColumns.length"
/> />
<slot name="actions" /> <slot name="actions" :data="formData" />
</component> </component>
</QForm> </QForm>
<QSpinner v-else color="primary" size="3em" :thickness="2" /> <QSpinner v-else color="primary" size="3em" :thickness="2" />

View File

@ -102,20 +102,25 @@ en-US:
name: Name name: Name
file: File file: File
send: Send send: Send
imageAdded: Image added successfully
es-ES: es-ES:
name: Nombre name: Nombre
file: Archivo file: Archivo
send: Enviar send: Enviar
imageAdded: Imagen añadida correctamente
ca-ES: ca-ES:
name: Nom name: Nom
file: Arxiu file: Arxiu
send: Enviar send: Enviar
imageAdded: Imatge afegida correctament
fr-FR: fr-FR:
name: Nom name: Nom
file: Fichier file: Fichier
send: Envoyer send: Envoyer
imageAdded: Image ajoutée correctement
pt-PT: pt-PT:
name: Nome name: Nome
file: Arquivo file: Arquivo
send: Enviar send: Enviar
imageAdded: Imagen adicionada corretamente
</i18n> </i18n>

View File

@ -59,6 +59,5 @@ export default {
adminPhotos: 'Imatges', adminPhotos: 'Imatges',
// //
orderLoadedIntoBasket: 'Comanda carregada a la cistella!', orderLoadedIntoBasket: 'Comanda carregada a la cistella!',
at: 'a les', at: 'a les'
imageAdded: 'Imatge afegida correctament'
}; };

View File

@ -73,7 +73,6 @@ export default {
// //
orderLoadedIntoBasket: 'Order loaded into basket!', orderLoadedIntoBasket: 'Order loaded into basket!',
at: 'at', at: 'at',
imageAdded: 'Image added successfully',
orders: 'Orders', orders: 'Orders',
order: 'Pending order', order: 'Pending order',

View File

@ -59,6 +59,5 @@ export default {
adminPhotos: 'Images', adminPhotos: 'Images',
// //
orderLoadedIntoBasket: 'Commande chargée dans le panier!', orderLoadedIntoBasket: 'Commande chargée dans le panier!',
at: 'à', at: 'à'
imageAdded: 'Image ajoutée correctement'
}; };

View File

@ -60,6 +60,5 @@ export default {
adminPhotos: 'Imagens', adminPhotos: 'Imagens',
// //
orderLoadedIntoBasket: 'Pedido carregado na cesta!', orderLoadedIntoBasket: 'Pedido carregado na cesta!',
at: 'às', at: 'às'
imageAdded: 'Imagen adicionada corretamente'
}; };

View File

@ -0,0 +1,280 @@
<script setup>
import { useI18n } from 'vue-i18n';
import { ref, onMounted, inject, reactive, computed } from 'vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import VnForm from 'src/components/common/VnForm.vue';
import VnInput from 'src/components/common/VnInput.vue';
import useNotify from 'src/composables/useNotify.js';
const jApi = inject('jApi');
const api = inject('api');
const { t } = useI18n();
const { notify } = useNotify();
const fileUploaderRef = ref(null);
const statusIcons = {
uploading: 'cloud_upload',
fulfilled: 'cloud_done',
rejected: 'error',
pending: 'add'
};
const formInitialData = reactive({
schema: 'catalog',
updateMatching: true
});
const imageCollections = ref([]);
const addedFiles = ref([]);
const isSubmitable = computed(() =>
addedFiles.value.some(file => file.uploadStatus === 'pending')
);
const getImageCollections = async () => {
try {
imageCollections.value = await jApi.query(
'SELECT name, `desc` FROM imageCollection ORDER BY `desc`'
);
} catch (error) {
console.error('Error getting image collections:', error);
}
};
const onSubmit = async data => {
if (!addedFiles.value.length) {
notify(t('noFilesToUpload'), 'warning');
return;
}
const filteredFiles = addedFiles.value.filter(
file => file.uploadStatus === 'pending'
);
const promises = filteredFiles.map((file, index) => {
const fileIndex = filteredFiles[index].index;
addedFiles.value[fileIndex].uploadStatus = 'uploading';
const formData = new FormData();
formData.append('updateMatching', data.updateMatching);
formData.append('image', file.file);
formData.append('name', file.name);
formData.append('schema', data.schema);
formData.append('srv', 'json:image/upload');
return api({
method: 'post',
url: location.origin,
data: formData,
headers: {
'Content-Type': 'multipart/form-data'
}
});
});
const results = await Promise.allSettled(promises);
results.forEach((result, index) => {
const fileIndex = filteredFiles[index].index;
addedFiles.value[fileIndex].uploadStatus = result.status;
});
const allSuccessful = results.every(
result => result.status === 'fulfilled'
);
if (allSuccessful) {
notify(t('uploadSuccess'), 'positive');
} else {
notify(t('uploadError'), 'negative');
}
};
const onFilesAdded = files => {
const initialFilesLength = addedFiles.value.length;
files.forEach((file, index) => {
const [name] = file.name.split('.');
const fileData = {
name,
file,
index: initialFilesLength + index,
uploadStatus: 'pending'
};
addedFiles.value.push(fileData);
});
};
const recalculateFilesIndexes = () => {
addedFiles.value.forEach((_, index) => {
addedFiles.value[index].index = index;
});
};
const removeFile = (file, index) => {
fileUploaderRef.value.removeFile(file);
addedFiles.value.splice(index, 1);
recalculateFilesIndexes();
};
const clearFiles = () => {
fileUploaderRef.value.reset();
addedFiles.value = [];
};
onMounted(async () => getImageCollections());
</script>
<template>
<QPage class="vn-w-sm">
<VnForm
ref="vnFormRef"
:defaultActions="false"
:formInitialData="formInitialData"
separationBetweenInputs="md"
showBottomActions
>
<template #form="{ data }">
<VnSelect
v-model="data.schema"
:label="t('collection')"
option-label="desc"
option-value="name"
:options="imageCollections"
/>
<QUploader
ref="fileUploaderRef"
:label="t('dropYourFiles')"
class="full-width"
square
flat
multiple
bordered
hide-upload-btn
@added="onFilesAdded"
>
<template v-slot:list="scope">
<QList v-if="addedFiles.length" separator>
<QItem
v-for="(file, index) in scope.files"
:key="file.__key"
class="flex full-width row items-center justify-center"
>
<img
:src="file.__img.src"
style="width: 28px; height: 21px"
class="q-mr-md"
/>
<VnInput
v-model="addedFiles[index].name"
:clearable="false"
dense
class="full-width"
/>
<QSpinner
v-if="
addedFiles[index].uploadStatus ===
'uploading'
"
color="primary"
size="2em"
:thickness="1"
/>
<QIcon
v-else-if="
addedFiles[index].uploadStatus &&
addedFiles[index].uploadStatus !==
'uploading'
"
:name="
statusIcons[
addedFiles[index].uploadStatus
]
"
size="sm"
/>
<QBtn
v-if="
addedFiles[index].uploadStatus !==
'uploading'
"
class="gt-xs"
size="md"
flat
dense
round
icon="delete"
@click="removeFile(file, index)"
/>
</QItem>
</QList>
</template>
</QUploader>
<QCheckbox
v-model="data.updateMatching"
:label="t('updateMatching')"
/>
</template>
<template #actions="{ data }">
<QBtn
:label="t('clearAll')"
rounded
no-caps
flat
@click="clearFiles()"
/>
<QBtn
:label="t('uploadFiles')"
rounded
no-caps
flat
:disable="!isSubmitable"
@click="onSubmit(data)"
/>
</template>
</VnForm>
</QPage>
</template>
<style lang="scss" scoped></style>
<i18n lang="yaml">
en-US:
collection: Collection
updateMatching: Update items with matching id
dropYourFiles: Click or drop files here
clearAll: Clear all
uploadFiles: Upload files
uploadSuccess: Upload finished successfully
uploadError: Some errors happened on upload
noFilesToUpload: There are no files to upload
es-ES:
collection: Colección
updateMatching: Actualizar artículos con id coincidente
dropYourFiles: Pulsa o suelta los archivos aquí
clearAll: Limpiar todo
uploadFiles: Subir archivos
uploadSuccess: Imágenes subidas correctamente
uploadError: Ocurrieron errores al subir alguna de las imágenes
noFilesToUpload: No se han seleccionado archivos para subir
ca-ES:
collection: Col·lecció
updateMatching: Actualitzar els elements amb id coincident
dropYourFiles: Prem o deixa anar els arxius aquí
clearAll: Netejar tot
uploadFiles: Pujar arxius
uploadSuccess: Imatges pujades correctament
uploadError: Van ocórrer errors en pujar alguna de les imatges
noFilesToUpload: No s'ha seleccionat arxius per pujar
fr-FR:
collection: Collection
updateMatching: Mettre à jour les éléments avec l'identifiant correspondant
dropYourFiles: Cliquez ici ou déposer des fichiers
clearAll: Tout effacer
uploadFiles: Upload Files
uploadSuccess: Les images téléchargées correctement
uploadError: Des erreurs sont survenues lors du téléchargement des images
noFilesToUpload: Aucun fichier sélectionné pour télécharger
pt-PT:
collection: Coleção
updateMatching: Atualizar itens com id correspondente
dropYourFiles: Clique ou solte arquivos aqui
clearAll: Limpar tudo
uploadFiles: Fazer upload de arquivos
uploadSuccess: Upload concluído com sucesso
uploadError: Ocorreram erros ao subir alguma das imagens
noFilesToUpload: Não arquivos selecionados para upload
</i18n>