@shefing/quickfilter 1.0.11 → 1.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +859 -0
- package/dist/FilterField.d.ts.map +1 -1
- package/dist/FilterField.js +18 -9
- package/dist/FilterField.js.map +1 -1
- package/dist/QuickFilter.d.ts.map +1 -1
- package/dist/QuickFilter.js +227 -55
- package/dist/QuickFilter.js.map +1 -1
- package/dist/filters/components/checkbox-filter.d.ts +2 -1
- package/dist/filters/components/checkbox-filter.d.ts.map +1 -1
- package/dist/filters/components/checkbox-filter.js +9 -7
- package/dist/filters/components/checkbox-filter.js.map +1 -1
- package/dist/filters/components/date-filter.d.ts +2 -1
- package/dist/filters/components/date-filter.d.ts.map +1 -1
- package/dist/filters/components/date-filter.js +26 -24
- package/dist/filters/components/date-filter.js.map +1 -1
- package/dist/filters/components/select-filter.d.ts +2 -1
- package/dist/filters/components/select-filter.d.ts.map +1 -1
- package/dist/filters/components/select-filter.js +20 -18
- package/dist/filters/components/select-filter.js.map +1 -1
- package/dist/filters/components/small-select-filter.d.ts +2 -1
- package/dist/filters/components/small-select-filter.d.ts.map +1 -1
- package/dist/filters/components/small-select-filter.js +4 -3
- package/dist/filters/components/small-select-filter.js.map +1 -1
- package/dist/filters/constants/date-filter-options.d.ts +7 -4
- package/dist/filters/constants/date-filter-options.d.ts.map +1 -1
- package/dist/filters/constants/date-filter-options.js +25 -70
- package/dist/filters/constants/date-filter-options.js.map +1 -1
- package/dist/filters/types/filters-type.d.ts.map +1 -1
- package/dist/filters/types/filters-type.js.map +1 -1
- package/dist/filters/utils/date-helpers.d.ts +4 -3
- package/dist/filters/utils/date-helpers.d.ts.map +1 -1
- package/dist/filters/utils/date-helpers.js +14 -11
- package/dist/filters/utils/date-helpers.js.map +1 -1
- package/dist/filters/utils/layout-helpers.d.ts.map +1 -1
- package/dist/filters/utils/layout-helpers.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/labels.d.ts +326 -0
- package/dist/labels.d.ts.map +1 -0
- package/dist/labels.js +192 -0
- package/dist/labels.js.map +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FilterField.d.ts","sourceRoot":"","sources":["../src/FilterField.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"FilterField.d.ts","sourceRoot":"","sources":["../src/FilterField.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAsB,MAAM,OAAO,CAAC;AAS3C,QAAA,MAAM,WAAW,uDAId;IACD,KAAK,EAAE,GAAG,CAAC;IACX,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IACxD,KAAK,EAAE,GAAG,CAAC;CACZ,sBAsFA,CAAC;AAEF,eAAe,WAAW,CAAC"}
|
package/dist/FilterField.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// FilterField.tsx (הקובץ המתוקן)
|
|
1
2
|
'use client';
|
|
2
3
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
4
|
import React, { useCallback } from 'react';
|
|
@@ -12,7 +13,7 @@ const FilterField = ({ field, onFilterChange, value: controlledValue })=>{
|
|
|
12
13
|
const localeLang = i18n.language;
|
|
13
14
|
const isRTL = rtlLanguages.includes(localeLang);
|
|
14
15
|
const direction = isRTL ? 'rtl' : 'ltr';
|
|
15
|
-
const
|
|
16
|
+
const locale = {
|
|
16
17
|
code: localeLang,
|
|
17
18
|
direction
|
|
18
19
|
};
|
|
@@ -40,8 +41,10 @@ const FilterField = ({ field, onFilterChange, value: controlledValue })=>{
|
|
|
40
41
|
label: field.label,
|
|
41
42
|
value: controlledValue,
|
|
42
43
|
onChange: handleDateFilterChange,
|
|
43
|
-
locale:
|
|
44
|
-
|
|
44
|
+
locale: locale,
|
|
45
|
+
style: {
|
|
46
|
+
width: field.width || '230px'
|
|
47
|
+
}
|
|
45
48
|
}, field.name);
|
|
46
49
|
case 'select':
|
|
47
50
|
if (field.options.length <= 3) {
|
|
@@ -53,8 +56,10 @@ const FilterField = ({ field, onFilterChange, value: controlledValue })=>{
|
|
|
53
56
|
})),
|
|
54
57
|
onChange: handleSelectFilterChange,
|
|
55
58
|
value: controlledValue,
|
|
56
|
-
locale:
|
|
57
|
-
|
|
59
|
+
locale: locale,
|
|
60
|
+
style: {
|
|
61
|
+
width: field.width || '230px'
|
|
62
|
+
}
|
|
58
63
|
}, field.name);
|
|
59
64
|
}
|
|
60
65
|
return /*#__PURE__*/ _jsx(SelectFilter, {
|
|
@@ -65,8 +70,10 @@ const FilterField = ({ field, onFilterChange, value: controlledValue })=>{
|
|
|
65
70
|
})),
|
|
66
71
|
onChange: handleSelectFilterChange,
|
|
67
72
|
value: controlledValue,
|
|
68
|
-
locale:
|
|
69
|
-
|
|
73
|
+
locale: locale,
|
|
74
|
+
style: {
|
|
75
|
+
width: field.width || '230px'
|
|
76
|
+
}
|
|
70
77
|
}, field.name);
|
|
71
78
|
case 'checkbox':
|
|
72
79
|
return /*#__PURE__*/ _jsx(CheckboxFilter, {
|
|
@@ -74,8 +81,10 @@ const FilterField = ({ field, onFilterChange, value: controlledValue })=>{
|
|
|
74
81
|
onChange: handleCheckboxFilterChange,
|
|
75
82
|
value: controlledValue,
|
|
76
83
|
checkboxLabel: '',
|
|
77
|
-
locale:
|
|
78
|
-
|
|
84
|
+
locale: locale,
|
|
85
|
+
style: {
|
|
86
|
+
width: field.width || '230px'
|
|
87
|
+
}
|
|
79
88
|
}, field.name);
|
|
80
89
|
default:
|
|
81
90
|
return null;
|
package/dist/FilterField.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/FilterField.tsx"],"sourcesContent":["'use client';\nimport React, { useCallback } from 'react';\nimport { useTranslation } from '@payloadcms/ui';\nimport { getTranslation, rtlLanguages } from '@payloadcms/translations';\nimport {
|
|
1
|
+
{"version":3,"sources":["../src/FilterField.tsx"],"sourcesContent":["// FilterField.tsx (הקובץ המתוקן)\n\n'use client';\nimport React, { useCallback } from 'react';\nimport { useTranslation } from '@payloadcms/ui';\nimport { getTranslation, rtlLanguages } from '@payloadcms/translations';\nimport { Locale } from './filters/types/filters-type';\nimport { DateFilter } from './filters/components/date-filter';\nimport { SmallSelectFilter } from './filters/components/small-select-filter';\nimport { SelectFilter } from './filters/components/select-filter';\nimport { CheckboxFilter } from './filters/components/checkbox-filter';\n\nconst FilterField = ({\n field,\n onFilterChange,\n value: controlledValue,\n}: {\n field: any;\n onFilterChange: (fieldName: string, value: any) => void;\n value: any;\n}) => {\n const { i18n } = useTranslation();\n const localeLang = i18n.language;\n const isRTL = (rtlLanguages as readonly string[]).includes(localeLang);\n const direction = isRTL ? 'rtl' : 'ltr';\n const locale = { code: localeLang, direction } as Locale;\n\n const handleDateFilterChange = useCallback(\n (value: any) => {\n onFilterChange(field.name, value);\n },\n [onFilterChange, field.name],\n );\n\n const handleSelectFilterChange = useCallback(\n (value: any) => {\n onFilterChange(field.name, value);\n },\n [onFilterChange, field.name],\n );\n\n const handleCheckboxFilterChange = useCallback(\n (state: any) => {\n onFilterChange(field.name, state);\n },\n [onFilterChange, field.name],\n );\n\n switch (field.type) {\n case 'date':\n return (\n <DateFilter\n label={field.label}\n value={controlledValue}\n key={field.name}\n onChange={handleDateFilterChange}\n locale={locale}\n style={{ width: field.width || '230px' }}\n />\n );\n case 'select':\n if (field.options.length <= 3) {\n return (\n <SmallSelectFilter\n label={field.label}\n key={field.name}\n options={(field.options || []).map((option: any) => ({\n label: getTranslation(option.label, i18n),\n value: option.value,\n }))}\n onChange={handleSelectFilterChange}\n value={controlledValue}\n locale={locale}\n style={{ width: field.width || '230px' }}\n />\n );\n }\n return (\n <SelectFilter\n label={field.label}\n key={field.name}\n options={(field.options || []).map((option: any) => ({\n label: getTranslation(option.label, i18n),\n value: option.value,\n }))}\n onChange={handleSelectFilterChange}\n value={controlledValue}\n locale={locale}\n style={{ width: field.width || '230px' }}\n />\n );\n case 'checkbox':\n return (\n <CheckboxFilter\n label={field.label}\n key={field.name}\n onChange={handleCheckboxFilterChange}\n value={controlledValue}\n checkboxLabel={''}\n locale={locale}\n style={{ width: field.width || '230px' }}\n />\n );\n default:\n return null;\n }\n};\n\nexport default FilterField;\n"],"names":["React","useCallback","useTranslation","getTranslation","rtlLanguages","DateFilter","SmallSelectFilter","SelectFilter","CheckboxFilter","FilterField","field","onFilterChange","value","controlledValue","i18n","localeLang","language","isRTL","includes","direction","locale","code","handleDateFilterChange","name","handleSelectFilterChange","handleCheckboxFilterChange","state","type","label","onChange","style","width","options","length","map","option","checkboxLabel"],"mappings":"AAAA,iCAAiC;AAEjC;;AACA,OAAOA,SAASC,WAAW,QAAQ,QAAQ;AAC3C,SAASC,cAAc,QAAQ,iBAAiB;AAChD,SAASC,cAAc,EAAEC,YAAY,QAAQ,2BAA2B;AAExE,SAASC,UAAU,QAAQ,mCAAmC;AAC9D,SAASC,iBAAiB,QAAQ,2CAA2C;AAC7E,SAASC,YAAY,QAAQ,qCAAqC;AAClE,SAASC,cAAc,QAAQ,uCAAuC;AAEtE,MAAMC,cAAc,CAAC,EACnBC,KAAK,EACLC,cAAc,EACdC,OAAOC,eAAe,EAKvB;IACC,MAAM,EAAEC,IAAI,EAAE,GAAGZ;IACjB,MAAMa,aAAaD,KAAKE,QAAQ;IAChC,MAAMC,QAAQ,AAACb,aAAmCc,QAAQ,CAACH;IAC3D,MAAMI,YAAYF,QAAQ,QAAQ;IAClC,MAAMG,SAAS;QAAEC,MAAMN;QAAYI;IAAU;IAE7C,MAAMG,yBAAyBrB,YAC7B,CAACW;QACCD,eAAeD,MAAMa,IAAI,EAAEX;IAC7B,GACA;QAACD;QAAgBD,MAAMa,IAAI;KAAC;IAG9B,MAAMC,2BAA2BvB,YAC/B,CAACW;QACCD,eAAeD,MAAMa,IAAI,EAAEX;IAC7B,GACA;QAACD;QAAgBD,MAAMa,IAAI;KAAC;IAG9B,MAAME,6BAA6BxB,YACjC,CAACyB;QACCf,eAAeD,MAAMa,IAAI,EAAEG;IAC7B,GACA;QAACf;QAAgBD,MAAMa,IAAI;KAAC;IAG9B,OAAQb,MAAMiB,IAAI;QAChB,KAAK;YACH,qBACE,KAACtB;gBACCuB,OAAOlB,MAAMkB,KAAK;gBAClBhB,OAAOC;gBAEPgB,UAAUP;gBACVF,QAAQA;gBACRU,OAAO;oBAAEC,OAAOrB,MAAMqB,KAAK,IAAI;gBAAQ;eAHlCrB,MAAMa,IAAI;QAMrB,KAAK;YACH,IAAIb,MAAMsB,OAAO,CAACC,MAAM,IAAI,GAAG;gBAC7B,qBACE,KAAC3B;oBACCsB,OAAOlB,MAAMkB,KAAK;oBAElBI,SAAS,AAACtB,CAAAA,MAAMsB,OAAO,IAAI,EAAE,AAAD,EAAGE,GAAG,CAAC,CAACC,SAAiB,CAAA;4BACnDP,OAAOzB,eAAegC,OAAOP,KAAK,EAAEd;4BACpCF,OAAOuB,OAAOvB,KAAK;wBACrB,CAAA;oBACAiB,UAAUL;oBACVZ,OAAOC;oBACPO,QAAQA;oBACRU,OAAO;wBAAEC,OAAOrB,MAAMqB,KAAK,IAAI;oBAAQ;mBARlCrB,MAAMa,IAAI;YAWrB;YACA,qBACE,KAAChB;gBACCqB,OAAOlB,MAAMkB,KAAK;gBAElBI,SAAS,AAACtB,CAAAA,MAAMsB,OAAO,IAAI,EAAE,AAAD,EAAGE,GAAG,CAAC,CAACC,SAAiB,CAAA;wBACnDP,OAAOzB,eAAegC,OAAOP,KAAK,EAAEd;wBACpCF,OAAOuB,OAAOvB,KAAK;oBACrB,CAAA;gBACAiB,UAAUL;gBACVZ,OAAOC;gBACPO,QAAQA;gBACRU,OAAO;oBAAEC,OAAOrB,MAAMqB,KAAK,IAAI;gBAAQ;eARlCrB,MAAMa,IAAI;QAWrB,KAAK;YACH,qBACE,KAACf;gBACCoB,OAAOlB,MAAMkB,KAAK;gBAElBC,UAAUJ;gBACVb,OAAOC;gBACPuB,eAAe;gBACfhB,QAAQA;gBACRU,OAAO;oBAAEC,OAAOrB,MAAMqB,KAAK,IAAI;gBAAQ;eALlCrB,MAAMa,IAAI;QAQrB;YACE,OAAO;IACX;AACF;AAEA,eAAed,YAAY"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QuickFilter.d.ts","sourceRoot":"","sources":["../src/QuickFilter.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"QuickFilter.d.ts","sourceRoot":"","sources":["../src/QuickFilter.tsx"],"names":[],"mappings":"AAmQA,QAAA,MAAM,WAAW,0BAGd;IACD,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,CAAC,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE,EAAE,CAAC;CAC5D,gCA0SA,CAAC;AAEF,eAAe,WAAW,CAAC"}
|
package/dist/QuickFilter.js
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
3
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
4
|
import { useConfig, useListQuery, useTranslation } from '@payloadcms/ui';
|
|
5
5
|
import { getTranslation } from '@payloadcms/translations';
|
|
6
6
|
import FilterField from './FilterField';
|
|
7
|
+
import { getLabel } from './labels';
|
|
7
8
|
import { groupFiltersByRow, parseColumns } from './filters/utils/layout-helpers';
|
|
8
9
|
import { ChevronDown, ChevronUp, Filter, X } from 'lucide-react';
|
|
9
|
-
import { futureDateFilterOptions, pastDateFilterOptions } from './filters/constants/date-filter-options';
|
|
10
10
|
import { getDateRangeForOption } from './filters/utils/date-helpers';
|
|
11
|
+
import { isEqual } from 'lodash';
|
|
12
|
+
import { futureOptionKeys, getDateFilterOptions, pastOptionKeys } from './filters/constants/date-filter-options';
|
|
11
13
|
import { Button } from './ui/button';
|
|
12
14
|
// Recursive function to find fields by name
|
|
13
15
|
function findFieldsByName(fields, fieldNames) {
|
|
@@ -36,13 +38,14 @@ function findFieldsByName(fields, fieldNames) {
|
|
|
36
38
|
recursiveSearch(fields);
|
|
37
39
|
return results;
|
|
38
40
|
}
|
|
39
|
-
//
|
|
40
|
-
const
|
|
41
|
-
const
|
|
41
|
+
// Builds an array of condition objects from the quick filter values
|
|
42
|
+
const buildQuickFilterConditions = (values, fieldDefs, locale)=>{
|
|
43
|
+
const conditions = [];
|
|
42
44
|
Object.entries(values).forEach(([fieldName, value])=>{
|
|
43
45
|
if (!value) return;
|
|
44
46
|
const fieldDef = fieldDefs.find((f)=>f.name === fieldName);
|
|
45
47
|
if (!fieldDef) return;
|
|
48
|
+
let condition = null;
|
|
46
49
|
switch(fieldDef.type){
|
|
47
50
|
case 'date':
|
|
48
51
|
{
|
|
@@ -50,25 +53,21 @@ const buildWhereClause = (values, fieldDefs, isHebrew)=>{
|
|
|
50
53
|
let from;
|
|
51
54
|
let to;
|
|
52
55
|
if (dateValue.predefinedValue) {
|
|
53
|
-
const locale = isHebrew ? 'he' : 'en';
|
|
54
56
|
const range = getDateRangeForOption(dateValue.predefinedValue, locale);
|
|
55
57
|
from = range.from;
|
|
56
58
|
to = range.to;
|
|
57
59
|
} else if (dateValue.customRange) {
|
|
58
|
-
if (dateValue.customRange.from)
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
if (dateValue.customRange.to) {
|
|
62
|
-
to = new Date(dateValue.customRange.to);
|
|
63
|
-
}
|
|
60
|
+
if (dateValue.customRange.from) from = new Date(dateValue.customRange.from);
|
|
61
|
+
if (dateValue.customRange.to) to = new Date(dateValue.customRange.to);
|
|
64
62
|
}
|
|
65
|
-
// Construct the query
|
|
66
63
|
if (from || to) {
|
|
67
64
|
const dateQuery = {};
|
|
68
65
|
if (from) dateQuery.greater_than_equal = from;
|
|
69
66
|
if (to) dateQuery.less_than_equal = to;
|
|
70
67
|
if (Object.keys(dateQuery).length > 0) {
|
|
71
|
-
|
|
68
|
+
condition = {
|
|
69
|
+
[fieldName]: dateQuery
|
|
70
|
+
};
|
|
72
71
|
}
|
|
73
72
|
}
|
|
74
73
|
break;
|
|
@@ -78,12 +77,16 @@ const buildWhereClause = (values, fieldDefs, isHebrew)=>{
|
|
|
78
77
|
const selectValue = value;
|
|
79
78
|
if (selectValue.selectedValues && selectValue.selectedValues.length > 0) {
|
|
80
79
|
if (selectValue.selectedValues.length === 1) {
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
condition = {
|
|
81
|
+
[fieldName]: {
|
|
82
|
+
equals: selectValue.selectedValues[0]
|
|
83
|
+
}
|
|
83
84
|
};
|
|
84
85
|
} else {
|
|
85
|
-
|
|
86
|
-
|
|
86
|
+
condition = {
|
|
87
|
+
[fieldName]: {
|
|
88
|
+
in: selectValue.selectedValues
|
|
89
|
+
}
|
|
87
90
|
};
|
|
88
91
|
}
|
|
89
92
|
}
|
|
@@ -93,19 +96,141 @@ const buildWhereClause = (values, fieldDefs, isHebrew)=>{
|
|
|
93
96
|
{
|
|
94
97
|
const checkboxState = value;
|
|
95
98
|
if (checkboxState === 'checked') {
|
|
96
|
-
|
|
97
|
-
|
|
99
|
+
condition = {
|
|
100
|
+
[fieldName]: {
|
|
101
|
+
equals: 'true'
|
|
102
|
+
}
|
|
98
103
|
};
|
|
99
104
|
} else if (checkboxState === 'unchecked') {
|
|
100
|
-
|
|
101
|
-
|
|
105
|
+
condition = {
|
|
106
|
+
[fieldName]: {
|
|
107
|
+
equals: 'false'
|
|
108
|
+
}
|
|
102
109
|
};
|
|
103
110
|
}
|
|
104
111
|
break;
|
|
105
112
|
}
|
|
106
113
|
}
|
|
114
|
+
if (condition) {
|
|
115
|
+
conditions.push(condition);
|
|
116
|
+
}
|
|
107
117
|
});
|
|
108
|
-
return
|
|
118
|
+
return conditions;
|
|
119
|
+
};
|
|
120
|
+
// Helper function to remove quick filter conditions from a 'where' clause
|
|
121
|
+
const cleanWhereClause = (clause, fieldsToClean)=>{
|
|
122
|
+
if (!clause || typeof clause !== 'object' || Array.isArray(clause)) {
|
|
123
|
+
return clause;
|
|
124
|
+
}
|
|
125
|
+
const newClause = {};
|
|
126
|
+
for(const key in clause){
|
|
127
|
+
if (key === 'and' || key === 'or') {
|
|
128
|
+
const cleanedSubClauses = clause[key].map((subClause)=>cleanWhereClause(subClause, fieldsToClean)).filter(Boolean);
|
|
129
|
+
if (cleanedSubClauses.length > 0) {
|
|
130
|
+
newClause[key] = cleanedSubClauses;
|
|
131
|
+
}
|
|
132
|
+
} else if (!fieldsToClean.has(key)) {
|
|
133
|
+
newClause[key] = clause[key];
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (Object.keys(newClause).length === 0) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
if (newClause.and?.length === 1 && Object.keys(newClause).length === 1) {
|
|
140
|
+
return newClause.and[0];
|
|
141
|
+
}
|
|
142
|
+
if (newClause.or?.length === 1 && Object.keys(newClause).length === 1) {
|
|
143
|
+
return newClause.or[0];
|
|
144
|
+
}
|
|
145
|
+
return newClause;
|
|
146
|
+
};
|
|
147
|
+
// Translates URL query conditions to the quick filter's internal state
|
|
148
|
+
const parseWhereClauseToFilterValues = (where, fields, locale)=>{
|
|
149
|
+
const values = {};
|
|
150
|
+
const fieldNames = new Set(fields.map((f)=>f.name));
|
|
151
|
+
const recursiveParse = (clause)=>{
|
|
152
|
+
if (!clause || typeof clause !== 'object') return;
|
|
153
|
+
if (clause.and) {
|
|
154
|
+
clause.and.forEach(recursiveParse);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (clause.or) {
|
|
158
|
+
clause.or.forEach(recursiveParse);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
for(const fieldName in clause){
|
|
162
|
+
if (fieldNames.has(fieldName)) {
|
|
163
|
+
const fieldDef = fields.find((f)=>f.name === fieldName);
|
|
164
|
+
const condition = clause[fieldName];
|
|
165
|
+
if (fieldDef && condition && typeof condition === 'object') {
|
|
166
|
+
if ('equals' in condition) {
|
|
167
|
+
if (fieldDef.type === 'checkbox') {
|
|
168
|
+
values[fieldName] = condition.equals == 'true' ? 'checked' : 'unchecked';
|
|
169
|
+
} else if (fieldDef.type === 'select') {
|
|
170
|
+
values[fieldName] = {
|
|
171
|
+
selectedValues: [
|
|
172
|
+
condition.equals
|
|
173
|
+
]
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
} else if ('in' in condition && Array.isArray(condition.in)) {
|
|
177
|
+
if (fieldDef.type === 'select') {
|
|
178
|
+
values[fieldName] = {
|
|
179
|
+
selectedValues: condition.in
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
} else if ('greater_than_equal' in condition || 'less_than_equal' in condition) {
|
|
183
|
+
if (fieldDef.type === 'date') {
|
|
184
|
+
const fromDate = condition.greater_than_equal ? new Date(condition.greater_than_equal) : null;
|
|
185
|
+
const toDate = condition.less_than_equal ? new Date(condition.less_than_equal) : null;
|
|
186
|
+
const allDateOptions = [
|
|
187
|
+
...pastOptionKeys,
|
|
188
|
+
...futureOptionKeys
|
|
189
|
+
];
|
|
190
|
+
let matchedOption = null;
|
|
191
|
+
for (const option of allDateOptions){
|
|
192
|
+
const range = getDateRangeForOption(option, locale);
|
|
193
|
+
let isFromMatch;
|
|
194
|
+
if (fromDate) {
|
|
195
|
+
isFromMatch = range.from?.toDateString() === fromDate.toDateString();
|
|
196
|
+
} else if (fromDate == null && range.to == undefined) {
|
|
197
|
+
// all future: fromDate == null & range.to == undefined
|
|
198
|
+
isFromMatch = true;
|
|
199
|
+
}
|
|
200
|
+
let isToMatch;
|
|
201
|
+
if (toDate) {
|
|
202
|
+
isToMatch = range.to?.toDateString() === toDate.toDateString();
|
|
203
|
+
} else if (toDate == null && range.to == undefined) {
|
|
204
|
+
// all future: fromDate == null & range.to == undefined
|
|
205
|
+
isToMatch = true;
|
|
206
|
+
}
|
|
207
|
+
if (isFromMatch && isToMatch) {
|
|
208
|
+
matchedOption = option;
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (matchedOption) {
|
|
213
|
+
values[fieldName] = {
|
|
214
|
+
type: 'predefined',
|
|
215
|
+
predefinedValue: matchedOption
|
|
216
|
+
};
|
|
217
|
+
} else {
|
|
218
|
+
values[fieldName] = {
|
|
219
|
+
type: 'custom',
|
|
220
|
+
customRange: {
|
|
221
|
+
from: fromDate,
|
|
222
|
+
to: toDate
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
recursiveParse(where);
|
|
233
|
+
return values;
|
|
109
234
|
};
|
|
110
235
|
const QuickFilter = ({ slug, filterList })=>{
|
|
111
236
|
const localStorageKey = useMemo(()=>`direct-filter-${slug}`, [
|
|
@@ -117,13 +242,13 @@ const QuickFilter = ({ slug, filterList })=>{
|
|
|
117
242
|
const { refineListData, query } = useListQuery();
|
|
118
243
|
const { getEntityConfig } = useConfig();
|
|
119
244
|
const { i18n } = useTranslation();
|
|
245
|
+
const locale = i18n.language;
|
|
246
|
+
const isSyncingFromQuery = useRef(false);
|
|
120
247
|
const [filterValues, setFilterValues] = useState(()=>{
|
|
121
248
|
if (typeof window == 'undefined') return {};
|
|
122
249
|
try {
|
|
123
250
|
const item = window.localStorage.getItem(localStorageKey);
|
|
124
|
-
if (!item) {
|
|
125
|
-
return {};
|
|
126
|
-
}
|
|
251
|
+
if (!item) return {};
|
|
127
252
|
const dateTimeReviver = (key, value)=>{
|
|
128
253
|
const isoDateRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/;
|
|
129
254
|
if (typeof value === 'string' && isoDateRegex.test(value)) {
|
|
@@ -137,6 +262,7 @@ const QuickFilter = ({ slug, filterList })=>{
|
|
|
137
262
|
return {};
|
|
138
263
|
}
|
|
139
264
|
});
|
|
265
|
+
// Build the list of filter fields from config
|
|
140
266
|
useEffect(()=>{
|
|
141
267
|
const collection = getEntityConfig({
|
|
142
268
|
collectionSlug: slug
|
|
@@ -174,25 +300,54 @@ const QuickFilter = ({ slug, filterList })=>{
|
|
|
174
300
|
getEntityConfig,
|
|
175
301
|
i18n
|
|
176
302
|
]);
|
|
303
|
+
// Sync from URL (query.where) into internal state
|
|
304
|
+
useEffect(()=>{
|
|
305
|
+
if (fields.length === 0) return;
|
|
306
|
+
const valuesFromQuery = parseWhereClauseToFilterValues(query.where, fields, locale);
|
|
307
|
+
if (!isEqual(valuesFromQuery, filterValues)) {
|
|
308
|
+
// Lock to prevent feedback loop when internal state changes
|
|
309
|
+
isSyncingFromQuery.current = true;
|
|
310
|
+
setFilterValues(valuesFromQuery);
|
|
311
|
+
}
|
|
312
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
313
|
+
}, [
|
|
314
|
+
query.where,
|
|
315
|
+
fields
|
|
316
|
+
]);
|
|
317
|
+
// Sync internal state (filterValues) back into the URL
|
|
177
318
|
useEffect(()=>{
|
|
178
|
-
|
|
319
|
+
// If the change originated from the first effect, skip to avoid infinite loop
|
|
320
|
+
if (isSyncingFromQuery.current) {
|
|
321
|
+
isSyncingFromQuery.current = false;
|
|
179
322
|
return;
|
|
180
323
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
324
|
+
if (fields.length === 0) return;
|
|
325
|
+
const quickFilterConditions = buildQuickFilterConditions(filterValues, fields, locale);
|
|
326
|
+
const quickFilterFieldNames = new Set(fields.map((f)=>f.name));
|
|
327
|
+
const otherFilters = cleanWhereClause(query.where, quickFilterFieldNames);
|
|
328
|
+
const allConditions = [
|
|
329
|
+
...quickFilterConditions
|
|
330
|
+
];
|
|
331
|
+
if (otherFilters) {
|
|
332
|
+
if (otherFilters.and && Array.isArray(otherFilters.and)) {
|
|
333
|
+
allConditions.push(...otherFilters.and);
|
|
334
|
+
} else if (Object.keys(otherFilters).length > 0) {
|
|
335
|
+
allConditions.push(otherFilters);
|
|
188
336
|
}
|
|
189
|
-
} catch (error) {
|
|
190
|
-
console.error('Failed to save filters to localStorage', error);
|
|
191
337
|
}
|
|
192
|
-
|
|
338
|
+
let newWhere = {};
|
|
339
|
+
if (allConditions.length > 1) {
|
|
340
|
+
newWhere = {
|
|
341
|
+
and: allConditions
|
|
342
|
+
};
|
|
343
|
+
} else if (allConditions.length === 1) {
|
|
344
|
+
newWhere = allConditions[0];
|
|
345
|
+
}
|
|
346
|
+
// Only update if the query has actually changed to avoid unnecessary updates
|
|
347
|
+
if (!isEqual(newWhere, query.where)) {
|
|
193
348
|
refineListData({
|
|
194
349
|
columns: parseColumns(query.columns),
|
|
195
|
-
where,
|
|
350
|
+
where: newWhere,
|
|
196
351
|
page: '1'
|
|
197
352
|
});
|
|
198
353
|
}
|
|
@@ -200,8 +355,25 @@ const QuickFilter = ({ slug, filterList })=>{
|
|
|
200
355
|
}, [
|
|
201
356
|
filterValues,
|
|
202
357
|
fields,
|
|
358
|
+
i18n.language,
|
|
359
|
+
refineListData
|
|
360
|
+
]);
|
|
361
|
+
// Effect for persisting to localStorage
|
|
362
|
+
useEffect(()=>{
|
|
363
|
+
try {
|
|
364
|
+
if (Object.keys(filterValues).length > 0) {
|
|
365
|
+
localStorage.setItem(localStorageKey, JSON.stringify(filterValues));
|
|
366
|
+
} else {
|
|
367
|
+
localStorage.removeItem(localStorageKey);
|
|
368
|
+
}
|
|
369
|
+
} catch (error) {
|
|
370
|
+
console.error('Failed to save filters to localStorage', error);
|
|
371
|
+
}
|
|
372
|
+
}, [
|
|
373
|
+
filterValues,
|
|
203
374
|
localStorageKey
|
|
204
375
|
]);
|
|
376
|
+
// Updates only the internal state
|
|
205
377
|
const handleFilterChange = useCallback((fieldName, value)=>{
|
|
206
378
|
setFilterValues((prev)=>{
|
|
207
379
|
const newValues = {
|
|
@@ -218,7 +390,7 @@ const QuickFilter = ({ slug, filterList })=>{
|
|
|
218
390
|
// This function remains largely the same.
|
|
219
391
|
const getActiveFiltersDetails = ()=>{
|
|
220
392
|
const activeFilters = [];
|
|
221
|
-
const
|
|
393
|
+
const locale = i18n.language;
|
|
222
394
|
Object.entries(filterValues).forEach(([fieldName, value])=>{
|
|
223
395
|
const field = fields.find((f)=>f.name === fieldName);
|
|
224
396
|
if (!field) return;
|
|
@@ -228,14 +400,15 @@ const QuickFilter = ({ slug, filterList })=>{
|
|
|
228
400
|
const dateValue = value;
|
|
229
401
|
let dateDescription = '';
|
|
230
402
|
if (dateValue.type === 'predefined' && dateValue.predefinedValue) {
|
|
403
|
+
const { pastOptions, futureOptions } = getDateFilterOptions(locale);
|
|
231
404
|
const allOptions = [
|
|
232
|
-
...
|
|
233
|
-
...
|
|
405
|
+
...pastOptions,
|
|
406
|
+
...futureOptions
|
|
234
407
|
];
|
|
235
408
|
const option = allOptions.find((opt)=>opt.value === dateValue.predefinedValue);
|
|
236
|
-
dateDescription = option ? option.label :
|
|
409
|
+
dateDescription = option ? option.label : getLabel('custom', locale);
|
|
237
410
|
} else if (dateValue.type === 'custom' || dateValue.customRange) {
|
|
238
|
-
dateDescription =
|
|
411
|
+
dateDescription = getLabel('custom', locale);
|
|
239
412
|
}
|
|
240
413
|
if (dateDescription) {
|
|
241
414
|
activeFilters.push(`${field.label} (${dateDescription})`);
|
|
@@ -246,19 +419,24 @@ const QuickFilter = ({ slug, filterList })=>{
|
|
|
246
419
|
{
|
|
247
420
|
const selectValue = value;
|
|
248
421
|
if (selectValue && selectValue.selectedValues && selectValue.selectedValues.length > 0) {
|
|
249
|
-
const count = selectValue.selectedValues.length;
|
|
250
422
|
const totalOptions = field.options?.length || 0;
|
|
251
423
|
if (selectValue.selectedValues.length === totalOptions) {
|
|
252
|
-
activeFilters.push(`${field.label} (${
|
|
424
|
+
activeFilters.push(`${field.label} (${getLabel('all', locale)})`);
|
|
425
|
+
} else if (selectValue.selectedValues.length === 1) {
|
|
426
|
+
// Show the actual option name when only one is selected
|
|
427
|
+
const selectedOption = field.options?.find((opt)=>opt.value === selectValue.selectedValues[0]);
|
|
428
|
+
const optionLabel = selectedOption ? selectedOption.label : selectValue.selectedValues[0];
|
|
429
|
+
activeFilters.push(`${field.label} (${optionLabel})`);
|
|
253
430
|
} else {
|
|
254
|
-
|
|
431
|
+
// Show count for multiple selections
|
|
432
|
+
activeFilters.push(`${field.label} (${selectValue.selectedValues.length})`);
|
|
255
433
|
}
|
|
256
434
|
}
|
|
257
435
|
break;
|
|
258
436
|
}
|
|
259
437
|
case 'checkbox':
|
|
260
438
|
if (value !== 'indeterminate') {
|
|
261
|
-
const checkboxValue = value === 'checked' ?
|
|
439
|
+
const checkboxValue = value === 'checked' ? getLabel('yes', locale) : getLabel('no', locale);
|
|
262
440
|
activeFilters.push(`${field.label} (${checkboxValue})`);
|
|
263
441
|
}
|
|
264
442
|
break;
|
|
@@ -267,11 +445,6 @@ const QuickFilter = ({ slug, filterList })=>{
|
|
|
267
445
|
return activeFilters;
|
|
268
446
|
};
|
|
269
447
|
const clearAllFilters = ()=>{
|
|
270
|
-
refineListData({
|
|
271
|
-
columns: parseColumns(query.columns),
|
|
272
|
-
where: {},
|
|
273
|
-
page: '1'
|
|
274
|
-
});
|
|
275
448
|
setFilterValues({});
|
|
276
449
|
};
|
|
277
450
|
const memoizedFilterRows = useMemo(()=>{
|
|
@@ -295,7 +468,6 @@ const QuickFilter = ({ slug, filterList })=>{
|
|
|
295
468
|
};
|
|
296
469
|
const activeFiltersDetails = getActiveFiltersDetails();
|
|
297
470
|
const hasActiveFilters = activeFiltersDetails.length > 0;
|
|
298
|
-
const isHebrew = i18n.language === 'he';
|
|
299
471
|
if (!fields.length) return null;
|
|
300
472
|
return /*#__PURE__*/ _jsxs("div", {
|
|
301
473
|
className: "filter-container useTw",
|
|
@@ -321,7 +493,7 @@ const QuickFilter = ({ slug, filterList })=>{
|
|
|
321
493
|
className: "text-sm truncate",
|
|
322
494
|
children: [
|
|
323
495
|
/*#__PURE__*/ _jsx("strong", {
|
|
324
|
-
children:
|
|
496
|
+
children: `${activeFiltersDetails.length === 1 ? getLabel('activeFilterSingular', locale) : getLabel('activeFilterPlural', locale)}: `
|
|
325
497
|
}),
|
|
326
498
|
' ',
|
|
327
499
|
activeFiltersDetails.join(' • ')
|
|
@@ -340,7 +512,7 @@ const QuickFilter = ({ slug, filterList })=>{
|
|
|
340
512
|
]
|
|
341
513
|
}) : /*#__PURE__*/ _jsx("span", {
|
|
342
514
|
className: "text-sm truncate",
|
|
343
|
-
children:
|
|
515
|
+
children: getLabel('quickFilters', locale)
|
|
344
516
|
}),
|
|
345
517
|
showFilters ? /*#__PURE__*/ _jsx(ChevronUp, {
|
|
346
518
|
className: "h-4 w-4"
|