feat: same searchbar logic filter in VnFilterPanel
This commit is contained in:
parent
2ec5c2b49f
commit
33d6662f97
|
@ -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"
|
||||
|
|
|
@ -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}`);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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()">
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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');
|
||||
});
|
||||
});
|
|
@ -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');
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue