diff --git a/src/components/common/__tests__/VnNotes.spec.js b/src/components/common/__tests__/VnNotes.spec.js index 2603bf03c..ea595060a 100644 --- a/src/components/common/__tests__/VnNotes.spec.js +++ b/src/components/common/__tests__/VnNotes.spec.js @@ -1,16 +1,6 @@ -import { - describe, - it, - expect, - vi, - beforeAll, - afterEach, - beforeEach, - afterAll, -} from 'vitest'; +import { describe, it, expect, vi, afterEach, beforeEach, afterAll } from 'vitest'; import { createWrapper, axios } from 'app/test/vitest/helper'; import VnNotes from 'src/components/ui/VnNotes.vue'; -import vnDate from 'src/boot/vnDate'; describe('VnNotes', () => { let vm; @@ -18,6 +8,7 @@ describe('VnNotes', () => { let spyFetch; let postMock; let patchMock; + let deleteMock; let expectedInsertBody; let expectedUpdateBody; const defaultOptions = { @@ -57,6 +48,7 @@ describe('VnNotes', () => { beforeEach(() => { postMock = vi.spyOn(axios, 'post'); patchMock = vi.spyOn(axios, 'patch'); + deleteMock = vi.spyOn(axios, 'delete'); }); afterEach(() => { @@ -153,4 +145,16 @@ describe('VnNotes', () => { ); }); }); + + describe('delete', () => { + it('Should call axios.delete with url and vnPaginateRef.fetch', async () => { + generateWrapper(); + createSpyFetch(); + + await vm.deleteNote({ id: 1 }); + + expect(deleteMock).toHaveBeenCalledWith(`${vm.$props.url}/1`); + expect(spyFetch).toHaveBeenCalled(); + }); + }); }); diff --git a/src/components/ui/VnNotes.vue b/src/components/ui/VnNotes.vue index b7e6ccbec..9cedbccfa 100644 --- a/src/components/ui/VnNotes.vue +++ b/src/components/ui/VnNotes.vue @@ -18,10 +18,10 @@ import VnInput from 'components/common/VnInput.vue'; const emit = defineEmits(['onFetch']); -const $attrs = useAttrs(); - -const isRequired = computed(() => { - return Object.keys($attrs).includes('required'); +const originalAttrs = useAttrs(); +const $attrs = computed(() => { + const { required, deletable, ...rest } = originalAttrs; + return rest; }); const $props = defineProps({ @@ -53,6 +53,11 @@ function handleClick(e) { else insert(); } +async function deleteNote(e) { + await axios.delete(`${$props.url}/${e.id}`); + await vnPaginateRef.value.fetch(); +} + async function insert() { if (!newNote.text || ($props.selectType && !newNote.observationTypeFk)) return; @@ -157,7 +162,7 @@ const handleObservationTypes = (data) => { v-model="newNote.observationTypeFk" option-label="description" style="flex: 0.15" - :required="isRequired" + :required="'required' in originalAttrs" @keyup.enter.stop="insert" /> <VnInput @@ -165,11 +170,10 @@ const handleObservationTypes = (data) => { type="textarea" :label="$props.justInput && newNote.text ? '' : t('Add note here...')" filled - size="lg" autogrow autofocus @keyup.enter.stop="handleClick" - :required="isRequired" + :required="'required' in originalAttrs" clearable > <template #append> @@ -239,6 +243,21 @@ const handleObservationTypes = (data) => { </QBadge> </div> <span v-text="toDateHourMin(note.created)" /> + <div> + <QIcon + v-if="'deletable' in originalAttrs" + name="delete" + size="sm" + class="cursor-pointer" + color="primary" + @click="deleteNote(note)" + data-cy="notesRemoveNoteBtn" + > + <QTooltip> + {{ t('ticketNotes.removeNote') }} + </QTooltip> + </QIcon> + </div> </div> </QCardSection> <QCardSection class="q-pa-xs q-my-none q-py-none"> diff --git a/src/pages/Route/Vehicle/Card/VehicleNotes.vue b/src/pages/Route/Vehicle/Card/VehicleNotes.vue new file mode 100644 index 000000000..0afc3c3ed --- /dev/null +++ b/src/pages/Route/Vehicle/Card/VehicleNotes.vue @@ -0,0 +1,35 @@ +<script setup> +import { computed } from 'vue'; +import { useRoute } from 'vue-router'; +import { useState } from 'src/composables/useState'; +import VnNotes from 'src/components/ui/VnNotes.vue'; + +const route = useRoute(); +const state = useState(); +const user = state.getUser(); +const vehicleId = computed(() => route.params.id); + +const noteFilter = computed(() => { + return { + order: 'created DESC', + where: { vehicleFk: vehicleId.value }, + }; +}); + +const body = { + vehicleFk: vehicleId.value, + workerFk: user.value.id, +}; +</script> + +<template> + <VnNotes + url="vehicleObservations" + :add-note="true" + :filter="noteFilter" + :body="body" + style="overflow-y: auto" + required + deletable + /> +</template> diff --git a/src/router/modules/route.js b/src/router/modules/route.js index 62765a49c..11133c50a 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', 'VehicleNotes'], }, children: [ { @@ -187,6 +187,15 @@ const vehicleCard = { }, component: () => import('src/pages/Route/Vehicle/Card/VehicleBasicData.vue'), }, + { + name: 'VehicleNotes', + path: 'notes', + meta: { + title: 'notes', + icon: 'vn:notes', + }, + component: () => import('src/pages/Route/Vehicle/Card/VehicleNotes.vue'), + } ], }; diff --git a/test/cypress/integration/route/vehicle/vehicleNotes.spec.js b/test/cypress/integration/route/vehicle/vehicleNotes.spec.js new file mode 100644 index 000000000..cd92cc4af --- /dev/null +++ b/test/cypress/integration/route/vehicle/vehicleNotes.spec.js @@ -0,0 +1,28 @@ +describe('Vehicle Notes', () => { + const selectors = { + addNoteInput: 'Add note here..._input', + saveNoteBtn: 'saveNote', + deleteNoteBtn: 'notesRemoveNoteBtn', + noteCard: '.column.full-width > :nth-child(1) > .q-card__section--vert', + }; + + const noteText = 'Golpe parachoques trasero'; + const newNoteText = 'probando'; + + beforeEach(() => { + cy.viewport(1920, 1080); + cy.login('developer'); + cy.visit(`/#/route/vehicle/1/notes`); + }); + + it('Should add new note', () => { + cy.dataCy(selectors.addNoteInput).should('be.visible').type(newNoteText); + cy.dataCy(selectors.saveNoteBtn).click(); + cy.validateContent(selectors.noteCard, newNoteText); + }); + + it('Should delete note', () => { + cy.dataCy(selectors.deleteNoteBtn).first().should('be.visible').click(); + cy.get(selectors.noteCard).first().should('have.text', noteText); + }); +});