#8193: Added filter in ClientNotes #1609
|
@ -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(
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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,
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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',
|
||||||
|
|
Loading…
Reference in New Issue
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.