#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"
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
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
Review

Fent aço estas fent la peticio 2 vegades.
Hi hauria que fer que VnNotes (onFetch) te retornara el data directament i ahi ja crides al setWorkerObservations.

Fent aço estas fent la peticio 2 vegades. Hi hauria que fer que VnNotes (onFetch) te retornara el data directament i ahi ja crides al setWorkerObservations.
: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',