feature/FaltantesModuloTravel #185

Merged
jsegarra merged 17 commits from :feature/FaltantesModuloTravel into dev 2024-02-09 13:11:23 +00:00
8 changed files with 506 additions and 139 deletions
Showing only changes of commit 7b542dc3bb - Show all commits

View File

@ -9,6 +9,7 @@ const user = ref({
lang: '',
darkMode: null,
companyFk: null,
warehouseFk: null,
});
const roles = ref([]);

View File

@ -1078,7 +1078,7 @@ export default {
travelCreate: 'New travel',
basicData: 'Basic data',
history: 'Log',
thermographs: 'Termographs',
thermographs: 'Thermograph',
},
summary: {
confirmed: 'Confirmed',
@ -1112,12 +1112,19 @@ export default {
delivered: 'Delivered',
received: 'Received',
},
termographs: {
thermographs: {
code: 'Code',
temperature: 'Temperature',
state: 'State',
destination: 'Destination',
created: 'Created',
thermograph: 'Thermograph',
reference: 'Reference',
type: 'Type',
company: 'Company',
warehouse: 'Warehouse',
travelFileDescription: 'Travel id { travelId }',
file: 'File',
},
},
item: {

View File

@ -1112,12 +1112,19 @@ export default {
delivered: 'Enviada',
received: 'Recibida',
},
termographs: {
thermographs: {
code: 'Código',
temperature: 'Temperatura',
state: 'Estado',
destination: 'Destino',
created: 'Fecha creación',
thermograph: 'Termógrafo',
reference: 'Referencia',
type: 'Tipo',
company: 'Empresa',
warehouse: 'Almacén',
travelFileDescription: 'Id envío { travelId }',
file: 'Fichero',
},
},
item: {

View File

@ -17,7 +17,6 @@ const stateStore = useStateStore();
<QPageContainer>
<QPage>
<VnSubToolbar />
<div class="q-pa-md"><RouterView></RouterView></div>
</QPage>
</QPageContainer>

View File

@ -1,133 +0,0 @@
<script setup>
import { ref, computed } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
import VnPaginate from 'src/components/ui/VnPaginate.vue';
import axios from 'axios';
// import useNotify from 'src/composables/useNotify.js';
import { toDate } from 'src/filters';
const route = useRoute();
// const router = useRouter();
const { t } = useI18n();
// const { notify } = useNotify();
const termographFilter = {
include: {
relation: 'warehouse',
scope: {
fields: ['id', 'name'],
},
},
};
const TableColumns = computed(() => {
return [
{
label: t('travel.termographs.code'),
field: 'thermographFk',
name: 'thermographFk',
align: 'left',
},
{
label: t('travel.termographs.temperature'),
field: 'temperatureFk',
name: 'temperatureFk',
align: 'left',
},
{
label: t('travel.termographs.state'),
field: 'result',
name: 'result',
align: 'left',
},
{
label: t('travel.termographs.destination'),
// field: (row) => row.warehouse.name,
field: 'warehouseFk',
name: 'destination',
align: 'left',
},
{
label: t('travel.termographs.created'),
field: 'created',
name: 'created',
align: 'left',
format: (val) => toDate(val),
},
];
});
const saveChange = async (rowData) => {
await axios.patch(`Buys/${rowData.id}`, rowData);
};
const openRemoveDialog = async () => {
// quasar
// .dialog({
// component: VnConfirm,
// componentProps: {
// title: t('Confirm deletion'),
// message:
// rowsSelected.value.length > 1
// ? t('Are you sure you want to delete this buys?')
// : t('Are you sure you want to delete this buy?'),
// data: rowsSelected.value,
// },
// })
// .onOk(async () => {
// try {
// await deleteBuys();
// const notifyMessage =
// rowsSelected.value.length > 1 ? t('Buys deleted') : t('Buy deleted');
// notify(notifyMessage, 'positive');
// } catch (err) {
// console.error('Error deleting buys');
// }
// });
};
const deleteTermograph = async () => {
// await axios.post('Buys/deleteBuys', { buys: rowsSelected.value });
// entryBuysPaginateRef.value.fetch();
};
const redirectToCreateView = () => {
// router.push({ name: 'EntryBuysImport' });
};
</script>
<template>
<VnPaginate
data-key="TravelTermograph"
url="TravelThermographs"
:filter="termographFilter"
:params="{ travelFk: route.params.id }"
auto-load
>
<template #body="{ rows }">
<QTable
:rows="rows"
:columns="TableColumns"
row-key="id"
hide-bottom
class="full-width q-mt-md"
>
</QTable>
</template>
</VnPaginate>
<QPageSticky :offset="[20, 20]">
<QBtn fab icon="add" color="primary" @click="redirectToCreateView()" />
<QTooltip>
{{ t('Add termograph') }}
</QTooltip>
</QPageSticky>
</template>
<i18n>
es:
Add termograph: Añadir termógrafo
</i18n>

View File

@ -0,0 +1,188 @@
<script setup>
import { computed } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar';
import VnPaginate from 'src/components/ui/VnPaginate.vue';
import VnConfirm from 'components/ui/VnConfirm.vue';
import axios from 'axios';
import useNotify from 'src/composables/useNotify.js';
import { toDate } from 'src/filters';
import { downloadFile } from 'src/composables/downloadFile';
const route = useRoute();
const quasar = useQuasar();
const router = useRouter();
const { t } = useI18n();
const { notify } = useNotify();
const thermographFilter = {
include: {
relation: 'warehouse',
scope: {
fields: ['id', 'name'],
},
},
where: { travelFk: route.params.id },
order: ['created'],
};
const TableColumns = computed(() => {
return [
{
label: t('travel.thermographs.code'),
field: 'thermographFk',
name: 'thermographFk',
align: 'left',
},
{
label: t('travel.thermographs.temperature'),
field: 'temperatureFk',
name: 'temperatureFk',
align: 'left',
},
{
label: t('travel.thermographs.state'),
field: 'result',
name: 'result',
align: 'left',
},
{
label: t('travel.thermographs.destination'),
field: 'warehouseFk',
name: 'destination',
align: 'left',
},
{
label: t('travel.thermographs.created'),
field: 'created',
name: 'created',
align: 'left',
format: (val) => toDate(val),
},
{
name: 'downloadFile',
align: 'left',
},
{
name: 'editFile',
align: 'left',
},
{
name: 'removeThermograph',
align: 'left',
},
];
});
const openRemoveDialog = async (id) => {
quasar
.dialog({
component: VnConfirm,
componentProps: {
message: t('Are you sure you want to remove the thermograph?'),
},
})
.onOk(async () => {
try {
await removeThermograph(id);
notify(t('Thermograph removed'), 'positive');
} catch (err) {
console.error('Error removing thermograph');
}
});
};
const redirectToThermographForm = (action) => {
router.push({
name: action === 'create' ? 'TravelThermographsCreate' : 'TravelThermographsEdit',
});
};
const removeThermograph = async (id) => {
await axios.delete('Travels/deleteThermograph', { id });
};
</script>
<template>
<VnPaginate
data-key="TravelThermographs"
url="TravelThermographs"
:filter="thermographFilter"
:params="{ travelFk: route.params.id }"
auto-load
>
<template #body="{ rows }">
<QTable
:rows="rows"
:columns="TableColumns"
row-key="id"
hide-bottom
class="full-width q-mt-md"
>
<template #body-cell-downloadFile="{ row }">
<QTd auto-width>
<QIcon
name="cloud_download"
color="primary"
size="sm"
class="cursor-pointer"
@click="downloadFile(row.dmsFk)"
>
<QTooltip>{{ t('Download file') }}</QTooltip>
</QIcon>
</QTd>
</template>
<template #body-cell-editFile>
<QTd auto-width>
<QIcon
name="edit"
color="primary"
size="sm"
class="cursor-pointer"
@click="redirectToThermographForm('edit')"
>
<QTooltip>{{ t('Edit file') }}</QTooltip>
</QIcon>
</QTd>
</template>
<template #body-cell-removeThermograph="{ row }">
<QTd auto-width>
<QIcon
name="delete"
color="primary"
size="sm"
class="cursor-pointer"
@click="openRemoveDialog(row.id)"
>
<QTooltip>{{ t('Remove thermograph') }}</QTooltip>
</QIcon>
</QTd>
</template>
</QTable>
</template>
</VnPaginate>
<QPageSticky :offset="[20, 20]">
<QBtn
fab
icon="add"
color="primary"
@click="redirectToThermographForm('create')"
/>
<QTooltip>
{{ t('Add thermograph') }}
</QTooltip>
</QPageSticky>
</template>
<i18n>
es:
Add thermograph: Añadir termógrafo
Download file: Descargar fichero
Edit file: Editar fichero
Remove thermograph: Eliminar termógrafo
Thermograph removed: Termógrafo eliminado
Are you sure you want to remove the thermograph?: ¿Seguro que quieres quitar el termógrafo?
</i18n>

View File

@ -0,0 +1,271 @@
<script setup>
jsegarra marked this conversation as resolved
Review

Desde el listado de termógrafos, si le das a editar te muestra el formulario pero:

  1. Te lanza esta llamada al api http://localhost:9000/api/TravelThermographs/[object%20Object]?filter=%7B%22include%22%3A%7B%22relation%22%3A%22dms%22%7D%7D
    . Como puedes ver tienes [object%20Object]
  2. La URL del edit queda algo así http://localhost:9000/#/travel/2/thermographs/[object%20Object]/edit
Desde el listado de termógrafos, si le das a editar te muestra el formulario pero: 1. Te lanza esta llamada al api http://localhost:9000/api/TravelThermographs/[object%20Object]?filter=%7B%22include%22%3A%7B%22relation%22%3A%22dms%22%7D%7D . Como puedes ver tienes [object%20Object] 2. La URL del edit queda algo así http://localhost:9000/#/travel/2/thermographs/[object%20Object]/edit
Review

Corregido.

Commit: 770f17a362

Corregido. Commit: https://gitea.verdnatura.es/verdnatura/salix-front/commit/770f17a3624ff4f8a08f677f9e90e02d2da3ba6c
import { useI18n } from 'vue-i18n';
jsegarra marked this conversation as resolved
Review

Si desde el listado de termógrafos, le das a eliminar te dice:

  1. Que ha habido un error
  2. Que se ha eliminado
  3. No elimina el registro de la tabla.
Si desde el listado de termógrafos, le das a eliminar te dice: 1. Que ha habido un error 2. Que se ha eliminado 3. No elimina el registro de la tabla.
Review

@jsegarra en salix me da el mismo comportamiento que en lilium, por ejemplo si vas a los thermographs del first travel

Aparecen 4 registros donde dos te deja borrar exitosamente y dos arroja un error el backend.

Adjunto captura tomada de salix:

@jsegarra en salix me da el mismo comportamiento que en lilium, por ejemplo si vas a los thermographs del [first travel](http://localhost:9000/travel/3/summary#/travel/1/thermographs/index?limit=10) Aparecen 4 registros donde dos te deja borrar exitosamente y dos arroja un error el backend. Adjunto captura tomada de `salix`:
Review

Se fixea el problema de las notificaciones.

Commit: f24b9f5a75

Se fixea el problema de las notificaciones. Commit: https://gitea.verdnatura.es/verdnatura/salix-front/commit/f24b9f5a75e55eb17903482da337d440189bcdb0
Review
  1. Si subes un fichero que es de tipo zip por ejemplo sólo te muestra el triangulo de advertencia en un toast rojo.
  2. Al crear te redirige a index pero la consola muestra errores. No sé muy bien si es por los errores o por el error que ocurre en mas partes de la aplicación.
  3. Darle a eliminar termógrafo, da error porque no se pasa el Id
1. Si subes un fichero que es de tipo zip por ejemplo sólo te muestra el triangulo de advertencia en un toast rojo. 2. Al crear te redirige a index pero la consola muestra errores. No sé muy bien si es por los errores o por el error que ocurre en mas partes de la aplicación. 3. Darle a eliminar termógrafo, da error porque no se pasa el Id
import { reactive, ref, onBeforeMount } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import FetchData from 'components/FetchData.vue';
import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
import VnRow from 'components/ui/VnRow.vue';
import VnInput from 'src/components/common/VnInput.vue';
import { useState } from 'src/composables/useState';
import { useStateStore } from 'stores/useStateStore';
import axios from 'axios';
import useNotify from 'src/composables/useNotify.js';
const props = defineProps({
viewAction: {
type: String,
default: 'create',
},
});
const stateStore = useStateStore();
const { t } = useI18n();
const route = useRoute();
const router = useRouter();
const state = useState();
const { notify } = useNotify();
const thermographFilter = {
fields: ['thermographFk'],
where: {
travelFk: null,
},
order: 'thermographFk ASC',
};
const user = state.getUser();
const allowedFileTypes = ref(null);
const thermographsOptions = ref([]);
const dmsTypesOptions = ref([]);
const companiesOptions = ref([]);
const warehousesOptions = ref([]);
const thermographForm = reactive({
thermographId: null,
state: null,
reference: null,
dmsTypeId: null,
companyId: null,
warehouseId: null,
files: [],
description: null,
});
onBeforeMount(() => {
getAllowedFileTypes();
if (props.viewAction === 'create') {
setCreateDefaultParams();
}
if (route.query.thermographData) {
const thermographData = JSON.parse(route.query.thermographData);
for (let key in thermographForm) {
thermographForm[key] = thermographData[key];
}
}
});
const getAllowedFileTypes = async () => {
try {
const { data } = await axios.get('DmsContainers/allowedContentTypes');
const contentTypes = data.join(', ');
allowedFileTypes.value = contentTypes;
} catch (err) {
console.error('Error fetching allowed content types');
}
};
const fetchDmsTypes = async () => {
try {
const params = {
filter: {
where: { code: 'miscellaneous' },
},
};
const { data } = await axios.get('DmsTypes/findOne', { params });
return data;
} catch (err) {
console.error('Error fetching Dms Types');
}
};
const setCreateDefaultParams = async () => {
const dataResponse = await fetchDmsTypes();
thermographForm.companyId = user.value.companyFk;
thermographForm.warehouseId = user.value.warehouseFk;
thermographForm.reference = route.params.id;
thermographForm.dmsTypeId = dataResponse.id;
thermographForm.state = 'Ok';
thermographForm.description = t('travel.thermographs.travelFileDescription', {
travelId: route.params.id,
}).toUpperCase();
};
const onSubmit = () => {
props.viewAction === 'create' ? createThermograph() : updateThermograph();
};
const createThermograph = async () => {
const formData = new FormData();
thermographForm.files.forEach((file) => {
formData.append(file.name, file);
});
try {
await axios.post(`/travels/${route.params.id}/createThermograph`, formData, {
params: thermographForm,
headers: {
'Content-Type': 'multipart/form-data',
},
});
router.push({ name: 'TravelThermographsIndex' });
notify(t('Thermograph created'), 'positive');
} catch (error) {
console.error('Error creating thermograph');
}
};
const updateThermograph = () => {
// `travels/${route.params.id}/updateThermograph`;
};
</script>
<template>
<FetchData
url="TravelThermographs"
@on-fetch="(data) => (thermographsOptions = data)"
:filter="thermographFilter"
auto-load
/>
<FetchData
url="DmsTypes"
:filter="{ order: 'name' }"
@on-fetch="(data) => (dmsTypesOptions = data)"
auto-load
/>
<FetchData
url="Companies"
@on-fetch="(data) => (companiesOptions = data)"
:filter="{ order: 'code' }"
auto-load
/>
<FetchData
url="Warehouses"
@on-fetch="(data) => (warehousesOptions = data)"
:filter="{ order: 'name' }"
auto-load
/>
<QPage class="column items-center full-width">
<QForm
model="travel"
:form-initial-data="thermographForm"
:observe-form-changes="viewAction === 'create'"
:default-actions="true"
@submit="onSubmit()"
class="full-width"
style="max-width: 800px"
>
<Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown()">
<div>
<QBtnGroup push class="q-gutter-x-sm">
<slot name="moreActions" />
<QBtn
color="primary"
icon="restart_alt"
flat
@click="reset()"
:label="t('globals.reset')"
/>
<QBtn
color="primary"
icon="save"
@click="onSubmit()"
:label="t('globals.save')"
/>
</QBtnGroup>
</div>
</Teleport>
<QCard class="q-pa-lg">
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelectFilter
:label="t('travel.thermographs.thermograph')"
v-model="thermographForm.thermographId"
:options="thermographsOptions"
option-value="thermographFk"
option-label="thermographFk"
/>
</div>
<div class="col">
<VnInput
v-model="thermographForm.state"
:label="t('travel.thermographs.state')"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnInput
v-model="thermographForm.reference"
:label="t('travel.thermographs.reference')"
/>
</div>
<div class="col">
<VnSelectFilter
:label="t('travel.thermographs.type')"
v-model="thermographForm.dmsTypeId"
:options="dmsTypesOptions"
option-value="id"
option-label="name"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<VnSelectFilter
:label="t('travel.thermographs.company')"
v-model="thermographForm.companyId"
:options="companiesOptions"
option-value="id"
option-label="code"
/>
</div>
<div class="col">
<VnSelectFilter
jsegarra marked this conversation as resolved
Review

No hay opción de crear termógrafo
Y por tanto no se puede evaluar el diálogo

No hay opción de crear termógrafo Y por tanto no se puede evaluar el diálogo
Review

Probe y pude acceder con el boton (+) que esta abajo a la derecha, que se encuentra en la vista de TravelThermographs, no te aparecio a vos?

Probe y pude acceder con el boton `(+)` que esta abajo a la derecha, que se encuentra en la vista de `TravelThermographs`, no te aparecio a vos?
Review

@jsegarra ahora si, cree el form necesario para crear un thermograph y lo aplique al input correspondiente.

Ademas de esto que era el requerimiento principal, implemente algunas cosas como:

  • Un estado de error en los inputs VnInput y VnSelectFilter cuando se le agrega la prop :required="true"
  • Algunas traducciones al objeto global para reutilizar
  • Implementacion del icon (i) con informacion sobre los tipos de files aceptados por el input QFile en el componente TravelThermographsForm

Commit: c67f4cf858

@jsegarra ahora si, cree el form necesario para crear un `thermograph` y lo aplique al input correspondiente. Ademas de esto que era el requerimiento principal, implemente algunas cosas como: - Un estado de error en los inputs `VnInput` y `VnSelectFilter` cuando se le agrega la prop `:required="true"` - Algunas traducciones al objeto global para reutilizar - Implementacion del icon `(i)` con informacion sobre los tipos de files aceptados por el input `QFile` en el componente `TravelThermographsForm` Commit: https://gitea.verdnatura.es/verdnatura/salix-front/commit/c67f4cf858897cd2684946f44bd02c6eae808c68
Review

Se ve mal. Esto creo que ya nos pasó en otra PR antigua, creo que PR-3

Se ve mal. Esto creo que ya nos pasó en otra PR antigua, creo que PR-3
Review

Falta traducir el título del modal

Falta traducir el título del modal
Review

Listo! Ambos comentarios corregidos.

Además de esto me atreví a implementar la posibilidad de agregar un Tooltip al ícono de acción del input VnSelectDialog si se desea

Commit: 1b31657338

Listo! Ambos comentarios corregidos. Además de esto me atreví a implementar la posibilidad de agregar un `Tooltip` al ícono de acción del input `VnSelectDialog` si se desea Commit: https://gitea.verdnatura.es/verdnatura/salix-front/commit/1b316573385f08d51a2b8279ace91d71732fb6d6
:label="t('travel.thermographs.warehouse')"
v-model="thermographForm.warehouseId"
:options="warehousesOptions"
option-value="id"
option-label="name"
/>
</div>
</VnRow>
<VnRow class="row q-gutter-md q-mb-md">
<div class="col">
<QFile
:label="t('travel.thermographs.file')"
multiple
:accept="allowedFileTypes"
v-model="thermographForm.files"
>
<template #append>
<QIcon name="vn:attach" class="cursor-pointer">
<QTooltip>{{ t('Select files') }}</QTooltip>
</QIcon>
</template>
</QFile>
</div>
</VnRow>
</QCard>
</QForm>
</QPage>
</template>
<i18n>
es:
Select files: Selecciona ficheros
Thermograph created: Termógrafo creado
</i18n>

View File

@ -88,8 +88,35 @@ export default {
title: 'thermographs',
icon: 'vn:thermometer',
},
component: () =>
import('src/pages/Travel/Card/TravelTermographs.vue'),
redirect: {
name: 'TravelThermographsIndex',
},
children: [
{
name: 'TravelThermographsIndex',
path: 'index',
component: () =>
import('src/pages/Travel/Card/TravelThermographs.vue'),
},
{
name: 'TravelThermographsCreate',
path: 'create',
props: { viewAction: 'create' },
component: () =>
import(
'src/pages/Travel/Card/TravelThermographsForm.vue'
),
},
{
name: 'TravelThermographsEdit',
path: 'edit',
props: { viewAction: 'edit' },
component: () =>
import(
'src/pages/Travel/Card/TravelThermographsForm.vue'
),
},
],
},
],
},