#8193: Added filter in ClientNotes #1609

Open
jon wants to merge 28 commits from 8193-AddFilterInClientNotes into dev
4 changed files with 190 additions and 24 deletions

View File

@ -4,7 +4,7 @@ import { ref, reactive, useAttrs, computed } from 'vue';
import { onBeforeRouteLeave } from 'vue-router'; import { onBeforeRouteLeave } from 'vue-router';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import { useState } from 'src/composables/useState';
import { toDateHourMin } from 'src/filters'; import { toDateHourMin } from 'src/filters';
import VnPaginate from 'components/ui/VnPaginate.vue'; import VnPaginate from 'components/ui/VnPaginate.vue';
@ -23,9 +23,9 @@ const $attrs = computed(() => {
const { required, deletable, ...rest } = originalAttrs; const { required, deletable, ...rest } = originalAttrs;
return rest; return rest;
}); });
const $props = defineProps({ const $props = defineProps({
url: { type: String, default: null }, url: { type: String, default: null },
dataKey: { type: String, default: null },
saveUrl: { type: String, default: null }, saveUrl: { type: String, default: null },
userFilter: { type: Object, default: () => {} }, userFilter: { type: Object, default: () => {} },
filter: { type: Object, default: () => {} }, filter: { type: Object, default: () => {} },
@ -33,16 +33,17 @@ const $props = defineProps({
addNote: { type: Boolean, default: false }, addNote: { type: Boolean, default: false },
selectType: { type: Boolean, default: false }, selectType: { type: Boolean, default: false },
justInput: { type: Boolean, default: false }, justInput: { type: Boolean, default: false },
filterColumns: { type: Array, default: () => [] },
}); });
const { t } = useI18n(); const { t } = useI18n();
const quasar = useQuasar(); const quasar = useQuasar();
const newNote = reactive({ text: null, observationTypeFk: null }); const newNote = reactive({ text: null, observationTypeFk: null });
const observationTypes = ref([]); const observationTypes = ref([]);
const vnPaginateRef = ref(); const vnPaginateRef = ref();
const state = useState();
const defaultObservationType = computed(() => const user = state.getUser();
observationTypes.value.find(ot => ot.code === 'salesPerson')?.id const defaultObservationType = computed(
() => observationTypes.value.find((ot) => ot.code === 'salesPerson')?.id,
); );
let originalText; let originalText;
@ -122,19 +123,38 @@ function fetchData([data]) {
emit('onFetch', data); emit('onFetch', data);
} }
const handleObservationTypes = (data) => { const handleObservationTypes = async (data) => {
observationTypes.value = data; observationTypes.value = data;
if(defaultObservationType.value) {
newNote.observationTypeFk = defaultObservationType.value; const { data: departments } = await axios.get('Departments');
} const userDepartment = departments.find((ot) => ot.id === user.value.departmentFk);
const hasObservationType =
observationTypes.value.find(
(ot) => parseInt(ot.departmentFk) === userDepartment.id,
) ||
observationTypes.value.find(
(ot) => parseInt(ot.departmentFk) === userDepartment.parentFk,
);
newNote.observationTypeFk = hasObservationType
? hasObservationType.code
: defaultObservationType.value;
}; };
function exprBuilder(param, value) {
switch (param) {
case 'observationTypeFk':
case 'workerFk':
return { [param]: value };
}
}
</script> </script>
<template> <template>
<FetchData <FetchData
v-if="selectType" v-if="selectType"
Outdated
Review

Esta part no se gasta no?

Esta part no se gasta no?
url="ObservationTypes" url="ObservationTypes"
:filter="{ fields: ['id', 'description', 'code'] }" :filter="{ fields: ['id', 'description', 'code', 'departmentFk'] }"
auto-load auto-load
@on-fetch="handleObservationTypes" @on-fetch="handleObservationTypes"
/> />
@ -156,6 +176,7 @@ const handleObservationTypes = (data) => {
<QCardSection class="q-px-xs q-my-none q-py-none"> <QCardSection class="q-px-xs q-my-none q-py-none">
<VnRow class="full-width"> <VnRow class="full-width">
<VnSelect <VnSelect
class="q-mr-xs"
:label="t('Observation type')" :label="t('Observation type')"
v-if="selectType" v-if="selectType"
url="ObservationTypes" url="ObservationTypes"
@ -164,6 +185,8 @@ const handleObservationTypes = (data) => {
style="flex: 0.15" style="flex: 0.15"
:required="'required' in originalAttrs" :required="'required' in originalAttrs"
@keyup.enter.stop="insert" @keyup.enter.stop="insert"
data-cy="VnNotes-observation-type"
filled
/> />
<VnInput <VnInput
v-model.trim="newNote.text" v-model.trim="newNote.text"
@ -171,10 +194,10 @@ const handleObservationTypes = (data) => {
:label="$props.justInput && newNote.text ? '' : t('Add note here...')" :label="$props.justInput && newNote.text ? '' : t('Add note here...')"
filled filled
autogrow autogrow
jon marked this conversation as resolved Outdated
Outdated
Review

Esto no deberia estar en un componente generico, VnNotes se usa en claim, worker, etc

Esto no deberia estar en un componente generico, VnNotes se usa en claim, worker, etc
autofocus
@keyup.enter.stop="handleClick" @keyup.enter.stop="handleClick"
:required="'required' in originalAttrs" :required="'required' in originalAttrs"
clearable clearable
data-cy="VnNotes-text-input"
> >
<template #append> <template #append>
<QBtn <QBtn
@ -204,10 +227,9 @@ const handleObservationTypes = (data) => {
ref="vnPaginateRef" ref="vnPaginateRef"
class="show" class="show"
v-bind="$attrs" v-bind="$attrs"
:search-url="false" :search-url="$props.url"
@on-fetch=" @on-fetch="newNote.text = ''"
newNote.text = ''; :exprBuilder
"
> >
<template #body="{ rows }"> <template #body="{ rows }">
<TransitionGroup name="list" tag="div" class="column items-center full-width"> <TransitionGroup name="list" tag="div" class="column items-center full-width">
@ -234,6 +256,7 @@ const handleObservationTypes = (data) => {
outline outline
color="grey" color="grey"
v-if="selectType && note.observationTypeFk" v-if="selectType && note.observationTypeFk"
data-cy="VnNotes-observation-type-badge"
> >
{{ {{
observationTypes.find( observationTypes.find(

View File

@ -1,15 +1,134 @@
<script setup> <script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue';
import { useI18n } from 'vue-i18n';
import { useStateStore } from 'stores/useStateStore';
import RightMenu from 'src/components/common/RightMenu.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import FetchData from 'src/components/FetchData.vue';
import VnNotes from 'src/components/ui/VnNotes.vue'; import VnNotes from 'src/components/ui/VnNotes.vue';
import VnTableFilter from 'src/components/VnTable/VnTableFilter.vue';
import VnAvatar from 'src/components/ui/VnAvatar.vue';
const { t } = useI18n();
const url = 'clientObservations';
const stateStore = useStateStore();
const filteredWorkers = ref([]);
const columns = computed(() => [
{
name: 'observationTypeFk',
},
{
name: 'workerFk',
},
]);
const setWorkerObservations = (data) => {
const seen = new Set();
filteredWorkers.value = data;
filteredWorkers.value = data.filter((worker) => {
if (!seen.has(worker.workerFk)) {
seen.add(worker.workerFk);
return true;
}
return false;
});
};
function exprBuilder(param, value) {
switch (param) {
case 'observationTypeFk':
case 'workerFk':
return { [param]: value };
}
}
onMounted(() => {
stateStore.rightDrawerChangeValue(true);
});
onUnmounted(() => {
stateStore.rightDrawer = false;
});
</script> </script>
<template> <template>
<FetchData
:url="url"
:filter="{ fields: ['id', 'workerFk'] }"
:where="{ clientFk: $route.params.id }"
auto-load
@on-fetch="setWorkerObservations"
/>
<RightMenu>
<template #right-panel>
<VnTableFilter
data-key="clientObservations"
:data-key
:columns="columns"
:redirect="false"
:exprBuilder
:search-url="url"
:showTagChips="false"
>
<template #filter-observationTypeFk="{ params, columnName, searchFn }">
<VnSelect
:label="t('Observation type')"
url="ObservationTypes"
v-model="params[columnName]"
option-label="description"
option-value="id"
@keyup.enter="searchFn"
@update:modelValue="() => searchFn()"
dense
filled
data-cy="VnNotes-observation-type-filter"
/>
</template>
<template #filter-workerFk="{ params, columnName, searchFn }">
<VnSelect
:label="t('globals.user')"
v-model="params[columnName]"
option-label="workerFk"
option-value="workerFk"
:options="filteredWorkers"
@keyup.enter="searchFn"
@update:modelValue="() => searchFn()"
hide-selected
dense
filled
data-cy="VnNotes-user-filter"
>
<template #option="{ opt, itemProps }">
<QItem v-bind="itemProps" class="q-pa-xs row items-center">
<QItemSection class="col-3 items-center">
<VnAvatar :worker-id="opt.worker.id" />
</QItemSection>
<QItemSection class="col-9 justify-center">
<span>{{ opt.worker?.user?.name }}</span>
<span class="text-grey">
{{ `#${opt.worker?.user?.id}` }}
</span>
</QItemSection>
</QItem>
</template>
</VnSelect>
</template>
</VnTableFilter>
</template>
</RightMenu>
<VnNotes <VnNotes
url="clientObservations" :url="url"
data-key="clientObservations"
:add-note="true" :add-note="true"
:filter="{ where: { clientFk: $route.params.id } }" :filter="{ where: { clientFk: $route.params.id } }"
:body="{ clientFk: $route.params.id }" :body="{ clientFk: $route.params.id }"
style="overflow-y: auto" style="overflow-y: auto"
:select-type="true" :select-type="true"
:filter-columns="['workerFk', 'observationTypeFk']"
required required
order="created DESC" order="created DESC"
/> />
</template> </template>
<i18n>
es:
Observation type: Tipo de observación
</i18n>

View File

@ -1,13 +1,37 @@
/// <reference types="cypress" /> /// <reference types="cypress" />
describe('Client notes', () => { describe('Client notes', () => {
const obervationType = 'Packager';
const user = 'developer';
beforeEach(() => { beforeEach(() => {
cy.viewport(1280, 720); cy.viewport(1920, 1080);
cy.login('developer'); cy.login('developer');
cy.visit('#/customer/1110/notes', { cy.visit('#/customer/1102/notes');
timeout: 5000,
});
}); });
it('Should load layout', () => {
cy.get('.q-card').should('be.visible'); it('should add and filter notes by observation type', () => {
cy.selectOption("[data-cy='VnNotes-observation-type']", obervationType);
cy.dataCy('VnNotes-text-input').type('Test note {enter}');
cy.selectOption("[data-cy='VnNotes-observation-type-filter']", obervationType);
cy.get('.column.full-width')
.children()
.each(($el) => {
cy.dataCy('VnNotes-observation-type-badge').should(
'include.text',
obervationType,
);
});
});
it('should filter notes by user', () => {
cy.selectOption("[data-cy='VnNotes-user-filter']", user);
cy.get('.column.full-width')
.children()
.each(($el) => {
cy.dataCy('VnNotes-observation-type-badge').should(
'include.text',
obervationType,
);
});
}); });
}); });

View File

@ -1,6 +1,6 @@
describe('Vehicle Notes', () => { describe('Vehicle Notes', () => {
const selectors = { const selectors = {
addNoteInput: 'Add note here..._input', addNoteInput: 'VnNotes-text-input',
saveNoteBtn: 'saveNote', saveNoteBtn: 'saveNote',
deleteNoteBtn: 'notesRemoveNoteBtn', deleteNoteBtn: 'notesRemoveNoteBtn',
noteCard: '.column.full-width > :nth-child(1) > .q-card__section--vert', noteCard: '.column.full-width > :nth-child(1) > .q-card__section--vert',