feat: same searchbar logic filter in VnFilterPanel

This commit is contained in:
Javier Segarra 2025-02-16 23:35:56 +01:00
parent 2ec5c2b49f
commit 33d6662f97
8 changed files with 154 additions and 19 deletions

View File

@ -2,7 +2,7 @@
import RightAdvancedMenu from './RightAdvancedMenu.vue';
import VnSearchbar from 'components/ui/VnSearchbar.vue';
import VnTableFilter from '../VnTable/VnTableFilter.vue';
import { onBeforeMount, onMounted, onUnmounted, computed, ref } from 'vue';
import { onBeforeMount, onMounted, onUnmounted, computed, ref, provide } from 'vue';
import { useArrayData } from 'src/composables/useArrayData';
import { useRoute, useRouter } from 'vue-router';
import { useHasContent } from 'src/composables/useHasContent';
@ -52,10 +52,12 @@ const router = useRouter();
let arrayData;
const sectionValue = computed(() => $props.section ?? $props.dataKey);
const isMainSection = ref(false);
const searchbarRef = ref(null);
const searchbarId = 'section-searchbar';
const advancedMenuSlot = 'advanced-menu';
const hasContent = useHasContent(`#${searchbarId}`);
provide('searchbar', () => searchbarRef.value?.search());
onBeforeMount(() => {
if ($props.dataKey)
@ -90,6 +92,7 @@ function checkIsMain() {
<template>
<slot name="searchbar">
<VnSearchbar
ref="searchbarRef"
v-if="searchBar && !hasContent"
v-bind="arrayDataProps"
:data-key="dataKey"

View File

@ -1,5 +1,5 @@
<script setup>
import { ref, computed } from 'vue';
import { ref, computed, provide, inject, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
import { useArrayData } from 'composables/useArrayData';
import toDate from 'filters/toDate';
@ -14,6 +14,10 @@ const $props = defineProps({
type: Object,
default: () => {},
},
searchBarRef: {
type: Object,
default: () => {},
},
dataKey: {
type: String,
required: true,
@ -61,6 +65,14 @@ const $props = defineProps({
type: Object,
default: null,
},
requiredParams: {
type: [Array, Object],
default: () => [],
},
useSearchbar: {
type: [Boolean, Function],
default: false,
},
});
const emit = defineEmits([
@ -84,13 +96,29 @@ const arrayData =
const store = arrayData.store;
const userParams = ref(useFilterParams($props.dataKey).params);
const userOrders = ref(useFilterParams($props.dataKey).orders);
const searchbar = ref(null);
defineExpose({ search, params: userParams, remove });
onMounted(() => {
searchbar.value = inject('searchbar');
});
const isLoading = ref(false);
async function search(evt) {
try {
if (evt && $props.disableSubmitEvent) return;
if ($props.useSearchbar) {
if (!searchbar.value) {
console.error('Searchbar not found');
return;
}
if (typeof $props.useSearchbar === 'function') {
$props.useSearchbar(userParams.value);
if (Object.keys(userParams.value).length == 0) {
searchbar.value();
return;
}
}
}
if (evt && $props.disableSubmitEvent) debugger;
store.filter.where = {};
isLoading.value = true;
@ -114,7 +142,7 @@ async function clearFilters() {
arrayData.resetPagination();
// Filtrar los params no removibles
const removableFilters = Object.keys(userParams.value).filter((param) =>
$props.unremovableParams.includes(param)
$props.unremovableParams.includes(param),
);
const newParams = {};
// Conservar solo los params que no son removibles
@ -162,13 +190,13 @@ const formatTags = (tags) => {
const tags = computed(() => {
const filteredTags = tagsList.value.filter(
(tag) => !($props.customTags || []).includes(tag.label)
(tag) => !($props.customTags || []).includes(tag.label),
);
return formatTags(filteredTags);
});
const customTags = computed(() =>
tagsList.value.filter((tag) => ($props.customTags || []).includes(tag.label))
tagsList.value.filter((tag) => ($props.customTags || []).includes(tag.label)),
);
async function remove(key) {
@ -191,7 +219,9 @@ const getLocale = (label) => {
if (te(globalLocale)) return t(globalLocale);
else if (te(t(`params.${param}`)));
else {
const camelCaseModuleName = route.meta.moduleName.charAt(0).toLowerCase() + route.meta.moduleName.slice(1);
const camelCaseModuleName =
route.meta.moduleName.charAt(0).toLowerCase() +
route.meta.moduleName.slice(1);
return t(`${camelCaseModuleName}.params.${param}`);
}
};

View File

@ -1,5 +1,5 @@
<script setup>
import { onMounted, ref, computed, watch } from 'vue';
import { onMounted, ref, computed, watch, provide } from 'vue';
import { useQuasar } from 'quasar';
import { useArrayData } from 'composables/useArrayData';
import VnInput from 'src/components/common/VnInput.vue';
@ -148,6 +148,7 @@ async function search() {
await arrayData.applyFilter(filter);
searchText.value = undefined;
}
defineExpose({ search });
</script>
<template>
<Teleport to="#searchbar" v-if="state.isHeaderMounted()">

View File

@ -1,5 +1,5 @@
<script setup>
import { ref } from 'vue';
import { computed, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue';
@ -8,6 +8,8 @@ import VnInput from 'src/components/common/VnInput.vue';
import VnInputDate from 'components/common/VnInputDate.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import VnSelectWorker from 'src/components/common/VnSelectWorker.vue';
import { Notify } from 'quasar';
import useNotify from 'src/composables/useNotify';
const { t } = useI18n();
const props = defineProps({
@ -15,6 +17,10 @@ const props = defineProps({
type: String,
required: true,
},
searchBarRef: {
type: Object,
default: () => ({}),
},
});
const provinces = ref([]);
@ -22,6 +28,7 @@ const states = ref([]);
const agencies = ref([]);
const warehouses = ref([]);
const groupedStates = ref([]);
const { notify } = useNotify();
const getGroupedStates = (data) => {
for (const state of data) {
@ -32,6 +39,29 @@ const getGroupedStates = (data) => {
});
}
};
const from = Date.vnNew();
from.setHours(0, 0, 0, 0);
from.setDate(from.getDate() - 7);
const to = Date.vnNew();
to.setHours(23, 59, 0, 0);
to.setDate(to.getDate() + 1);
const userParams = computed(() => {
from.value = from.toISOString();
to.value = to.toISOString();
return { from, to };
});
function validateDateRange(params) {
const hasFrom = 'from' in params;
const hasTo = 'to' in params;
if (hasFrom !== hasTo) {
notify(t(`dateRangeMustHaveBothFrom`), 'negative');
throw new Error(t(`dateRangeMustHaveBothFrom`));
}
return hasFrom && hasTo;
}
</script>
<template>
@ -48,7 +78,13 @@ const getGroupedStates = (data) => {
/>
<FetchData url="AgencyModes" @on-fetch="(data) => (agencies = data)" auto-load />
<FetchData url="Warehouses" @on-fetch="(data) => (warehouses = data)" auto-load />
<VnFilterPanel :data-key="props.dataKey" :search-button="true">
<VnFilterPanel
:searchBarRef="$props.searchBarRef"
:data-key="props.dataKey"
:search-button="true"
:use-searchbar="validateDateRange"
:requiredParams="{ ...userParams }"
>
<template #tags="{ tag, formatFn }">
<div class="q-gutter-x-xs">
<strong>{{ t(`params.${tag.label}`) }}: </strong>
@ -74,10 +110,20 @@ const getGroupedStates = (data) => {
</QItem>
<QItem>
<QItemSection>
<VnInputDate v-model="params.from" :label="t('From')" is-outlined />
<VnInputDate
v-model="params.from"
:label="t('From')"
is-outlined
data-cy="From_date"
/>
</QItemSection>
<QItemSection>
<VnInputDate v-model="params.to" :label="t('To')" is-outlined />
<VnInputDate
v-model="params.to"
:label="t('To')"
is-outlined
data-cy="To_date"
/>
</QItemSection>
</QItem>
<QItem>
@ -288,6 +334,7 @@ const getGroupedStates = (data) => {
<i18n>
en:
dateRangeMustHaveBothFrom: The date range must have both 'from' and 'to'
params:
search: Contains
clientFk: Customer
@ -315,6 +362,7 @@ en:
DELIVERED: Delivered
ON_PREVIOUS: ON_PREVIOUS
es:
dateRangeMustHaveBothFrom: El rango de fechas debe tener 'desde' y 'hasta'
params:
search: Contiene
clientFk: Cliente

View File

@ -478,6 +478,7 @@ watch(
auto-load
/>
<VnSection
ref="sectionRef"
:data-key="dataKey"
:columns="columns"
prefix="card"
@ -490,7 +491,10 @@ watch(
}"
>
<template #advanced-menu>
<TicketFilter data-key="TicketList" />
<TicketFilter
data-key="TicketList"
:searchbarRef="$refs.sectionRef?.$refs.searchbarRef"
/>
</template>
<template #body>
<VnTable

View File

@ -0,0 +1,44 @@
/// <reference types="cypress" />
describe('TicketFilter', () => {
const firstRow = 'tbody.q-virtual-scroll__content tr:nth-child(1)';
beforeEach(() => {
cy.login('developer');
cy.viewport(1920, 1080);
cy.visit('/#/ticket/list');
cy.domContentLoad();
});
it.only('use search button', function () {
cy.waitForElement('.q-page');
cy.intercept('GET', /\/api\/Tickets\/filter/).as('ticketFilter');
cy.searchBtnFilterPanel();
cy.wait('@ticketFilter').then(({ request }) => {
const { query } = request;
expect(query).to.have.property('from');
expect(query).to.have.property('to');
});
cy.on('uncaught:exception', () => {
return false;
});
cy.get('.q-field__control-container > [data-cy="From_date"]').type(
'14-02-2025{enter}',
);
cy.get('.q-notification').should(
'contain',
`The date range must have both 'from' and 'to'`,
);
cy.get('.q-field__control-container > [data-cy="To_date"]').type(
'16/02/2025{enter}',
);
cy.intercept('GET', /\/api\/Tickets\/filter/).as('ticketFilter');
cy.searchBtnFilterPanel();
cy.wait('@ticketFilter').then(({ request }) => {
const { query } = request;
expect(query).to.have.property('from');
expect(query).to.have.property('to');
});
cy.location('href').should('contain', '#/ticket/999999');
});
});

View File

@ -41,8 +41,8 @@ describe('TicketList', () => {
it('filter client and create ticket', () => {
cy.intercept('GET', /\/api\/Tickets\/filter/).as('ticketSearchbar');
searchResults();
cy.wait('@ticketSearchbar').then((interception) => {
const { query } = interception.request;
cy.wait('@ticketSearchbar').then(({ request }) => {
const { query } = request;
expect(query).to.have.property('from');
expect(query).to.have.property('to');
expect(query).to.not.have.property('clientFk');
@ -50,8 +50,8 @@ describe('TicketList', () => {
cy.intercept('GET', /\/api\/Tickets\/filter/).as('ticketFilter');
cy.get('[data-cy="Customer ID_input"]').clear('1');
cy.get('[data-cy="Customer ID_input"]').type('1101{enter}');
cy.wait('@ticketFilter').then((interception) => {
const { query } = interception.request;
cy.wait('@ticketFilter').then(({ request }) => {
const { query } = request;
expect(query).to.not.have.property('from');
expect(query).to.not.have.property('to');
expect(query).to.have.property('clientFk');

View File

@ -368,3 +368,8 @@ Cypress.Commands.add('clickButtonWithText', (buttonText) => {
Cypress.Commands.add('selectOptionBeta', (index = 1) => {
cy.get(`[role="listbox"] .q-item:nth-child(${index})`).click();
});
Cypress.Commands.add('searchBtnFilterPanel', () => {
cy.get(
'.q-scrollarea__content > .q-btn--standard > .q-btn__content > .q-icon',
).click();
});