@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.
Files changed (43) hide show
  1. package/README.md +859 -0
  2. package/dist/FilterField.d.ts.map +1 -1
  3. package/dist/FilterField.js +18 -9
  4. package/dist/FilterField.js.map +1 -1
  5. package/dist/QuickFilter.d.ts.map +1 -1
  6. package/dist/QuickFilter.js +227 -55
  7. package/dist/QuickFilter.js.map +1 -1
  8. package/dist/filters/components/checkbox-filter.d.ts +2 -1
  9. package/dist/filters/components/checkbox-filter.d.ts.map +1 -1
  10. package/dist/filters/components/checkbox-filter.js +9 -7
  11. package/dist/filters/components/checkbox-filter.js.map +1 -1
  12. package/dist/filters/components/date-filter.d.ts +2 -1
  13. package/dist/filters/components/date-filter.d.ts.map +1 -1
  14. package/dist/filters/components/date-filter.js +26 -24
  15. package/dist/filters/components/date-filter.js.map +1 -1
  16. package/dist/filters/components/select-filter.d.ts +2 -1
  17. package/dist/filters/components/select-filter.d.ts.map +1 -1
  18. package/dist/filters/components/select-filter.js +20 -18
  19. package/dist/filters/components/select-filter.js.map +1 -1
  20. package/dist/filters/components/small-select-filter.d.ts +2 -1
  21. package/dist/filters/components/small-select-filter.d.ts.map +1 -1
  22. package/dist/filters/components/small-select-filter.js +4 -3
  23. package/dist/filters/components/small-select-filter.js.map +1 -1
  24. package/dist/filters/constants/date-filter-options.d.ts +7 -4
  25. package/dist/filters/constants/date-filter-options.d.ts.map +1 -1
  26. package/dist/filters/constants/date-filter-options.js +25 -70
  27. package/dist/filters/constants/date-filter-options.js.map +1 -1
  28. package/dist/filters/types/filters-type.d.ts.map +1 -1
  29. package/dist/filters/types/filters-type.js.map +1 -1
  30. package/dist/filters/utils/date-helpers.d.ts +4 -3
  31. package/dist/filters/utils/date-helpers.d.ts.map +1 -1
  32. package/dist/filters/utils/date-helpers.js +14 -11
  33. package/dist/filters/utils/date-helpers.js.map +1 -1
  34. package/dist/filters/utils/layout-helpers.d.ts.map +1 -1
  35. package/dist/filters/utils/layout-helpers.js.map +1 -1
  36. package/dist/index.d.ts +6 -1
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js.map +1 -1
  39. package/dist/labels.d.ts +326 -0
  40. package/dist/labels.d.ts.map +1 -0
  41. package/dist/labels.js +192 -0
  42. package/dist/labels.js.map +1 -0
  43. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"FilterField.d.ts","sourceRoot":"","sources":["../src/FilterField.tsx"],"names":[],"mappings":"AACA,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"}
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"}
@@ -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 userLocale = {
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: userLocale,
44
- className: ` w-[${field.width || '250px'}]`
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: userLocale,
57
- className: ` w-[${field.width || '250px'}]`
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: userLocale,
69
- className: ` w-[${field.width || '250px'}]`
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: userLocale,
78
- className: ` w-[${field.width || '250px'}]`
84
+ locale: locale,
85
+ style: {
86
+ width: field.width || '230px'
87
+ }
79
88
  }, field.name);
80
89
  default:
81
90
  return null;
