Skip to content

Commit 60e7152

Browse files
committed
feat: add support for 'not empty' filter in search functionality
- Introduced a new filter symbol '~' to check for non-empty values in the search filter schema. - Updated the internal filtering logic to handle the new '~' symbol appropriately. - Enhanced the UI components to support the new filter type, including a dedicated input for 'not empty' conditions. - Adjusted the filters query logic to accommodate the new filter type in the composables.
1 parent db0af1b commit 60e7152

File tree

5 files changed

+54
-5
lines changed

5 files changed

+54
-5
lines changed

apps/api/src/_common/restools/search-filter-schema.decorator.spec.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,14 @@ describe('search-filter-schema', () => {
102102
).toStrictEqual({ age: { $ne: 18 } })
103103
})
104104

105+
it('test not empty filter', () => {
106+
expect(
107+
filterSchema({
108+
'~patterns': 'true',
109+
}),
110+
).toStrictEqual({ patterns: { $exists: true, $nin: [null, ''] } })
111+
})
112+
105113

106114
it('test not equal null filter', () => {
107115
expect(

apps/api/src/_common/restools/search-filter-schema.decorator.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export const FILTER_SYMBOL_IN = '@'
2323
export const FILTER_SYMBOL_REGEX = '^'
2424
export const FILTER_SYMBOL_BOOLEAN = '?'
2525
export const FILTER_SYMBOL_NUMBER = '#'
26+
export const FILTER_SYMBOL_NOT_EMPTY = '~'
2627

2728
export const DEFAULT_ALLOWED_FILTERS = [
2829
FILTER_SYMBOL_EQUAL,
@@ -33,6 +34,7 @@ export const DEFAULT_ALLOWED_FILTERS = [
3334
FILTER_SYMBOL_LESS,
3435
FILTER_SYMBOL_REGEX,
3536
FILTER_SYMBOL_IN,
37+
FILTER_SYMBOL_NOT_EMPTY,
3638
]
3739

3840
export const DEFAULT_SCHEMA_OPTIONS = {
@@ -129,6 +131,15 @@ function internalFilterbyType(
129131
break
130132
}
131133

134+
case FILTER_SYMBOL_NOT_EMPTY: {
135+
if (/true|on|yes|1/i.test(data)) {
136+
parsed[key.slice(1)] = { $exists: true, $nin: [null, ''] }
137+
} else {
138+
parsed[key.slice(1)] = { $exists: true, $in: [null, ''] }
139+
}
140+
break
141+
}
142+
132143
case FILTER_SYMBOL_LESS:
133144
case FILTER_SYMBOL_GREATER: {
134145
let upperLowerType = key[0] === FILTER_SYMBOL_GREATER ? '$gt' : '$lt'

apps/web/src/components/core/edit-filters.vue

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ q-card.transparent(style='min-width: 45vw; max-width: 90vw')
9696
span {{ scope.opt.label }}
9797
q-input.col(
9898
style='min-width: 300px'
99-
v-if="!comparator?.multiplefields && optionsMapping.length === 0 && comparator?.querySign !== '?'"
99+
v-if="!comparator?.multiplefields && optionsMapping.length === 0 && comparator?.querySign !== '?' && comparator?.querySign !== '~'"
100100
v-model='filter.value'
101101
label='Valeur'
102102
:prefix="comparator?.prefix"
@@ -120,7 +120,7 @@ q-card.transparent(style='min-width: 45vw; max-width: 90vw')
120120
)
121121
q-select.col(
122122
style='min-width: 300px'
123-
v-if="!comparator?.multiplefields && optionsMapping.length > 0"
123+
v-if="!comparator?.multiplefields && optionsMapping.length > 0 && comparator?.querySign !== '~'"
124124
v-model='filter.value'
125125
label='Valeur'
126126
:prefix="comparator?.prefix"
@@ -138,6 +138,20 @@ q-card.transparent(style='min-width: 45vw; max-width: 90vw')
138138
dense
139139
outlined
140140
)
141+
q-select.col(
142+
style='min-width: 300px'
143+
v-if="comparator?.querySign === '~'"
144+
v-model='filter.value'
145+
label='Valeur'
146+
:prefix="comparator?.prefix"
147+
:suffix="comparator?.suffix"
148+
:type='searchInputType'
149+
:readonly='!filter.operator'
150+
:options="['true', 'false']"
151+
emit-value
152+
dense
153+
outlined
154+
)
141155
q-input.col(
142156
style='min-width: 200px'
143157
v-if="comparator?.multiplefields && comparator?.querySign === '<<'"
@@ -342,7 +356,7 @@ export default defineNuxtComponent({
342356
const operator = detectInitialOperator()
343357
const comp = comparatorTypes.value.find((comp) => comp.value === operator)
344358
345-
if (comp?.type.includes('date') && initialFilter.value) {
359+
if (comp?.type.includes('date') && comp?.querySign !== '~' && initialFilter.value) {
346360
const dateValue = dayjs(initialFilter.value).format('YYYY-MM-DDTHH:mm')
347361
initialValue.value = dateValue
348362
} else {

apps/web/src/composables/useFiltersQuery.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,16 @@ export type ColumnType = {
3636
}
3737

3838
const comparatorTypes = ref<ComparatorType[]>([
39+
{
40+
label: "Est vide/non défini",
41+
querySign: '~',
42+
value: '~',
43+
icon: 'mdi-tilde',
44+
type: ['text', 'number', 'date', 'array'],
45+
multiplefields: false,
46+
prefix: '',
47+
suffix: '',
48+
},
3949
{
4050
label: 'Est un booléen',
4151
querySign: '?',
@@ -225,7 +235,7 @@ const getLabelByName = (columns: Ref<QTableProps['columns'] & { type: string }[]
225235
* @returns comparator and field extracted from the key
226236
*/
227237
const extractComparator = (key: string): { comparator: string, field: string } | null => {
228-
const match = key.match(/^(\:|\?|\||\#|\!|\>|\<|\^|\@)+/m)
238+
const match = key.match(/^(\:|\?|\||\#|\!|\>|\<|\^|\@|\~)+/m)
229239

230240
if (!match) return null
231241

@@ -438,6 +448,12 @@ export function useFiltersQuery(columns: Ref<QTableProps['columns'] & { type: st
438448
}
439449
break
440450

451+
case '~':
452+
if (value) {
453+
query[filterKey] = value
454+
}
455+
break
456+
441457
default:
442458
if (comparator?.type.includes('date') && value) {
443459
const dateValue = dayjs(value as string).toISOString()

apps/web/src/composables/useMenu.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ function useMenu(identityStateStore: ReturnType<typeof useIdentityStateStore>):
150150
{
151151
icon: 'mdi-account-switch-outline',
152152
label: 'Fusionnées',
153-
path: '/identities/table?sort[metadata.lastUpdatedAt]=desc&skip=0&filters[!:primaryEmployeeNumber]=null',
153+
path: '/identities/table?sort[metadata.lastUpdatedAt]=desc&skip=0&filters[~primaryEmployeeNumber]=true',
154154
color: 'linear-gradient(135deg, #7C3AED 0%, #EC4899 50%, #F97316 100%)',
155155
textColor: 'white',
156156
badge: { color: 'linear-gradient(135deg, #7C3AED 0%, #EC4899 50%, #F97316 100%)', textColor: 'white' },

0 commit comments

Comments
 (0)