From a261de39b44de606a7e6ca8a90ca51941a69e540 Mon Sep 17 00:00:00 2001 From: provira Date: Wed, 12 Feb 2025 12:26:43 +0100 Subject: [PATCH 01/35] feat: refs #8443 created vehicle events --- .../Card/VehicleEventInclusionForm.vue | 169 ++++++++++++++ .../Route/Vehicle/Card/VehicleEvents.vue | 78 +++++++ .../Route/Vehicle/Card/VehicleEventsPanel.vue | 183 +++++++++++++++ src/pages/Route/Vehicle/VehicleCalendar.vue | 145 ++++++++++++ .../Route/Vehicle/VehicleCalendarGrid.vue | 210 ++++++++++++++++++ src/router/modules/route.js | 11 +- 6 files changed, 795 insertions(+), 1 deletion(-) create mode 100644 src/pages/Route/Vehicle/Card/VehicleEventInclusionForm.vue create mode 100644 src/pages/Route/Vehicle/Card/VehicleEvents.vue create mode 100644 src/pages/Route/Vehicle/Card/VehicleEventsPanel.vue create mode 100644 src/pages/Route/Vehicle/VehicleCalendar.vue create mode 100644 src/pages/Route/Vehicle/VehicleCalendarGrid.vue diff --git a/src/pages/Route/Vehicle/Card/VehicleEventInclusionForm.vue b/src/pages/Route/Vehicle/Card/VehicleEventInclusionForm.vue new file mode 100644 index 000000000..360dc7830 --- /dev/null +++ b/src/pages/Route/Vehicle/Card/VehicleEventInclusionForm.vue @@ -0,0 +1,169 @@ + + + + + + es: + Started: Inicio + Finished: Fin + Add vehicle event: Agregar evento + Edit vehicle event: Editar evento + \ No newline at end of file diff --git a/src/pages/Route/Vehicle/Card/VehicleEvents.vue b/src/pages/Route/Vehicle/Card/VehicleEvents.vue new file mode 100644 index 000000000..552bcd2e3 --- /dev/null +++ b/src/pages/Route/Vehicle/Card/VehicleEvents.vue @@ -0,0 +1,78 @@ + + + diff --git a/src/pages/Route/Vehicle/Card/VehicleEventsPanel.vue b/src/pages/Route/Vehicle/Card/VehicleEventsPanel.vue new file mode 100644 index 000000000..9ce2f44b4 --- /dev/null +++ b/src/pages/Route/Vehicle/Card/VehicleEventsPanel.vue @@ -0,0 +1,183 @@ + + + + + diff --git a/src/pages/Route/Vehicle/VehicleCalendar.vue b/src/pages/Route/Vehicle/VehicleCalendar.vue new file mode 100644 index 000000000..a7d51daa1 --- /dev/null +++ b/src/pages/Route/Vehicle/VehicleCalendar.vue @@ -0,0 +1,145 @@ + + + + + diff --git a/src/pages/Route/Vehicle/VehicleCalendarGrid.vue b/src/pages/Route/Vehicle/VehicleCalendarGrid.vue new file mode 100644 index 000000000..0fc440643 --- /dev/null +++ b/src/pages/Route/Vehicle/VehicleCalendarGrid.vue @@ -0,0 +1,210 @@ + + + + + \ No newline at end of file diff --git a/src/router/modules/route.js b/src/router/modules/route.js index 835324d20..c14e5f8d0 100644 --- a/src/router/modules/route.js +++ b/src/router/modules/route.js @@ -166,7 +166,7 @@ const vehicleCard = { component: () => import('src/pages/Route/Vehicle/Card/VehicleCard.vue'), redirect: { name: 'VehicleSummary' }, meta: { - menu: ['VehicleBasicData'], + menu: ['VehicleBasicData', 'VehicleEvents'], }, children: [ { @@ -187,6 +187,15 @@ const vehicleCard = { }, component: () => import('src/pages/Route/Vehicle/Card/VehicleBasicData.vue'), }, + { + name: 'VehicleEvents', + path: 'events', + meta: { + title: 'calendar', + icon: 'vn:calendar', + }, + component: () => import('src/pages/Route/Vehicle/Card/VehicleEvents.vue'), + }, ], }; From 398f76c6e7525eaeffcc453391a74e8c893f6b64 Mon Sep 17 00:00:00 2001 From: provira Date: Thu, 13 Feb 2025 07:49:12 +0100 Subject: [PATCH 02/35] feat: refs #8443 added select in form for vehicle state --- .../Card/VehicleEventInclusionForm.vue | 42 ++++++------------- .../Route/Vehicle/Card/VehicleEventsPanel.vue | 22 ---------- 2 files changed, 12 insertions(+), 52 deletions(-) diff --git a/src/pages/Route/Vehicle/Card/VehicleEventInclusionForm.vue b/src/pages/Route/Vehicle/Card/VehicleEventInclusionForm.vue index 360dc7830..800ef1b2b 100644 --- a/src/pages/Route/Vehicle/Card/VehicleEventInclusionForm.vue +++ b/src/pages/Route/Vehicle/Card/VehicleEventInclusionForm.vue @@ -11,6 +11,7 @@ import VnInput from 'src/components/common/VnInput.vue'; import { useArrayData } from 'src/composables/useArrayData'; import { useVnConfirm } from 'composables/useVnConfirm'; import axios from 'axios'; +import VnSelect from 'src/components/common/VnSelect.vue'; const props = defineProps({ event: { @@ -47,12 +48,13 @@ const arrayData = useArrayData('VehicleEvents'); const createVehicleEvent = async () => { vehicleFormData.value.vehicleFk = route.params.id; + vehicleFormData.value.userFk = 3; if (isNew.value) { await axios.post(`Vehicles/${route.params.id}/event`, vehicleFormData.value); } else { await axios.put( `Vehicles/${route.params.id}/event/${props.event?.id}`, - vehicleFormData.value + vehicleFormData.value, ); } @@ -90,42 +92,22 @@ onMounted(() => { :default-submit-button="false" > @@ -147,7 +129,7 @@ onMounted(() => { openConfirmationModal( t('vehicleForm.deleteTitle'), t('vehicleForm.deleteSubtitle'), - () => deleteVehicleEvent() + () => deleteVehicleEvent(), ) " /> @@ -166,4 +148,4 @@ onMounted(() => { Finished: Fin Add vehicle event: Agregar evento Edit vehicle event: Editar evento - \ No newline at end of file + diff --git a/src/pages/Route/Vehicle/Card/VehicleEventsPanel.vue b/src/pages/Route/Vehicle/Card/VehicleEventsPanel.vue index 9ce2f44b4..3ff849c16 100644 --- a/src/pages/Route/Vehicle/Card/VehicleEventsPanel.vue +++ b/src/pages/Route/Vehicle/Card/VehicleEventsPanel.vue @@ -38,24 +38,10 @@ const fetchVehicleState = async () => { vehicleStates.value = vehicles.data; }; -const fetchUser = async (userId) => { - console.log(userId); - if (!userId || users.value[userId]) return; - - const usersData = await axios.get(`Accounts/2/user`); - users.value[userId] = usersData.data; - console.log(users.value[userId]); -}; - const getVehicleStateName = (id) => { return vehicleStates.value[id - 1] ?? dashIfEmpty(id - 1); }; -const getUserName = (id) => { - console.log(users.value?.nickname); - return users.value?.nickname ?? dashIfEmpty(id - 1); -}; - const params = computed(() => ({ vehicleFk: route.params.id, started: props.firstDay, @@ -101,10 +87,6 @@ const openInclusionForm = (event) => { onMounted(async () => { weekdayStore.initStore(); await fetchVehicleState(); - - for (let event of props.events) { - await fetchUser(event.userFk); - } }); @@ -135,10 +117,6 @@ onMounted(async () => { getVehicleStateName(event.vehicleStateFk).state }} - {{ t('globals.user') }}: - {{ getUserName(event.userFk) }} - Date: Mon, 3 Mar 2025 13:08:19 +0100 Subject: [PATCH 03/35] feat: refs #8443 added notifications and translations --- .../Card/VehicleEventInclusionForm.vue | 21 ++++++++----- .../Route/Vehicle/Card/VehicleEventsPanel.vue | 6 +++- .../Route/Vehicle/VehicleCalendarGrid.vue | 4 +-- src/pages/Route/Vehicle/locale/en.yml | 2 ++ src/pages/Route/Vehicle/locale/es.yml | 2 ++ .../route/vehicle/vehicleEvents.spec.js | 30 +++++++++++++++++++ 6 files changed, 55 insertions(+), 10 deletions(-) create mode 100644 test/cypress/integration/route/vehicle/vehicleEvents.spec.js diff --git a/src/pages/Route/Vehicle/Card/VehicleEventInclusionForm.vue b/src/pages/Route/Vehicle/Card/VehicleEventInclusionForm.vue index 800ef1b2b..0a21c05a6 100644 --- a/src/pages/Route/Vehicle/Card/VehicleEventInclusionForm.vue +++ b/src/pages/Route/Vehicle/Card/VehicleEventInclusionForm.vue @@ -10,8 +10,10 @@ import VnInput from 'src/components/common/VnInput.vue'; import { useArrayData } from 'src/composables/useArrayData'; import { useVnConfirm } from 'composables/useVnConfirm'; +import { useState } from 'src/composables/useState'; import axios from 'axios'; import VnSelect from 'src/components/common/VnSelect.vue'; +import useNotify from 'src/composables/useNotify.js'; const props = defineProps({ event: { @@ -32,7 +34,10 @@ const emit = defineEmits(['onSubmit', 'closeForm']); const route = useRoute(); const { t } = useI18n(); +const { notify } = useNotify(); const { openConfirmationModal } = useVnConfirm(); +const state = useState(); +const user = state.getUser(); const isNew = computed(() => props.isNewMode); const vehicleFormData = ref({ @@ -48,12 +53,13 @@ const arrayData = useArrayData('VehicleEvents'); const createVehicleEvent = async () => { vehicleFormData.value.vehicleFk = route.params.id; - vehicleFormData.value.userFk = 3; + vehicleFormData.value.userFk = user.value.id; if (isNew.value) { - await axios.post(`Vehicles/${route.params.id}/event`, vehicleFormData.value); + await axios.post(`VehicleEvents`, vehicleFormData.value); + notify(t('globals.dataSaved'), 'positive'); } else { - await axios.put( - `Vehicles/${route.params.id}/event/${props.event?.id}`, + await axios.patch( + `VehicleEvents/${props.event?.id}`, vehicleFormData.value, ); } @@ -64,7 +70,8 @@ const createVehicleEvent = async () => { const deleteVehicleEvent = async () => { if (!props.event) return; - await axios.delete(`Vehicles/${route.params.id}/event/${props.event?.id}`); + await axios.delete(`VehicleEvents/${props.event?.id}`); + notify(t('globals.dataSaved'), 'positive'); await refetchEvents(); }; @@ -127,8 +134,8 @@ onMounted(() => { class="q-mr-sm" @click=" openConfirmationModal( - t('vehicleForm.deleteTitle'), - t('vehicleForm.deleteSubtitle'), + t('vehicle.deleteTitle'), + t('vehicle.deleteSubtitle'), () => deleteVehicleEvent(), ) " diff --git a/src/pages/Route/Vehicle/Card/VehicleEventsPanel.vue b/src/pages/Route/Vehicle/Card/VehicleEventsPanel.vue index 3ff849c16..ead43b908 100644 --- a/src/pages/Route/Vehicle/Card/VehicleEventsPanel.vue +++ b/src/pages/Route/Vehicle/Card/VehicleEventsPanel.vue @@ -9,6 +9,7 @@ import { toDateFormat } from 'src/filters/date.js'; import { dashIfEmpty } from 'src/filters'; import { useWeekdayStore } from 'src/stores/useWeekdayStore'; import { useVnConfirm } from 'composables/useVnConfirm'; +import useNotify from 'src/composables/useNotify.js'; const props = defineProps({ firstDay: { @@ -28,6 +29,7 @@ const props = defineProps({ const emit = defineEmits(['openVehicleForm']); const { t } = useI18n(); const route = useRoute(); +const { notify } = useNotify(); const weekdayStore = useWeekdayStore(); const { openConfirmationModal } = useVnConfirm(); const vehicleStates = ref({}); @@ -72,7 +74,8 @@ watch( const deleteEvent = async (id) => { if (!id) return; - await axios.delete(`Vehicles/${route.params.id}/event/${id}`); + await axios.delete(`VehicleEvents/${id}`); + notify(t('dataSaved'), 'positive'); await fetchData(); }; @@ -121,6 +124,7 @@ onMounted(async () => { { const onDateSelected = (data) => emit('onDateSelected', data); onMounted(async () => { - let initialDate = new Date(); + let initialDate = Date.vnNew(); initialDate.setDate(1); initialDate.setHours(0, 0, 0, 0); date.value = initialDate; diff --git a/src/pages/Route/Vehicle/locale/en.yml b/src/pages/Route/Vehicle/locale/en.yml index c92022f9d..706f77e0c 100644 --- a/src/pages/Route/Vehicle/locale/en.yml +++ b/src/pages/Route/Vehicle/locale/en.yml @@ -15,6 +15,8 @@ vehicle: remove: Vehicle removed search: Search Vehicle searchInfo: Search by id or number plate + deleteTitle: This item will be deleted + deleteSubtitle: Are you sure you want to continue? params: vehicleTypeFk: Type vehicleStateFk: State diff --git a/src/pages/Route/Vehicle/locale/es.yml b/src/pages/Route/Vehicle/locale/es.yml index c878f97ac..23d163eaf 100644 --- a/src/pages/Route/Vehicle/locale/es.yml +++ b/src/pages/Route/Vehicle/locale/es.yml @@ -15,6 +15,8 @@ vehicle: remove: Vehículo eliminado search: Buscar Vehículo searchInfo: Buscar por id o matrícula + deleteTitle: Este elemento será eliminado + deleteSubtitle: ¿Seguro que quieres continuar? params: vehicleTypeFk: Tipo vehicleStateFk: Estado diff --git a/test/cypress/integration/route/vehicle/vehicleEvents.spec.js b/test/cypress/integration/route/vehicle/vehicleEvents.spec.js new file mode 100644 index 000000000..65d5aa8a1 --- /dev/null +++ b/test/cypress/integration/route/vehicle/vehicleEvents.spec.js @@ -0,0 +1,30 @@ +describe('Vehicle', () => { + beforeEach(() => { + cy.viewport(1920, 1080); + cy.login('deliveryAssistant'); + cy.visit(`/#/route/vehicle/3/events`); + }); + + it('should add a new vehicle event', () => { + cy.get('.q-page-sticky > div > .q-btn').click(); + cy.dataCy('Started_inputDate').type('05/02/2001'); + cy.dataCy('Finished_inputDate').type('08/02/2001'); + cy.get(':nth-child(5) > [label="Description"] > .q-field > .q-field__inner > .q-field__control > .q-field__control-container > [data-cy="Description_input"]').type('Test'); + cy.selectOption(':nth-child(5) > .q-select > .q-field__inner > .q-field__control > .q-field__control-container', 3); + cy.get('.q-mt-lg > .q-btn--standard').click(); + }); + + it('should edit a vehicle event', () => { + cy.get('[aria-label="Tuesday, February 6, 2001"] .q-btn__content').click(); + cy.dataCy('Started_inputDate').clear().type('03/02/2001'); + cy.dataCy('Finished_inputDate').clear().type('15/03/2001'); + cy.get(':nth-child(5) > [label="Description"] > .q-field > .q-field__inner > .q-field__control > .q-field__control-container > [data-cy="Description_input"]').clear().type('Test2'); + cy.selectOption(':nth-child(5) > .q-select > .q-field__inner > .q-field__control > .q-field__control-container', 5); + cy.get('.q-mt-lg > .q-btn--standard').click(); + }); + + it('should delete a vehicle event', () => { + cy.dataCy('delete_event').eq(0).click(); + cy.dataCy('VnConfirm_confirm').click(); + }); +}); From 75a99caf510004de410a53926fde9df8046620c7 Mon Sep 17 00:00:00 2001 From: provira Date: Fri, 21 Mar 2025 12:32:01 +0100 Subject: [PATCH 04/35] feat: refs #8443 changed VehicleEvents to use native loopback for events fetch --- src/composables/getWeekdays.js | 10 +++ src/css/app.scss | 17 ++++ .../Card/VehicleEventInclusionForm.vue | 49 ++++++++--- .../Route/Vehicle/Card/VehicleEvents.vue | 14 +++- .../Route/Vehicle/Card/VehicleEventsPanel.vue | 51 ++++++++--- src/pages/Route/Vehicle/VehicleCalendar.vue | 4 +- .../Route/Vehicle/VehicleCalendarGrid.vue | 84 ++++++++----------- src/pages/Worker/Card/WorkerCalendarItem.vue | 4 +- .../Worker/Card/WorkerTimeControlCalendar.vue | 5 +- src/pages/Zone/ZoneCalendar.vue | 4 +- src/pages/Zone/ZoneCalendarGrid.vue | 19 ----- .../route/vehicle/vehicleEvents.spec.js | 10 +-- 12 files changed, 170 insertions(+), 101 deletions(-) create mode 100644 src/composables/getWeekdays.js diff --git a/src/composables/getWeekdays.js b/src/composables/getWeekdays.js new file mode 100644 index 000000000..d619103da --- /dev/null +++ b/src/composables/getWeekdays.js @@ -0,0 +1,10 @@ +import { ref } from 'vue'; +import moment from 'moment'; + +export default function useWeekdaysOrder() { + + const firstDay = moment().weekday(1).day(); + const weekdays = [...Array(7).keys()].map(i => (i + firstDay) % 7); + + return ref(weekdays); +} diff --git a/src/css/app.scss b/src/css/app.scss index 994ae7ff1..7eae00f89 100644 --- a/src/css/app.scss +++ b/src/css/app.scss @@ -339,3 +339,20 @@ input::-webkit-inner-spin-button { .containerShrinked { width: 80%; } + +.calendars-header { + height: 45px; + display: flex; + justify-content: space-between; + align-items: center; + background-color: $primary; + font-weight: bold; + font-size: 16px; +} + +.calendars-container { + max-width: 800px; + display: flex; + flex-wrap: wrap; + justify-content: space-evenly; +} \ No newline at end of file diff --git a/src/pages/Route/Vehicle/Card/VehicleEventInclusionForm.vue b/src/pages/Route/Vehicle/Card/VehicleEventInclusionForm.vue index 0a21c05a6..23be4340d 100644 --- a/src/pages/Route/Vehicle/Card/VehicleEventInclusionForm.vue +++ b/src/pages/Route/Vehicle/Card/VehicleEventInclusionForm.vue @@ -28,9 +28,17 @@ const props = defineProps({ type: Boolean, default: true, }, + firstDay: { + type: Date, + default: null, + }, + lastDay: { + type: Date, + default: null, + }, }); -const emit = defineEmits(['onSubmit', 'closeForm']); +const emit = defineEmits(['onSubmit', 'closeForm', 'refresh-events']); const route = useRoute(); const { t } = useI18n(); @@ -54,6 +62,7 @@ const arrayData = useArrayData('VehicleEvents'); const createVehicleEvent = async () => { vehicleFormData.value.vehicleFk = route.params.id; vehicleFormData.value.userFk = user.value.id; + if (isNew.value) { await axios.post(`VehicleEvents`, vehicleFormData.value); notify(t('globals.dataSaved'), 'positive'); @@ -62,10 +71,11 @@ const createVehicleEvent = async () => { `VehicleEvents/${props.event?.id}`, vehicleFormData.value, ); + notify(t('globals.dataSaved'), 'positive'); } - + await refetchEvents(); - emit('onSubmit'); + emit('closeForm'); }; const deleteVehicleEvent = async () => { @@ -73,15 +83,35 @@ const deleteVehicleEvent = async () => { await axios.delete(`VehicleEvents/${props.event?.id}`); notify(t('globals.dataSaved'), 'positive'); await refetchEvents(); -}; - -const closeForm = () => { emit('closeForm'); }; const refetchEvents = async () => { - await arrayData.refresh({ append: false }); - closeForm(); + await arrayData.refresh({ + append: false, + params: { + filter: { + where: { + vehicleFk: route.params.id, + and: [ + { + or: [ + { started: { lte: props.lastDay?.toISOString() } }, + { started: null } + ] + }, + { + or: [ + { finished: { gte: props.firstDay?.toISOString() } }, + { finished: null } + ] + } + ] + } + } + } + }); + emit('refresh-events'); }; onMounted(() => { @@ -94,7 +124,6 @@ onMounted(() => { diff --git a/src/pages/Route/Vehicle/Card/VehicleEvents.vue b/src/pages/Route/Vehicle/Card/VehicleEvents.vue index 552bcd2e3..ba6ccad8b 100644 --- a/src/pages/Route/Vehicle/Card/VehicleEvents.vue +++ b/src/pages/Route/Vehicle/Card/VehicleEvents.vue @@ -1,5 +1,5 @@ - - diff --git a/test/cypress/integration/route/vehicle/vehicleEvents.spec.js b/test/cypress/integration/route/vehicle/vehicleEvents.spec.js index 65d5aa8a1..f6c14ba33 100644 --- a/test/cypress/integration/route/vehicle/vehicleEvents.spec.js +++ b/test/cypress/integration/route/vehicle/vehicleEvents.spec.js @@ -5,25 +5,21 @@ describe('Vehicle', () => { cy.visit(`/#/route/vehicle/3/events`); }); - it('should add a new vehicle event', () => { + it('should add, edit and delete a vehicle event', () => { cy.get('.q-page-sticky > div > .q-btn').click(); - cy.dataCy('Started_inputDate').type('05/02/2001'); + cy.dataCy('Started_inputDate').type('01/01/2001'); cy.dataCy('Finished_inputDate').type('08/02/2001'); cy.get(':nth-child(5) > [label="Description"] > .q-field > .q-field__inner > .q-field__control > .q-field__control-container > [data-cy="Description_input"]').type('Test'); cy.selectOption(':nth-child(5) > .q-select > .q-field__inner > .q-field__control > .q-field__control-container', 3); cy.get('.q-mt-lg > .q-btn--standard').click(); - }); - it('should edit a vehicle event', () => { - cy.get('[aria-label="Tuesday, February 6, 2001"] .q-btn__content').click(); + cy.get('.q-current-day > .q-calendar-month__day--content > .q-btn').click(); cy.dataCy('Started_inputDate').clear().type('03/02/2001'); cy.dataCy('Finished_inputDate').clear().type('15/03/2001'); cy.get(':nth-child(5) > [label="Description"] > .q-field > .q-field__inner > .q-field__control > .q-field__control-container > [data-cy="Description_input"]').clear().type('Test2'); cy.selectOption(':nth-child(5) > .q-select > .q-field__inner > .q-field__control > .q-field__control-container', 5); cy.get('.q-mt-lg > .q-btn--standard').click(); - }); - it('should delete a vehicle event', () => { cy.dataCy('delete_event').eq(0).click(); cy.dataCy('VnConfirm_confirm').click(); }); From 47779437d3033dcc186e4323805113bdc35bcbf1 Mon Sep 17 00:00:00 2001 From: provira Date: Wed, 26 Mar 2025 08:32:29 +0100 Subject: [PATCH 05/35] feat: refs #8443 added EntityCalendar and EntityCalendarGrid --- src/components/EntityCalendar.vue | 147 +++++++++++++++++ src/components/EntityCalendarGrid.vue | 128 +++++++++++++++ .../Card/VehicleEventInclusionForm.vue | 22 ++- .../Route/Vehicle/Card/VehicleEvents.vue | 6 +- src/pages/Route/Vehicle/VehicleCalendar.vue | 147 ++--------------- .../Route/Vehicle/VehicleCalendarGrid.vue | 149 +++--------------- .../route/vehicle/vehicleEvents.spec.js | 4 +- 7 files changed, 324 insertions(+), 279 deletions(-) create mode 100644 src/components/EntityCalendar.vue create mode 100644 src/components/EntityCalendarGrid.vue diff --git a/src/components/EntityCalendar.vue b/src/components/EntityCalendar.vue new file mode 100644 index 000000000..ec70acf15 --- /dev/null +++ b/src/components/EntityCalendar.vue @@ -0,0 +1,147 @@ + + + + + \ No newline at end of file diff --git a/src/components/EntityCalendarGrid.vue b/src/components/EntityCalendarGrid.vue new file mode 100644 index 000000000..ed2521091 --- /dev/null +++ b/src/components/EntityCalendarGrid.vue @@ -0,0 +1,128 @@ + + + \ No newline at end of file diff --git a/src/pages/Route/Vehicle/Card/VehicleEventInclusionForm.vue b/src/pages/Route/Vehicle/Card/VehicleEventInclusionForm.vue index 23be4340d..54ed50c80 100644 --- a/src/pages/Route/Vehicle/Card/VehicleEventInclusionForm.vue +++ b/src/pages/Route/Vehicle/Card/VehicleEventInclusionForm.vue @@ -59,31 +59,32 @@ const vehicleFormData = ref({ const arrayData = useArrayData('VehicleEvents'); +onMounted(() => { + if (props.event) { + vehicleFormData.value = { ...props.event }; + } +}); + const createVehicleEvent = async () => { vehicleFormData.value.vehicleFk = route.params.id; vehicleFormData.value.userFk = user.value.id; if (isNew.value) { await axios.post(`VehicleEvents`, vehicleFormData.value); - notify(t('globals.dataSaved'), 'positive'); } else { await axios.patch( `VehicleEvents/${props.event?.id}`, vehicleFormData.value, ); - notify(t('globals.dataSaved'), 'positive'); } - await refetchEvents(); - emit('closeForm'); + }; const deleteVehicleEvent = async () => { if (!props.event) return; await axios.delete(`VehicleEvents/${props.event?.id}`); - notify(t('globals.dataSaved'), 'positive'); await refetchEvents(); - emit('closeForm'); }; const refetchEvents = async () => { @@ -112,13 +113,11 @@ const refetchEvents = async () => { } }); emit('refresh-events'); + notify(t('globals.dataSaved'), 'positive'); + emit('closeForm'); }; -onMounted(() => { - if (props.event) { - vehicleFormData.value = { ...props.event }; - } -}); + \ No newline at end of file diff --git a/src/pages/Route/Vehicle/VehicleCalendarGrid.vue b/src/pages/Route/Vehicle/VehicleCalendarGrid.vue index 7fd30dd47..f88aa0134 100644 --- a/src/pages/Route/Vehicle/VehicleCalendarGrid.vue +++ b/src/pages/Route/Vehicle/VehicleCalendarGrid.vue @@ -1,10 +1,7 @@ + + \ No newline at end of file diff --git a/test/cypress/integration/route/vehicle/vehicleEvents.spec.js b/test/cypress/integration/route/vehicle/vehicleEvents.spec.js index f6c14ba33..c94c6c214 100644 --- a/test/cypress/integration/route/vehicle/vehicleEvents.spec.js +++ b/test/cypress/integration/route/vehicle/vehicleEvents.spec.js @@ -9,14 +9,14 @@ describe('Vehicle', () => { cy.get('.q-page-sticky > div > .q-btn').click(); cy.dataCy('Started_inputDate').type('01/01/2001'); cy.dataCy('Finished_inputDate').type('08/02/2001'); - cy.get(':nth-child(5) > [label="Description"] > .q-field > .q-field__inner > .q-field__control > .q-field__control-container > [data-cy="Description_input"]').type('Test'); + cy.get(':nth-child(5)').find('[data-cy="Description_input"]').clear().type('Test'); cy.selectOption(':nth-child(5) > .q-select > .q-field__inner > .q-field__control > .q-field__control-container', 3); cy.get('.q-mt-lg > .q-btn--standard').click(); cy.get('.q-current-day > .q-calendar-month__day--content > .q-btn').click(); cy.dataCy('Started_inputDate').clear().type('03/02/2001'); cy.dataCy('Finished_inputDate').clear().type('15/03/2001'); - cy.get(':nth-child(5) > [label="Description"] > .q-field > .q-field__inner > .q-field__control > .q-field__control-container > [data-cy="Description_input"]').clear().type('Test2'); + cy.get(':nth-child(5)').find('[data-cy="Description_input"]').clear().type('Test2'); cy.selectOption(':nth-child(5) > .q-select > .q-field__inner > .q-field__control > .q-field__control-container', 5); cy.get('.q-mt-lg > .q-btn--standard').click(); From ee1fcf75fbf630bcade551f739215d8931071955 Mon Sep 17 00:00:00 2001 From: jtubau Date: Wed, 26 Mar 2025 12:57:02 +0100 Subject: [PATCH 06/35] refactor: refs #7638 enhance CmrList component with additional fields and improved formatting --- src/pages/Route/Cmr/CmrList.vue | 265 +++++++++++++++++++++++++++----- 1 file changed, 230 insertions(+), 35 deletions(-) diff --git a/src/pages/Route/Cmr/CmrList.vue b/src/pages/Route/Cmr/CmrList.vue index 170f73bc0..3c331ec46 100644 --- a/src/pages/Route/Cmr/CmrList.vue +++ b/src/pages/Route/Cmr/CmrList.vue @@ -6,13 +6,18 @@ import { useRoute } from 'vue-router'; import { useSession } from 'src/composables/useSession'; import { toDateHourMin } from 'filters/index'; import { useStateStore } from 'src/stores/useStateStore'; +import { dashIfEmpty } from 'src/filters'; +import AgencyDescriptorProxy from '../Agency/Card/AgencyDescriptorProxy.vue'; +import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue'; +import RouteDescriptorProxy from '../Card/RouteDescriptorProxy.vue'; +import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorProxy.vue'; import TicketDescriptorProxy from 'pages/Ticket/Card/TicketDescriptorProxy.vue'; -import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue'; import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; import VnTable from 'components/VnTable/VnTable.vue'; import VnSearchbar from 'src/components/ui/VnSearchbar.vue'; +import VnInput from 'src/components/common/VnInput.vue'; const route = useRoute(); const { t } = useI18n(); @@ -30,39 +35,117 @@ const userParams = { const columns = computed(() => [ { - align: 'left', + align: 'right', name: 'cmrFk', - label: t('route.cmr.params.cmrFk'), + label: t('cmr.params.cmrFk'), chip: { condition: () => true, }, isId: true, }, { - align: 'center', - name: 'hasCmrDms', - label: t('route.cmr.params.hasCmrDms'), - component: 'checkbox', - cardVisible: true, - }, - { - align: 'left', - label: t('route.cmr.params.ticketFk'), + align: 'right', + label: t('cmr.params.ticketFk'), name: 'ticketFk', }, { - align: 'left', - label: t('route.cmr.params.routeFk'), + align: 'right', + label: t('cmr.params.routeFk'), name: 'routeFk', }, { - align: 'left', - label: t('route.cmr.params.clientFk'), + label: t('cmr.params.client'), name: 'clientFk', + component: 'select', + attrs: { + url: 'Clients', + fields: ['id', 'name'], + }, + columnFilter: { + name: 'clientFk', + attrs: { + url: 'Clients', + fields: ['id', 'name'], + }, + }, }, { - align: 'right', - label: t('route.cmr.params.countryFk'), + label: t('cmr.params.agency'), + name: 'agencyModeFk', + component: 'select', + attrs: { + url: 'Agencies', + fields: ['id', 'name'], + }, + columnFilter: { + name: 'agencyModeFk', + attrs: { + url: 'Agencies', + fields: ['id', 'name'], + }, + }, + format: ({ agencyName }) => agencyName, + }, + { + label: t('cmr.params.supplier'), + name: 'supplierFk', + component: 'select', + attrs: { + url: 'suppliers', + fields: ['id', 'name'], + }, + columnFilter: { + name: 'supplierFk', + attrs: { + url: 'suppliers', + fields: ['id', 'name'], + }, + }, + }, + { + label: t('cmr.params.sender'), + name: 'addressFromFk', + component: 'select', + attrs: { + url: 'Addresses', + fields: ['id', 'nickname'], + optionValue: 'id', + optionLabel: 'nickname', + }, + columnFilter: { + name: 'addressFromFk', + attrs: { + url: 'Addresses', + fields: ['id', 'nickname'], + optionValue: 'id', + optionLabel: 'nickname', + }, + }, + format: ({ origin }) => origin, + }, + { + label: t('cmr.params.destination'), + name: 'addressToFk', + component: 'select', + attrs: { + url: 'addresses', + fields: ['id', 'nickname'], + optionValue: 'id', + optionLabel: 'nickname', + }, + columnFilter: { + name: 'addressToFk', + attrs: { + url: 'addresses', + fields: ['id', 'nickname'], + optionValue: 'id', + optionLabel: 'nickname', + }, + }, + format: ({ destination }) => destination, + }, + { + label: t('cmr.params.country'), name: 'countryFk', component: 'select', attrs: { @@ -79,16 +162,61 @@ const columns = computed(() => [ format: ({ countryName }) => countryName, }, { - align: 'right', - label: t('route.cmr.params.shipped'), - name: 'shipped', - cardVisible: true, + label: t('cmr.params.created'), + name: 'created', component: 'date', - format: ({ shipped }) => toDateHourMin(shipped), + format: ({ created }) => dashIfEmpty(toDateHourMin(created)), }, { - align: 'right', - label: t('route.cmr.params.warehouseFk'), + label: t('cmr.params.shipped'), + name: 'shipped', + component: 'date', + format: ({ shipped }) => dashIfEmpty(toDateHourMin(shipped)), + }, + { + label: t('cmr.params.etd'), + name: 'ead', + component: 'date', + format: ({ ead }) => dashIfEmpty(toDateHourMin(ead)), + toolTip: t('cmr.params.etdTooltip'), + }, + { + label: t('globals.landed'), + name: 'landed', + component: 'date', + format: ({ landed }) => dashIfEmpty(toDateHourMin(landed)), + }, + { + align: 'left', + label: t('cmr.params.packageList'), + name: 'packagesList', + columnFilter: false, + }, + { + align: 'left', + label: t('cmr.params.observation'), + name: 'observation', + columnFilter: false, + }, + { + align: 'left', + label: t('cmr.params.senderInstructions'), + name: 'senderInstruccions', + columnFilter: false, + }, + { + align: 'left', + label: t('cmr.params.paymentInstructions'), + name: 'paymentInstruccions', + columnFilter: false, + }, + { + align: 'left', + label: t('cmr.params.vehiclePlate'), + name: 'truckPlate', + }, + { + label: t('cmr.params.warehouse'), name: 'warehouseFk', component: 'select', attrs: { @@ -96,7 +224,6 @@ const columns = computed(() => [ fields: ['id', 'name'], }, columnFilter: { - inWhere: true, name: 'warehouseFk', attrs: { url: 'warehouses', @@ -105,12 +232,23 @@ const columns = computed(() => [ }, format: ({ warehouseName }) => warehouseName, }, + { + align: 'left', + name: 'specialAgreements', + label: t('cmr.params.specialAgreements'), + columnFilter: false, + }, + { + name: 'hasCmrDms', + label: t('cmr.params.hasCmrDms'), + component: 'checkbox', + }, { align: 'center', name: 'tableActions', actions: [ { - title: t('route.cmr.params.viewCmr'), + title: t('cmr.params.viewCmr'), icon: 'visibility', isPrimary: true, action: (row) => window.open(getCmrUrl(row?.cmrFk), '_blank'), @@ -151,11 +289,7 @@ function downloadPdfs() { } + + + + + + + + From f2e4fff69aaaddffcebe788e7c7a1b93b0f05b7a Mon Sep 17 00:00:00 2001 From: jtubau Date: Wed, 26 Mar 2025 12:57:14 +0100 Subject: [PATCH 07/35] refactor: refs #7638 add localization support for Cmr and Route components in English and Spanish --- src/pages/Route/Cmr/locale/en.yml | 31 +++++++++++++++++++++++++++++++ src/pages/Route/Cmr/locale/es.yml | 31 +++++++++++++++++++++++++++++++ src/pages/Route/locale/en.yml | 23 +++++------------------ src/pages/Route/locale/es.yml | 7 ++++++- 4 files changed, 73 insertions(+), 19 deletions(-) create mode 100644 src/pages/Route/Cmr/locale/en.yml create mode 100644 src/pages/Route/Cmr/locale/es.yml diff --git a/src/pages/Route/Cmr/locale/en.yml b/src/pages/Route/Cmr/locale/en.yml new file mode 100644 index 000000000..49b9895f8 --- /dev/null +++ b/src/pages/Route/Cmr/locale/en.yml @@ -0,0 +1,31 @@ +cmr: + search: Search Cmr + searchInfo: You can search Cmr by Id + params: + agency: Agency + client: Client + cmrFk: CMR id + country: Country + created: Created + destination: Destination + downloadCmrs: Download CMRs + etd: ETD + etdTooltip: Estimated Time Delivery + hasCmrDms: Attached in gestdoc + observation: Observation + packageList: Package List + paymentInstructions: Payment instructions + routeFk: Route id + results: results + search: General search + sender: Sender + senderInstructions: Sender instructions + shipped: Shipped + specialAgreements: Special agreements + supplier: Carrier + ticketFk: Ticket id + vehiclePlate: Vehicle plate + viewCmr: View CMR + warehouse: Warehouse + 'true': 'Yes' + 'false': 'No' \ No newline at end of file diff --git a/src/pages/Route/Cmr/locale/es.yml b/src/pages/Route/Cmr/locale/es.yml new file mode 100644 index 000000000..b419a238b --- /dev/null +++ b/src/pages/Route/Cmr/locale/es.yml @@ -0,0 +1,31 @@ +cmr: + search: Buscar Cmr + searchInfo: Puedes buscar cmr por id + params: + agency: Agencia + client: Cliente + cmrFk: Id cmr + country: País + created: Creado + destination: Destinatario + downloadCmrs: Descargar CMRs + etd: ETD + etdTooltip: Fecha estimada de entrega + hasCmrDms: Adjunto en gestdoc + observation: Observaciones + packageList: Listado embalajes + paymentInstructions: Instrucciones de pago + routeFk: Id ruta + results: Resultados + search: Busqueda general + sender: Remitente + senderInstructions: Instrucciones de envío + shipped: F. envío + specialAgreements: Acuerdos especiales + supplier: Transportista + ticketFk: Id ticket + vehiclePlate: Matrícula + viewCmr: Ver CMR + warehouse: Almacén + 'true': 'Si' + 'false': 'No' \ No newline at end of file diff --git a/src/pages/Route/locale/en.yml b/src/pages/Route/locale/en.yml index 283b61855..206100b6b 100644 --- a/src/pages/Route/locale/en.yml +++ b/src/pages/Route/locale/en.yml @@ -51,6 +51,11 @@ route: agencyModeName: Agency route isOwn: Own isAnyVolumeallowed: Any volume allowed + created: Created + addressFromFk: Sender + addressToFk: Destination + landed: Landed + ead: EAD Worker: Worker Agency: Agency Vehicle: Vehicle @@ -70,21 +75,3 @@ route: searchInfo: You can search by route reference dated: Dated preview: Preview - cmr: - search: Search Cmr - searchInfo: You can search Cmr by Id - params: - results: results - cmrFk: CMR id - hasCmrDms: Attached in gestdoc - 'true': 'Yes' - 'false': 'No' - ticketFk: Ticketd id - routeFk: Route id - countryFk: Country - clientFk: Client id - warehouseFk: Warehouse - shipped: Preparation date - viewCmr: View CMR - downloadCmrs: Download CMRs - search: General search diff --git a/src/pages/Route/locale/es.yml b/src/pages/Route/locale/es.yml index 2785ded31..fe3ba08be 100644 --- a/src/pages/Route/locale/es.yml +++ b/src/pages/Route/locale/es.yml @@ -47,11 +47,16 @@ route: routeFk: Id ruta clientFk: Id cliente countryFk: Pais - shipped: Fecha preparación + shipped: F. envío agencyModeName: Agencia Ruta agencyAgreement: Agencia Acuerdo isOwn: Propio isAnyVolumeAllowed: Cualquier volumen + created: Creado + addressFromFk: Remitente + addressToFk: Destinatario + landed: F. entrega + ead: ETD Worker: Trabajador Agency: Agencia Vehicle: Vehículo From af612f937936fa975a8ed414158f2ec53f0ccbab Mon Sep 17 00:00:00 2001 From: jtubau Date: Wed, 26 Mar 2025 13:28:18 +0100 Subject: [PATCH 08/35] test: refs #7638 add route, agency, and carrier pop-up redirection tests in CmrList component --- .../integration/route/cmr/cmrList.spec.js | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/test/cypress/integration/route/cmr/cmrList.spec.js b/test/cypress/integration/route/cmr/cmrList.spec.js index d33508e3a..452a696bd 100644 --- a/test/cypress/integration/route/cmr/cmrList.spec.js +++ b/test/cypress/integration/route/cmr/cmrList.spec.js @@ -5,6 +5,9 @@ describe('Cmr list', () => { const selectors = { ticket: getLinkSelector('ticketFk'), client: getLinkSelector('clientFk'), + route: getLinkSelector('routeFk'), + agency: getLinkSelector('agencyModeFk'), + carrier: getLinkSelector('supplierFk'), lastRowSelectCheckBox: '.q-virtual-scroll__content > tr:last-child > :nth-child(1) > .q-checkbox', downloadBtn: '#subToolbar > .q-btn', @@ -21,6 +24,10 @@ describe('Cmr list', () => { const data = { ticket: '1', client: 'Bruce Wayne', + route: 'first route', + routeId: '1', + agency: 'inhouse pickup', + carrier: 'PLANTS SL', }; beforeEach(() => { @@ -69,6 +76,26 @@ describe('Cmr list', () => { }); }); + describe('Route pop-ups', () => { + it('Should redirect to the route summary from the route descriptor pop-up', () => { + cy.get(selectors.route).should('be.visible').click(); + cy.containContent(selectors.descriptorId, data.routeId); + cy.get(selectors.descriptorGoToSummaryBtn).should('be.visible').click(); + cy.url().should('include', '/route/1/summary'); + cy.containContent(selectors.summaryTitle, data.route); + }); + + it('Should redirect to the route summary from summary pop-up from the route descriptor pop-up', () => { + cy.get(selectors.route).should('be.visible').click(); + cy.containContent(selectors.descriptorId, data.routeId); + cy.get(selectors.descriptorOpenSummaryBtn).should('be.visible').click(); + cy.containContent(selectors.summaryTitle, data.route); + cy.get(selectors.summaryGoToSummaryBtn).should('be.visible').click(); + cy.url().should('include', '/route/1/summary'); + cy.containContent(selectors.summaryTitle, data.route); + }); + }); + describe('Client pop-ups', () => { it('Should redirect to the client summary from the client descriptor pop-up', () => { cy.get(selectors.client).should('be.visible').click(); @@ -88,4 +115,44 @@ describe('Cmr list', () => { cy.containContent(selectors.summaryTitle, data.client); }); }); + + describe('Agency pop-ups', () => { + it('Should redirect to the agency summary from the agency descriptor pop-up', () => { + cy.get(selectors.agency).should('be.visible').click(); + cy.containContent(selectors.descriptorTitle, data.agency); + cy.get(selectors.descriptorGoToSummaryBtn).should('be.visible').click(); + cy.url().should('include', '/agency/1/summary'); + cy.containContent(selectors.summaryTitle, data.agency); + }); + + it('Should redirect to the agency summary from summary pop-up from the agency descriptor pop-up', () => { + cy.get(selectors.agency).should('be.visible').click(); + cy.containContent(selectors.descriptorTitle, data.agency); + cy.get(selectors.descriptorOpenSummaryBtn).should('be.visible').click(); + cy.containContent(selectors.summaryTitle, data.agency); + cy.get(selectors.summaryGoToSummaryBtn).should('be.visible').click(); + cy.url().should('include', '/agency/1/summary'); + cy.containContent(selectors.summaryTitle, data.agency); + }); + }); + + describe('Carrier pop-ups', () => { + it('Should redirect to the supplier summary from the supplier descriptor pop-up', () => { + cy.get(selectors.carrier).should('be.visible').click(); + cy.containContent(selectors.descriptorTitle, data.carrier); + cy.get(selectors.descriptorGoToSummaryBtn).should('be.visible').click(); + cy.url().should('include', '/supplier/1/summary'); + cy.containContent(selectors.summaryTitle, data.carrier); + }); + + it('Should redirect to the supplier summary from summary pop-up from the supplier descriptor pop-up', () => { + cy.get(selectors.carrier).should('be.visible').click(); + cy.containContent(selectors.descriptorTitle, data.carrier); + cy.get(selectors.descriptorOpenSummaryBtn).should('be.visible').click(); + cy.containContent(selectors.summaryTitle, data.carrier); + cy.get(selectors.summaryGoToSummaryBtn).should('be.visible').click(); + cy.url().should('include', '/supplier/1/summary'); + cy.containContent(selectors.summaryTitle, data.carrier); + }); + }); }); From ac19803fcd96410bb955d4b12ed9cb3ce1852491 Mon Sep 17 00:00:00 2001 From: jtubau Date: Wed, 26 Mar 2025 13:28:51 +0100 Subject: [PATCH 09/35] refactor: refs #7638 add cmrFk order --- src/pages/Route/Cmr/CmrList.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Route/Cmr/CmrList.vue b/src/pages/Route/Cmr/CmrList.vue index 3c331ec46..98e1bda02 100644 --- a/src/pages/Route/Cmr/CmrList.vue +++ b/src/pages/Route/Cmr/CmrList.vue @@ -308,7 +308,7 @@ function downloadPdfs() { :data-key url="Cmrs/filter" :columns="columns" - :order="['shipped DESC']" + :order="['shipped DESC', 'cmrFk ASC']" :user-params="userParams" default-mode="table" v-model:selected="selectedRows" From 70c87807823995d633f7bfb0a9a9aa902f9be89e Mon Sep 17 00:00:00 2001 From: provira Date: Mon, 31 Mar 2025 09:25:25 +0200 Subject: [PATCH 10/35] fix: refs #8443 code and form view fixes --- src/components/EntityCalendar.vue | 8 ++++++-- src/components/EntityCalendarGrid.vue | 4 +--- .../Vehicle/Card/VehicleEventInclusionForm.vue | 14 ++++++-------- src/pages/Route/Vehicle/VehicleCalendar.vue | 7 ------- src/pages/Route/Vehicle/VehicleCalendarGrid.vue | 2 +- .../route/vehicle/vehicleEvents.spec.js | 4 ++-- 6 files changed, 16 insertions(+), 23 deletions(-) diff --git a/src/components/EntityCalendar.vue b/src/components/EntityCalendar.vue index ec70acf15..27c837054 100644 --- a/src/components/EntityCalendar.vue +++ b/src/components/EntityCalendar.vue @@ -8,6 +8,10 @@ import '@quasar/quasar-ui-qcalendar/src/QCalendarVariables.scss'; import { useWeekdayStore } from 'src/stores/useWeekdayStore'; import useWeekdaysOrder from 'src/composables/getWeekdays'; +const formatDate = (dateToFormat, format = 'YYYY-MM-DD') => { + return date.formatDate(dateToFormat, format); +}; + const props = defineProps({ year: { type: Number, @@ -33,13 +37,13 @@ const { locale } = useI18n(); const weekdayStore = useWeekdayStore(); const weekDays = useWeekdaysOrder(); const calendarRef = ref(null); -const today = ref(date.formatDate(Date.vnNew(), 'YYYY-MM-DD')); +const today = ref(formatDate(Date.vnNew())); const todayTimestamp = computed(() => { const date = Date.vnNew(); date.setHours(0, 0, 0, 0); return date.getTime(); }); -const _monthDate = computed(() => date.formatDate(props.monthDate, 'YYYY-MM-DD')); +const _monthDate = computed(() => formatDate(props.monthDate)); const calendarHeaderTitle = computed(() => { return `${weekdayStore.getLocaleMonths[props.month - 1].locale} ${props.year}`; diff --git a/src/components/EntityCalendarGrid.vue b/src/components/EntityCalendarGrid.vue index ed2521091..09ccaad07 100644 --- a/src/components/EntityCalendarGrid.vue +++ b/src/components/EntityCalendarGrid.vue @@ -1,5 +1,5 @@