@@ -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 { DateFilter } from './filters/components/date-filter';\nimport { Locale } from './filters/types/filters-type';\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 userLocale = { code: localeLang, direction };\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={userLocale as Locale}\n className={` w-[${field.width || '250px'}]`}\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={userLocale as Locale}\n className={` w-[${field.width || '250px'}]`}\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={userLocale as Locale}\n className={` w-[${field.width || '250px'}]`}\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={userLocale as Locale}\n className={` w-[${field.width || '250px'}]`}\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","userLocale","code","handleDateFilterChange","name","handleSelectFilterChange","handleCheckboxFilterChange","state","type","label","onChange","locale","className","width","options","length","map","option","checkboxLabel"],"mappings":"AAAA;;AACA,OAAOA,SAASC,WAAW,QAAQ,QAAQ;AAC3C,SAASC,cAAc,QAAQ,iBAAiB;AAChD,SAASC,cAAc,EAAEC,YAAY,QAAQ,2BAA2B;AACxE,SAASC,UAAU,QAAQ,mCAAmC;AAE9D,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,aAAa;QAAEC,MAAMN;QAAYI;IAAU;IAEjD,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;gBACVQ,QAAQV;gBACRW,WAAW,CAAC,IAAI,EAAErB,MAAMsB,KAAK,IAAI,QAAQ,CAAC,CAAC;eAHtCtB,MAAMa,IAAI;QAMrB,KAAK;YACH,IAAIb,MAAMuB,OAAO,CAACC,MAAM,IAAI,GAAG;gBAC7B,qBACE,KAAC5B;oBACCsB,OAAOlB,MAAMkB,KAAK;oBAElBK,SAAS,AAACvB,CAAAA,MAAMuB,OAAO,IAAI,EAAE,AAAD,EAAGE,GAAG,CAAC,CAACC,SAAiB,CAAA;4BACnDR,OAAOzB,eAAeiC,OAAOR,KAAK,EAAEd;4BACpCF,OAAOwB,OAAOxB,KAAK;wBACrB,CAAA;oBACAiB,UAAUL;oBACVZ,OAAOC;oBACPiB,QAAQV;oBACRW,WAAW,CAAC,IAAI,EAAErB,MAAMsB,KAAK,IAAI,QAAQ,CAAC,CAAC;mBARtCtB,MAAMa,IAAI;YAWrB;YACA,qBACE,KAAChB;gBACCqB,OAAOlB,MAAMkB,KAAK;gBAElBK,SAAS,AAACvB,CAAAA,MAAMuB,OAAO,IAAI,EAAE,AAAD,EAAGE,GAAG,CAAC,CAACC,SAAiB,CAAA;wBACnDR,OAAOzB,eAAeiC,OAAOR,KAAK,EAAEd;wBACpCF,OAAOwB,OAAOxB,KAAK;oBACrB,CAAA;gBACAiB,UAAUL;gBACVZ,OAAOC;gBACPiB,QAAQV;gBACRW,WAAW,CAAC,IAAI,EAAErB,MAAMsB,KAAK,IAAI,QAAQ,CAAC,CAAC;eARtCtB,MAAMa,IAAI;QAWrB,KAAK;YACH,qBACE,KAACf;gBACCoB,OAAOlB,MAAMkB,KAAK;gBAElBC,UAAUJ;gBACVb,OAAOC;gBACPwB,eAAe;gBACfP,QAAQV;gBACRW,WAAW,CAAC,IAAI,EAAErB,MAAMsB,KAAK,IAAI,QAAQ,CAAC,CAAC;eALtCtB,MAAMa,IAAI;QAQrB;YACE,OAAO;IACX;AACF;AAEA,eAAed,YAAY"}
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":"AAiIA,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,gCA0PA,CAAC;AAEF,eAAe,WAAW,CAAC"}
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"}
@@ -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
- // Helper function to convert UI state to a where query
40
- const buildWhereClause = (values, fieldDefs, isHebrew)=>{
41
- const where = {};
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
- from = new Date(dateValue.customRange.from);
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
- where[fieldName] = dateQuery;
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
- where[fieldName] = {
82
- equals: selectValue.selectedValues[0]
80
+ condition = {
81
+ [fieldName]: {
82
+ equals: selectValue.selectedValues[0]
83
+ }
83
84
  };
84
85
  } else {
85
- where[fieldName] = {
86
- in: selectValue.selectedValues
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
- where[fieldName] = {
97
- equals: true
99
+ condition = {
100
+ [fieldName]: {
101
+ equals: 'true'
102
+ }
98
103
  };
99
104
  } else if (checkboxState === 'unchecked') {
100
- where[fieldName] = {
101
- equals: false
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 where;
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
- if (fields.length === 0) {
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
- const where = buildWhereClause(filterValues, fields, isHebrew);
182
- try {
183
- if (Object.keys(filterValues).length > 0) {
184
- localStorage.setItem(localStorageKey, JSON.stringify(filterValues));
185
- } else {
186
- localStorage.removeItem(localStorageKey);
187
- return;
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
- if (JSON.stringify(where) !== JSON.stringify(query.where)) {
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 isHebrew = i18n.language === 'he';
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
- ...pastDateFilterOptions,
233
- ...futureDateFilterOptions
405
+ ...pastOptions,
406
+ ...futureOptions
234
407
  ];
235
408
  const option = allOptions.find((opt)=>opt.value === dateValue.predefinedValue);
236
- dateDescription = option ? option.label : isHebrew ? 'מותאם אישית' : 'Custom';
409
+ dateDescription = option ? option.label : getLabel('custom', locale);
237
410
  } else if (dateValue.type === 'custom' || dateValue.customRange) {
238
- dateDescription = isHebrew ? 'מותאם אישית' : 'Custom';
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} (${isHebrew ? 'הכל' : 'All'})`);
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
- activeFilters.push(`${field.label} (${count})`);
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' ? isHebrew ? 'כן' : 'Yes' : isHebrew ? 'לא' : 'No';
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: isHebrew ? `${activeFiltersDetails.length === 1 ? 'סינון פעיל בעמודה' : 'סינון פעיל בעמודות'}: ` : `${activeFiltersDetails.length === 1 ? 'Active filter on column' : 'Active filters on columns'}: `
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: isHebrew ? 'סינון מהיר' : 'Quick Filters'
515
+ children: getLabel('quickFilters', locale)
344
516
  }),
345
517
  showFilters ? /*#__PURE__*/ _jsx(ChevronUp, {
346
518
  className: "h-4 w-4"