Skip to content

Commit db000a7

Browse files
Pagination + filters
1 parent e792c1f commit db000a7

File tree

4 files changed

+223
-71
lines changed

4 files changed

+223
-71
lines changed

app/nuxt.config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ export default defineNuxtConfig({
2727
dirs: [{ path: '~/components', prefix: 'tk' }],
2828
},
2929
modules: ['nuxt-api-party', '@sidebase/nuxt-auth', 'nuxt-quasar-ui', '@vueuse/nuxt', 'dayjs-nuxt', ...extensions.appSetup.default()],
30+
dayjs: {
31+
locales: ['fr'],
32+
defaultLocale: 'fr',
33+
},
3034
auth: {
3135
baseURL: `${TK_APP_API_URL}/core/auth`,
3236
provider: {
Lines changed: 145 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
1-
<template lang="pug">
2-
.q-pa-sm(@keydown.enter="addFilter")
1+
<template lang="pug" @keypress="addFilter">
2+
.q-ma-sm
33
.row.q-gutter-sm.items-center
4-
.col-1
5-
q-toggle(v-model="inclus" :label="inclus ? 'Inclus' : 'Exclus'")
6-
.col-1
7-
q-select(:options="fieldTypes" label="Type de champ" v-model="fieldType")
8-
.col-2
9-
q-select(:options="fields" label="Champs" v-model="field")
10-
.col-1
11-
q-select(:options="comparatorTypes" label="Comparateurs" v-model="comparator")
4+
.col.col-md-2
5+
q-select(:options="fieldTypes" label="Type de champ" v-model="fieldType" clearable @update:model-value="clearFields(['field', 'comparator'])")
6+
.col.col-md-2
7+
q-select(:options="fieldsFilteredByType" label="Champs" v-model="field" clearable @update:model-value="clearFields(['comparator'])")
8+
.col.col-md-2
9+
q-select(:options="comparatorFilteredByType" label="Comparateurs" v-model="comparator" clearable @update:model-value="clearFields([])")
1210
template(v-slot:selected-item="scope")
1311
q-icon(:name="scope.opt.icon" size="xs")
14-
1512
template(v-slot:option="scope")
1613
q-item(v-bind="scope.itemProps")
1714
q-item-section(avatar)
@@ -20,12 +17,17 @@
2017
q-item-label
2118
span {{ scope.opt.label }}
2219

23-
.col-2
24-
q-input(v-model="search" label="Rechercher")
25-
.col-2
26-
q-btn(flat icon="mdi-plus" color="primary" @click="addFilter")
27-
q-tooltip.text-body2(transition-show="scale" transition-hide="scale") Ajouter
20+
.col-12.col-md-2(v-show="!comparator?.multiplefields")
21+
q-input(v-model="search" label="Rechercher" clearable :type="searchInputType" :prefix="comparator?.prefix" :suffix="comparator?.suffix")
22+
template(v-slot:append)
23+
2824

25+
.col-6.col-md-2(v-show="comparator?.multiplefields")
26+
q-input(v-model="searchMin" label="Min" clearable :type="searchInputType")
27+
.col-6.col-md-2(v-show="comparator?.multiplefields")
28+
q-input(v-model="searchMax" label="Max" clearable :type="searchInputType")
29+
.col-12.col-md-1
30+
q-btn(color="primary" @click="addFilter") Ajouter
2931
//Filters chips
3032
.row.q-gutter-sm.items-center.q-mt-sm
3133
q-chip(
@@ -35,9 +37,12 @@
3537
</template>
3638

3739
<script lang="ts" setup>
38-
import { ref } from 'vue'
40+
import { ref, computed } from 'vue'
3941
import type { PropType } from 'vue'
4042
import { useRouter, useRoute } from 'nuxt/app'
43+
import { useDayjs } from '#imports';
44+
45+
const dayjs = useDayjs()
4146
4247
type Filter = {
4348
field: string
@@ -48,12 +53,26 @@ type Filter = {
4853
type Field = {
4954
name: string
5055
label: string
56+
type: string
5157
}
5258
5359
type Comparator = {
5460
label: string
61+
querySign: string
5562
value: string
5663
icon: string
64+
type: string[]
65+
multiplefields: boolean
66+
prefix: string
67+
suffix: string
68+
}
69+
70+
type SearchFilter = {
71+
field: Field
72+
comparator: Comparator
73+
search: string
74+
searchMin: string
75+
searchMax: string
5776
}
5877
5978
const props = defineProps({
@@ -67,59 +86,102 @@ const router = useRouter()
6786
const route = useRoute()
6887
6988
const inclus = ref(true)
70-
const fieldType = ref('')
89+
const fieldType = ref<{
90+
label: string
91+
value: string
92+
}>()
93+
7194
let field = ref<Field>()
7295
let comparator = ref<Comparator>()
7396
const search = ref('')
97+
const searchMin = ref('')
98+
const searchMax = ref('')
99+
74100
75101
const filters = ref<Filter[]>([])
76102
77-
const fieldTypes = ref([
103+
const fieldTypes = ref<{
104+
label: string
105+
value: string
106+
}[]>([
78107
{ label: 'Texte', value: 'text' },
79108
{ label: 'Nombre', value: 'number' },
80109
{ label: 'Date', value: 'date' },
81-
{ label: 'Heure', value: 'time' },
82-
{ label: 'Date et heure', value: 'datetime' },
83-
{ label: 'Liste', value: 'list' },
84-
{ label: 'Liste de valeurs', value: 'listOfValues' },
85-
{ label: 'Liste de valeurs multiples', value: 'listOfValuesMultiple' },
86110
])
87111
88112
const comparatorTypes = ref<Comparator[]>([
89-
{ label: 'Egal', value: '=', icon: 'mdi-equal' },
90-
{ label: 'Différent', value: '!=', icon: 'mdi-exclamation' },
91-
{ label: 'Est supérieur à', value: '>', icon: 'mdi-greater-than' },
92-
{ label: 'Est supérieur ou égal à', value: '>=', icon: 'mdi-greater-than-or-equal' },
93-
{ label: 'Est inférieur à', value: '<', icon: 'mdi-less-than' },
94-
{ label: 'Est inférieur ou égal à', value: '<=', icon: 'mdi-less-than-or-equal' },
95-
{ label: 'Est entre', value: 'between', icon: 'mdi-arrow-expand-horizontal' },
113+
{ label: 'Egal', querySign: '=', value: '=', icon: 'mdi-equal', type: ['number'], multiplefields: false, prefix: '', suffix: '' },
114+
{ label: 'Différent', querySign: '!=', value: '!=', icon: 'mdi-exclamation', type: ['number'], multiplefields: false, prefix: '', suffix: '' },
115+
{ label: 'Est supérieur à', querySign: '>', value: '>', icon: 'mdi-greater-than', type: ['number', 'date'], multiplefields: false, prefix: '', suffix: '' },
116+
{ label: 'Est supérieur ou égal à', querySign: '>=', value: '>=', icon: 'mdi-greater-than-or-equal', type: ['number', 'date'], multiplefields: false, prefix: '', suffix: '' },
117+
{ label: 'Est inférieur à', querySign: '<', value: '<', icon: 'mdi-less-than', type: ['number', 'date'], multiplefields: false, prefix: '', suffix: '' },
118+
{ label: 'Est inférieur ou égal à', querySign: '<=', value: '<=', icon: 'mdi-less-than-or-equal', type: ['number', 'date'], multiplefields: false, prefix: '', suffix: '' },
119+
{ label: 'Est entre', querySign: '<<', value: 'between', icon: 'mdi-arrow-expand-horizontal', type: ['number', 'date'], multiplefields: true, prefix: '', suffix: '' },
120+
{ label: 'Contiens', querySign: '^', value: '^', icon: 'mdi-apple-keyboard-control', type: ['text'], multiplefields: false, prefix: '/', suffix: '/' },
121+
{ label: 'Commence par', querySign: '^', value: '/^', icon: 'mdi-apple-keyboard-control', type: ['text'], multiplefields: false, prefix: '/^', suffix: '/' },
122+
{ label: 'Fini par', querySign: '^', value: '$/', icon: 'mdi-apple-keyboard-control', type: ['text'], multiplefields: false, prefix: '/', suffix: '$/' },
96123
])
97124
98-
const addFilter = () => {
99-
if (
100-
!field.value ||
101-
!comparator.value ||
102-
!search.value
103-
) return
104-
105-
const object = {
106-
field: field.value.name,
107-
comparator: comparator.value.value,
108-
search: search.value
125+
const clearFields = (fields: string[]) => {
126+
if (fields.includes('field')) field = ref()
127+
if (fields.includes('comparator')) comparator = ref()
128+
search.value = ''
129+
searchMin.value = ''
130+
searchMax.value = ''
131+
}
132+
133+
const addFilter = async () => {
134+
const searchFilter = getSearchFilter.value
135+
if (!searchFilter) return
136+
if (searchFilter.comparator.multiplefields) {
137+
for (const { key, value } of parseMultipleFilter(searchFilter)) {
138+
await pushQuery(key, value)
139+
}
140+
} else {
141+
const { key, value } = parseSimpleFilter(searchFilter)
142+
await pushQuery(key, value)
143+
}
144+
}
145+
146+
const parseSimpleFilter = (searchFilter: SearchFilter) => {
147+
if (searchFilter.field.type === 'date') {
148+
if (searchFilter.comparator.querySign === '<' || searchFilter.comparator.querySign === '>=') searchFilter.search = dayjs(searchFilter.search).startOf('day').toISOString()
149+
if (searchFilter.comparator.querySign === '>' || searchFilter.comparator.querySign === '<=') searchFilter.search = dayjs(searchFilter.search).endOf('day').toISOString()
150+
}
151+
return {
152+
key: `filters[${searchFilter.comparator.querySign}${searchFilter.field.name}]`,
153+
value: `${searchFilter.comparator.prefix}${searchFilter.search}${searchFilter.comparator.suffix}`
154+
}
155+
}
156+
157+
const parseMultipleFilter = (searchFilter: SearchFilter) => {
158+
if (searchFilter.field.type === 'date') {
159+
searchFilter.searchMin = dayjs(searchFilter.searchMin).startOf('day').toISOString()
160+
searchFilter.searchMax = dayjs(searchFilter.searchMax).endOf('day').toISOString()
161+
}
162+
const min = {
163+
key: `filters[>=${searchFilter.field.name}]`,
164+
value: `${searchFilter.comparator.prefix}${searchFilter.searchMin}${searchFilter.comparator.suffix}`
165+
}
166+
const max = {
167+
key: `filters[<=${searchFilter.field.name}]`,
168+
value: `${searchFilter.comparator.prefix}${searchFilter.searchMax}${searchFilter.comparator.suffix}`
109169
}
110-
const key = `filter[${object.comparator}${object.field}]`
111-
const value = object.search
170+
return [min, max]
171+
}
172+
173+
const pushQuery = async (key: string, value: string) => {
112174
const query = {
113175
...route.query,
114176
}
115177
query[key] = value
116-
router.push({
178+
await router.push({
117179
query
118180
})
119181
}
120182
121183
const removeFilter = (filter: Filter) => {
122-
const key = `filter[${filter.comparator}${filter.field}]`
184+
const key = `filters[${filter.comparator}${filter.field}]`
123185
const query = {
124186
...route.query,
125187
}
@@ -131,7 +193,7 @@ const removeFilter = (filter: Filter) => {
131193
132194
const getLabelByName = (name: string) => {
133195
const field = props.fields.find(field => field.name === name)
134-
if (!field) return
196+
if (!field) return ''
135197
return field.label
136198
}
137199
@@ -141,7 +203,7 @@ const exctractComparator = (key: string): {
141203
field: string
142204
} | null => {
143205
const match = key.match(/^(\=|\?|\#|\!|\>|\<|\^|\@)+/)
144-
if (!match) return
206+
if (!match) return null
145207
const comparator = match[0]
146208
const field = key.replace(comparator, '')
147209
return {
@@ -150,28 +212,59 @@ const exctractComparator = (key: string): {
150212
}
151213
}
152214
215+
const getSearchFilter = computed(() => {
216+
if (field.value === undefined || field.value === null) return null
217+
if (comparator.value === undefined || comparator.value === null) return null
218+
return {
219+
field: field!.value,
220+
comparator: comparator!.value,
221+
search: search.value,
222+
searchMin: searchMin.value,
223+
searchMax: searchMax.value
224+
}
225+
})
226+
153227
const filterArray = computed(() => {
154228
const queries = { ...route.query }
155-
const filters: Record<string, string> = {};
229+
const filters: Record<string, { label: string, field: string, comparator: string, search: string }> = {};
156230
157231
// Iterate through the keys and values in the input object
158232
for (const key in queries) {
159233
if (queries.hasOwnProperty(key) && key.includes("filter")) {
160234
161235
// Extract the key without the "filter[" and "]" parts
162-
const filteredKey = key.replace("filter[", "").replace("]", "");
236+
const filteredKey = key.replace("filters[", "").replace("]", "");
163237
const exctract = exctractComparator(filteredKey)
164238
if (!exctract) continue
165239
const { comparator, field } = exctract
166240
// Assign the value to the extracted key in the filter array
167-
filters[field] = {
241+
filters[key] = {
168242
label: getLabelByName(field),
169243
field,
170244
comparator,
171-
search: queries[key]
245+
search: queries[key]?.toString() ?? ''
172246
};
173247
}
174248
}
175249
return filters
176250
})
251+
252+
const searchInputType = computed(() => {
253+
if (fieldType.value === undefined || fieldType.value === null) return 'text'
254+
return fieldType.value!.value
255+
})
256+
257+
const fieldsFilteredByType = computed(() => {
258+
if (fieldType.value === undefined || fieldType.value === null) return []
259+
return props.fields.filter((field) => {
260+
return field.type === fieldType.value!.value
261+
})
262+
})
263+
264+
const comparatorFilteredByType = computed(() => {
265+
if (fieldType.value === undefined || fieldType.value === null) return []
266+
return comparatorTypes.value.filter((comparator) => {
267+
return comparator.type.includes(fieldType.value!.value)
268+
})
269+
})
177270
</script>

0 commit comments

Comments
 (0)