@firecms/core 3.0.1 → 3.1.0-canary.9e89e98

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 (170) hide show
  1. package/README.md +1 -1
  2. package/dist/components/AIIcon.d.ts +16 -0
  3. package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +7 -1
  4. package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +1 -1
  5. package/dist/components/EntityCollectionTable/EntityCollectionTableProps.d.ts +14 -0
  6. package/dist/components/EntityCollectionTable/PropertyTableCell.d.ts +6 -0
  7. package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +5 -4
  8. package/dist/components/EntityCollectionTable/internal/EntityTableCell.d.ts +6 -0
  9. package/dist/components/EntityCollectionView/Board.d.ts +2 -0
  10. package/dist/components/EntityCollectionView/BoardColumn.d.ts +42 -0
  11. package/dist/components/EntityCollectionView/BoardColumnTitle.d.ts +9 -0
  12. package/dist/components/EntityCollectionView/BoardSortableList.d.ts +14 -0
  13. package/dist/components/EntityCollectionView/EntityBoardCard.d.ts +26 -0
  14. package/dist/components/EntityCollectionView/EntityCard.d.ts +19 -0
  15. package/dist/components/EntityCollectionView/EntityCollectionBoardView.d.ts +20 -0
  16. package/dist/components/EntityCollectionView/EntityCollectionCardView.d.ts +31 -0
  17. package/dist/components/EntityCollectionView/EntityCollectionViewActions.d.ts +2 -2
  18. package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +7 -3
  19. package/dist/components/EntityCollectionView/FiltersDialog.d.ts +14 -0
  20. package/dist/components/EntityCollectionView/ViewModeToggle.d.ts +44 -0
  21. package/dist/components/EntityCollectionView/board_types.d.ts +105 -0
  22. package/dist/components/EntityCollectionView/useBoardDataController.d.ts +60 -0
  23. package/dist/components/SelectableTable/SelectableTable.d.ts +5 -1
  24. package/dist/components/SelectableTable/filters/DateTimeFilterField.d.ts +2 -1
  25. package/dist/components/VirtualTable/VirtualTableCell.d.ts +6 -0
  26. package/dist/components/VirtualTable/VirtualTableHeader.d.ts +2 -0
  27. package/dist/components/VirtualTable/VirtualTableHeaderRow.d.ts +1 -1
  28. package/dist/components/VirtualTable/VirtualTableProps.d.ts +11 -0
  29. package/dist/components/VirtualTable/fields/VirtualTableDateField.d.ts +1 -0
  30. package/dist/components/VirtualTable/types.d.ts +2 -0
  31. package/dist/components/index.d.ts +3 -0
  32. package/dist/contexts/index.d.ts +10 -0
  33. package/dist/core/DrawerNavigationGroup.d.ts +45 -0
  34. package/dist/core/index.d.ts +1 -0
  35. package/dist/form/validation.d.ts +3 -2
  36. package/dist/hooks/useBreadcrumbsController.d.ts +16 -0
  37. package/dist/hooks/useCollapsedGroups.d.ts +4 -1
  38. package/dist/index.es.js +5185 -1561
  39. package/dist/index.es.js.map +1 -1
  40. package/dist/index.umd.js +5179 -1556
  41. package/dist/index.umd.js.map +1 -1
  42. package/dist/preview/PropertyPreviewProps.d.ts +5 -0
  43. package/dist/preview/components/DatePreview.d.ts +13 -3
  44. package/dist/preview/components/ImagePreview.d.ts +5 -1
  45. package/dist/preview/components/StorageThumbnail.d.ts +2 -1
  46. package/dist/preview/components/UrlComponentPreview.d.ts +2 -1
  47. package/dist/preview/property_previews/ArrayOfStorageComponentsPreview.d.ts +1 -1
  48. package/dist/preview/property_previews/ArrayOfStringsPreview.d.ts +1 -1
  49. package/dist/preview/property_previews/SkeletonPropertyComponent.d.ts +1 -1
  50. package/dist/types/collections.d.ts +50 -2
  51. package/dist/types/datasource.d.ts +0 -1
  52. package/dist/types/plugins.d.ts +46 -1
  53. package/dist/types/properties.d.ts +259 -4
  54. package/dist/util/__tests__/conditions.test.d.ts +1 -0
  55. package/dist/util/__tests__/objects.test.d.ts +1 -0
  56. package/dist/util/conditions.d.ts +26 -0
  57. package/dist/util/entities.d.ts +1 -2
  58. package/dist/util/index.d.ts +2 -1
  59. package/dist/util/property_utils.d.ts +2 -1
  60. package/dist/util/resolutions.d.ts +1 -1
  61. package/package.json +10 -7
  62. package/src/app/Scaffold.tsx +14 -15
  63. package/src/components/AIIcon.tsx +39 -0
  64. package/src/components/ArrayContainer.tsx +1 -4
  65. package/src/components/ClearFilterSortButton.tsx +19 -16
  66. package/src/components/ConfirmationDialog.tsx +0 -2
  67. package/src/components/DeleteEntityDialog.tsx +2 -4
  68. package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +74 -41
  69. package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +130 -79
  70. package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +121 -104
  71. package/src/components/EntityCollectionTable/PropertyTableCell.tsx +132 -103
  72. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +20 -42
  73. package/src/components/EntityCollectionTable/internal/EntityTableCell.tsx +90 -49
  74. package/src/components/EntityCollectionView/Board.tsx +324 -0
  75. package/src/components/EntityCollectionView/BoardColumn.tsx +158 -0
  76. package/src/components/EntityCollectionView/BoardColumnTitle.tsx +45 -0
  77. package/src/components/EntityCollectionView/BoardSortableList.tsx +172 -0
  78. package/src/components/EntityCollectionView/EntityBoardCard.tsx +212 -0
  79. package/src/components/EntityCollectionView/EntityCard.tsx +231 -0
  80. package/src/components/EntityCollectionView/EntityCollectionBoardView.tsx +713 -0
  81. package/src/components/EntityCollectionView/EntityCollectionCardView.tsx +244 -0
  82. package/src/components/EntityCollectionView/EntityCollectionView.tsx +490 -203
  83. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +31 -19
  84. package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +84 -15
  85. package/src/components/EntityCollectionView/FiltersDialog.tsx +249 -0
  86. package/src/components/EntityCollectionView/ViewModeToggle.tsx +199 -0
  87. package/src/components/EntityCollectionView/board_types.ts +113 -0
  88. package/src/components/EntityCollectionView/useBoardDataController.tsx +490 -0
  89. package/src/components/ErrorTooltip.tsx +2 -1
  90. package/src/components/HomePage/DefaultHomePage.tsx +47 -10
  91. package/src/components/HomePage/HomePageDnD.tsx +56 -41
  92. package/src/components/HomePage/NavigationCard.tsx +20 -18
  93. package/src/components/HomePage/NavigationGroup.tsx +17 -16
  94. package/src/components/HomePage/RenameGroupDialog.tsx +0 -2
  95. package/src/components/HomePage/SmallNavigationCard.tsx +10 -9
  96. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +3 -10
  97. package/src/components/ReferenceWidget.tsx +2 -4
  98. package/src/components/SelectableTable/SelectableTable.tsx +75 -67
  99. package/src/components/SelectableTable/filters/BooleanFilterField.tsx +7 -6
  100. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +39 -40
  101. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +38 -38
  102. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +49 -58
  103. package/src/components/UnsavedChangesDialog.tsx +0 -2
  104. package/src/components/UserDisplay.tsx +4 -4
  105. package/src/components/VirtualTable/VirtualTable.tsx +170 -19
  106. package/src/components/VirtualTable/VirtualTableCell.tsx +18 -2
  107. package/src/components/VirtualTable/VirtualTableHeader.tsx +20 -11
  108. package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +158 -42
  109. package/src/components/VirtualTable/VirtualTableProps.tsx +14 -1
  110. package/src/components/VirtualTable/VirtualTableRow.tsx +1 -1
  111. package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +3 -0
  112. package/src/components/VirtualTable/fields/VirtualTableSelect.tsx +17 -4
  113. package/src/components/VirtualTable/types.tsx +2 -0
  114. package/src/components/common/useColumnsIds.tsx +95 -3
  115. package/src/components/index.tsx +4 -0
  116. package/src/contexts/BreacrumbsContext.tsx +15 -8
  117. package/src/contexts/index.ts +10 -0
  118. package/src/core/DefaultAppBar.tsx +39 -26
  119. package/src/core/DefaultDrawer.tsx +42 -56
  120. package/src/core/DrawerNavigationGroup.tsx +118 -0
  121. package/src/core/DrawerNavigationItem.tsx +4 -3
  122. package/src/core/EntityEditView.tsx +41 -43
  123. package/src/core/SideDialogs.tsx +4 -2
  124. package/src/core/index.tsx +1 -0
  125. package/src/form/PropertyFieldBinding.tsx +58 -43
  126. package/src/form/components/StorageItemPreview.tsx +2 -1
  127. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +0 -1
  128. package/src/form/field_bindings/DateTimeFieldBinding.tsx +17 -16
  129. package/src/form/field_bindings/KeyValueFieldBinding.tsx +0 -1
  130. package/src/form/field_bindings/MapFieldBinding.tsx +69 -67
  131. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +21 -17
  132. package/src/form/field_bindings/TextFieldBinding.tsx +71 -35
  133. package/src/form/validation.ts +245 -160
  134. package/src/hooks/useBreadcrumbsController.tsx +18 -0
  135. package/src/hooks/useBuildNavigationController.tsx +42 -19
  136. package/src/hooks/useCollapsedGroups.ts +12 -4
  137. package/src/internal/useBuildDataSource.ts +69 -34
  138. package/src/internal/useBuildSideDialogsController.tsx +11 -8
  139. package/src/internal/useBuildSideEntityController.tsx +2 -4
  140. package/src/internal/useRestoreScroll.tsx +26 -14
  141. package/src/preview/PropertyPreview.tsx +40 -32
  142. package/src/preview/PropertyPreviewProps.tsx +6 -0
  143. package/src/preview/components/DatePreview.tsx +72 -4
  144. package/src/preview/components/EmptyValue.tsx +1 -1
  145. package/src/preview/components/ImagePreview.tsx +37 -21
  146. package/src/preview/components/StorageThumbnail.tsx +16 -12
  147. package/src/preview/components/UrlComponentPreview.tsx +28 -25
  148. package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +9 -7
  149. package/src/preview/property_previews/ArrayOfStringsPreview.tsx +11 -9
  150. package/src/preview/property_previews/ArrayPropertyPreview.tsx +26 -24
  151. package/src/preview/property_previews/SkeletonPropertyComponent.tsx +61 -56
  152. package/src/routes/CustomCMSRoute.tsx +1 -0
  153. package/src/routes/FireCMSRoute.tsx +26 -13
  154. package/src/types/collections.ts +57 -3
  155. package/src/types/datasource.ts +54 -56
  156. package/src/types/plugins.tsx +51 -1
  157. package/src/types/properties.ts +347 -27
  158. package/src/util/__tests__/conditions.test.ts +506 -0
  159. package/src/util/__tests__/objects.test.ts +196 -0
  160. package/src/util/callbacks.ts +6 -3
  161. package/src/util/collections.ts +51 -6
  162. package/src/util/conditions.ts +339 -0
  163. package/src/util/entities.ts +28 -29
  164. package/src/util/entity_cache.ts +2 -1
  165. package/src/util/index.ts +2 -1
  166. package/src/util/objects.ts +31 -13
  167. package/src/util/{references.ts → previews.ts} +14 -0
  168. package/src/util/property_utils.tsx +36 -10
  169. package/src/util/resolutions.ts +57 -55
  170. /package/dist/util/{references.d.ts → previews.d.ts} +0 -0
@@ -2,8 +2,19 @@ import React from "react";
2
2
 
3
3
  import { canCreateEntity, canDeleteEntity } from "../../util";
4
4
  import { useAuthController, useCustomizationController, useFireCMSContext, useLargeLayout } from "../../hooks";
5
- import { CollectionActionsProps, EntityCollection, EntityTableController, SelectionController } from "../../types";
6
- import { AddIcon, Button, DeleteIcon, IconButton, Tooltip } from "@firecms/ui";
5
+ import {
6
+ CollectionActionsProps,
7
+ EntityCollection,
8
+ EntityTableController,
9
+ SelectionController
10
+ } from "../../types";
11
+ import {
12
+ AddIcon,
13
+ Button,
14
+ DeleteIcon,
15
+ IconButton,
16
+ Tooltip
17
+ } from "@firecms/ui";
7
18
  import { toArray } from "../../util/arrays";
8
19
  import { ErrorBoundary } from "../ErrorBoundary";
9
20
 
@@ -17,21 +28,21 @@ export type EntityCollectionViewActionsProps<M extends Record<string, any>> = {
17
28
  onMultipleDeleteClick: () => void;
18
29
  selectionController: SelectionController<M>;
19
30
  tableController: EntityTableController<M>;
20
- collectionEntitiesCount: number;
31
+ collectionEntitiesCount?: number;
21
32
  }
22
33
 
23
34
  export function EntityCollectionViewActions<M extends Record<string, any>>({
24
- collection,
25
- relativePath,
26
- parentCollectionIds,
27
- onNewClick,
28
- onMultipleDeleteClick,
29
- selectionEnabled,
30
- path,
31
- selectionController,
32
- tableController,
33
- collectionEntitiesCount
34
- }: EntityCollectionViewActionsProps<M>) {
35
+ collection,
36
+ relativePath,
37
+ parentCollectionIds,
38
+ onNewClick,
39
+ onMultipleDeleteClick,
40
+ selectionEnabled,
41
+ path,
42
+ selectionController,
43
+ tableController,
44
+ collectionEntitiesCount,
45
+ }: EntityCollectionViewActionsProps<M>) {
35
46
 
36
47
  const context = useFireCMSContext();
37
48
 
@@ -49,7 +60,7 @@ export function EntityCollectionViewActions<M extends Record<string, any>>({
49
60
  ? <Button
50
61
  id={`add_entity_${path}`}
51
62
  onClick={onNewClick}
52
- startIcon={<AddIcon/>}
63
+ startIcon={<AddIcon size={"small"} />}
53
64
  variant="filled"
54
65
  color="primary">
55
66
  Add {collection.singularName ?? collection.name}
@@ -60,7 +71,7 @@ export function EntityCollectionViewActions<M extends Record<string, any>>({
60
71
  variant="filled"
61
72
  color="primary"
62
73
  >
63
- <AddIcon/>
74
+ <AddIcon size={"small"} />
64
75
  </Button>);
65
76
 
66
77
  const multipleDeleteEnabled = canDeleteEntity(collection, authController, path, null);
@@ -71,7 +82,7 @@ export function EntityCollectionViewActions<M extends Record<string, any>>({
71
82
  ? <Button
72
83
  variant={"text"}
73
84
  disabled={!(selectedEntities?.length) || !multipleDeleteEnabled}
74
- startIcon={<DeleteIcon size={"small"}/>}
85
+ startIcon={<DeleteIcon size={"small"} />}
75
86
  onClick={onMultipleDeleteClick}
76
87
  color={"primary"}
77
88
  className="lg:w-20"
@@ -83,7 +94,7 @@ export function EntityCollectionViewActions<M extends Record<string, any>>({
83
94
  color={"primary"}
84
95
  disabled={!(selectedEntities?.length) || !multipleDeleteEnabled}
85
96
  onClick={onMultipleDeleteClick}>
86
- <DeleteIcon size={"small"}/>
97
+ <DeleteIcon size={"small"} />
87
98
  </IconButton>;
88
99
  multipleDeleteButton =
89
100
  <Tooltip
@@ -92,6 +103,7 @@ export function EntityCollectionViewActions<M extends Record<string, any>>({
92
103
  </Tooltip>
93
104
  }
94
105
 
106
+
95
107
  const actionProps: CollectionActionsProps = {
96
108
  path,
97
109
  relativePath,
@@ -116,7 +128,7 @@ export function EntityCollectionViewActions<M extends Record<string, any>>({
116
128
  actions.push(...toArray(plugin.collectionView?.CollectionActions)
117
129
  .map((Action, j) => (
118
130
  <ErrorBoundary key={`plugin_actions_${i}_${j}`}>
119
- <Action {...actionProps} {...plugin.collectionView?.collectionActionsProps}/>
131
+ <Action {...actionProps} {...plugin.collectionView?.collectionActionsProps} />
120
132
  </ErrorBoundary>
121
133
  )));
122
134
  }
@@ -1,9 +1,17 @@
1
- import React from "react";
2
- import { useCustomizationController, useFireCMSContext } from "../../hooks";
3
- import { CollectionActionsProps, EntityCollection, EntityTableController, SelectionController } from "../../types";
1
+ import React, { useState } from "react";
2
+ import { useCustomizationController, useFireCMSContext, useLargeLayout } from "../../hooks";
3
+ import {
4
+ CollectionActionsProps,
5
+ EntityCollection,
6
+ EntityTableController,
7
+ ResolvedProperty,
8
+ SelectionController
9
+ } from "../../types";
4
10
  import { toArray } from "../../util/arrays";
5
11
  import { ErrorBoundary } from "../ErrorBoundary";
6
12
  import { ClearFilterSortButton } from "../ClearFilterSortButton";
13
+ import { FiltersDialog } from "./FiltersDialog";
14
+ import { Badge, Button, cls, FilterListIcon, IconButton, Tooltip } from "@firecms/ui";
7
15
 
8
16
  export type EntityCollectionViewStartActionsProps<M extends Record<string, any>> = {
9
17
  collection: EntityCollection<M>;
@@ -12,23 +20,38 @@ export type EntityCollectionViewStartActionsProps<M extends Record<string, any>>
12
20
  parentCollectionIds: string[];
13
21
  selectionController: SelectionController<M>;
14
22
  tableController: EntityTableController<M>;
15
- collectionEntitiesCount: number;
23
+ collectionEntitiesCount?: number;
24
+ /**
25
+ * Resolved properties from the collection for the filters dialog
26
+ */
27
+ resolvedProperties?: Record<string, ResolvedProperty>;
16
28
  }
17
29
 
18
30
  export function EntityCollectionViewStartActions<M extends Record<string, any>>({
19
- collection,
20
- relativePath,
21
- parentCollectionIds,
22
- path,
23
- selectionController,
24
- tableController,
25
- collectionEntitiesCount
26
- }: EntityCollectionViewStartActionsProps<M>) {
31
+ collection,
32
+ relativePath,
33
+ parentCollectionIds,
34
+ path,
35
+ selectionController,
36
+ tableController,
37
+ collectionEntitiesCount,
38
+ resolvedProperties
39
+ }: EntityCollectionViewStartActionsProps<M>) {
27
40
 
28
41
  const context = useFireCMSContext();
29
-
30
42
  const customizationController = useCustomizationController();
31
43
  const plugins = customizationController.plugins ?? [];
44
+ const largeLayout = useLargeLayout();
45
+
46
+ // Filters dialog state
47
+ const [filtersDialogOpen, setFiltersDialogOpen] = useState(false);
48
+
49
+ // Count active filters (excluding force filters)
50
+ const filterValues = tableController.filterValues;
51
+ const forceFilter = collection.forceFilter;
52
+ const activeFilterCount = filterValues
53
+ ? Object.keys(filterValues).filter(key => !forceFilter || !(key in forceFilter)).length
54
+ : 0;
32
55
 
33
56
  const actionProps: CollectionActionsProps = {
34
57
  path,
@@ -40,11 +63,44 @@ export function EntityCollectionViewStartActions<M extends Record<string, any>>(
40
63
  tableController,
41
64
  collectionEntitiesCount
42
65
  };
66
+
67
+ // Filters button
68
+ const filtersButton = resolvedProperties && tableController.setFilterValues && (
69
+ <Tooltip title="Filters"
70
+ key={"filters_tooltip"}>
71
+ <Badge
72
+ color="primary"
73
+ invisible={activeFilterCount === 0}
74
+ >
75
+ {largeLayout ? (
76
+ <Button
77
+ variant="text"
78
+ size="small"
79
+ onClick={() => setFiltersDialogOpen(true)}
80
+ startIcon={<FilterListIcon size="small" />}
81
+ className={cls(activeFilterCount > 0 && "text-primary")}
82
+ >
83
+ Filters{activeFilterCount > 0 ? ` (${activeFilterCount})` : ""}
84
+ </Button>
85
+ ) : (
86
+ <IconButton
87
+ size="small"
88
+ onClick={() => setFiltersDialogOpen(true)}
89
+ className={cls(activeFilterCount > 0 && "text-primary")}
90
+ >
91
+ <FilterListIcon size="small" />
92
+ </IconButton>
93
+ )}
94
+ </Badge>
95
+ </Tooltip>
96
+ );
97
+
43
98
  const actions: React.ReactNode[] = [
99
+ filtersButton,
44
100
  <ClearFilterSortButton
45
101
  key={"clear_filter"}
46
102
  tableController={tableController}
47
- enabled={!collection.forceFilter}/>
103
+ enabled={!collection.forceFilter} />
48
104
  ];
49
105
 
50
106
  if (plugins) {
@@ -53,7 +109,7 @@ export function EntityCollectionViewStartActions<M extends Record<string, any>>(
53
109
  actions.push(...toArray(plugin.collectionView?.CollectionActionsStart)
54
110
  .map((Action, j) => (
55
111
  <ErrorBoundary key={`plugin_actions_${i}_${j}`}>
56
- <Action {...actionProps} {...plugin.collectionView?.collectionActionsStartProps}/>
112
+ <Action {...actionProps} {...plugin.collectionView?.collectionActionsStartProps} />
57
113
  </ErrorBoundary>
58
114
  )));
59
115
  }
@@ -63,6 +119,19 @@ export function EntityCollectionViewStartActions<M extends Record<string, any>>(
63
119
  return (
64
120
  <>
65
121
  {actions}
122
+
123
+ {/* Filters Dialog */}
124
+ {resolvedProperties && tableController.setFilterValues && (
125
+ <FiltersDialog
126
+ open={filtersDialogOpen}
127
+ onOpenChange={setFiltersDialogOpen}
128
+ properties={resolvedProperties}
129
+ filterValues={tableController.filterValues}
130
+ setFilterValues={(filterValues) => tableController.setFilterValues?.(filterValues ?? {})}
131
+ forceFilter={collection.forceFilter}
132
+ />
133
+ )}
66
134
  </>
67
135
  );
68
136
  }
137
+
@@ -0,0 +1,249 @@
1
+ import React, { useCallback, useMemo, useState } from "react";
2
+ import {
3
+ FilterValues,
4
+ ResolvedProperty,
5
+ WhereFilterOp
6
+ } from "../../types";
7
+ import {
8
+ Button,
9
+ cls,
10
+ defaultBorderMixin,
11
+ Dialog,
12
+ DialogActions,
13
+ DialogContent,
14
+ DialogTitle,
15
+ FilterListIcon,
16
+ Typography
17
+ } from "@firecms/ui";
18
+ import { StringNumberFilterField } from "../SelectableTable/filters/StringNumberFilterField";
19
+ import { BooleanFilterField } from "../SelectableTable/filters/BooleanFilterField";
20
+ import { DateTimeFilterField } from "../SelectableTable/filters/DateTimeFilterField";
21
+ import { ReferenceFilterField } from "../SelectableTable/filters/ReferenceFilterField";
22
+ import { VirtualTableWhereFilterOp } from "../VirtualTable";
23
+ import { enumToObjectEntries } from "../../util";
24
+
25
+ export interface FiltersDialogProps {
26
+ open: boolean;
27
+ onOpenChange: (open: boolean) => void;
28
+ properties: Record<string, ResolvedProperty>;
29
+ filterValues: FilterValues<any> | undefined;
30
+ setFilterValues: (filterValues?: FilterValues<any>) => void;
31
+ forceFilter?: FilterValues<any>;
32
+ }
33
+
34
+ /**
35
+ * Dialog that displays all filterable properties from a collection
36
+ * and allows setting filter values for each.
37
+ */
38
+ export function FiltersDialog({
39
+ open,
40
+ onOpenChange,
41
+ properties,
42
+ filterValues,
43
+ setFilterValues,
44
+ forceFilter
45
+ }: FiltersDialogProps) {
46
+ // Local state for filters being edited
47
+ const [localFilters, setLocalFilters] = useState<FilterValues<any>>(filterValues ?? {});
48
+
49
+ // Track hidden state for reference fields (when reference dialog is open)
50
+ const [hiddenFields, setHiddenFields] = useState<Record<string, boolean>>({});
51
+
52
+ // Reset local state when dialog opens
53
+ React.useEffect(() => {
54
+ if (open) {
55
+ setLocalFilters(filterValues ?? {});
56
+ }
57
+ }, [open, filterValues]);
58
+
59
+ // Get list of filterable properties
60
+ const filterableProperties = useMemo(() => {
61
+ return Object.entries(properties).filter(([key, property]) => {
62
+ if (!property) return false;
63
+ // Force filter properties should not be editable
64
+ if (forceFilter && key in forceFilter) return false;
65
+ // Check if property type is filterable
66
+ const baseProperty = property.dataType === "array" ? property.of : property;
67
+ if (!baseProperty) return false;
68
+ return ["string", "number", "boolean", "date", "reference"].includes(baseProperty.dataType);
69
+ });
70
+ }, [properties, forceFilter]);
71
+
72
+ const handleFilterChange = useCallback((propertyKey: string, value?: [VirtualTableWhereFilterOp, any]) => {
73
+ setLocalFilters(prev => {
74
+ const newFilters = { ...prev };
75
+ if (value) {
76
+ newFilters[propertyKey] = value as [WhereFilterOp, any];
77
+ } else {
78
+ delete newFilters[propertyKey];
79
+ }
80
+ return newFilters;
81
+ });
82
+ }, []);
83
+
84
+ const handleApply = useCallback(() => {
85
+ const hasFilters = Object.keys(localFilters).length > 0;
86
+ setFilterValues(hasFilters ? { ...localFilters, ...forceFilter } : (forceFilter || undefined));
87
+ onOpenChange(false);
88
+ }, [localFilters, setFilterValues, forceFilter, onOpenChange]);
89
+
90
+ const handleClearAll = useCallback(() => {
91
+ setLocalFilters({});
92
+ }, []);
93
+
94
+ const setHiddenForField = useCallback((propertyKey: string, hidden: boolean) => {
95
+ setHiddenFields(prev => ({
96
+ ...prev,
97
+ [propertyKey]: hidden
98
+ }));
99
+ }, []);
100
+
101
+ // Check if any reference field's dialog is currently open (should hide this dialog)
102
+ const isAnyFieldHidden = Object.values(hiddenFields).some(hidden => hidden);
103
+ const activeFilterCount = Object.keys(localFilters).length;
104
+
105
+ const renderFilterField = useCallback((propertyKey: string, property: ResolvedProperty) => {
106
+ const isArray = property.dataType === "array";
107
+ const baseProperty: ResolvedProperty = isArray ? property.of : property;
108
+
109
+ if (!baseProperty) return null;
110
+
111
+ const filterValue = localFilters[propertyKey] as [VirtualTableWhereFilterOp, any] | undefined;
112
+ const setValue = (value?: [VirtualTableWhereFilterOp, any]) => handleFilterChange(propertyKey, value);
113
+
114
+ if (baseProperty.dataType === "reference") {
115
+ return (
116
+ <ReferenceFilterField
117
+ value={filterValue}
118
+ setValue={setValue}
119
+ name={propertyKey}
120
+ isArray={isArray}
121
+ path={baseProperty.path}
122
+ title={property.name}
123
+ includeId={baseProperty.includeId}
124
+ previewProperties={baseProperty.previewProperties}
125
+ hidden={hiddenFields[propertyKey] ?? false}
126
+ setHidden={(hidden) => setHiddenForField(propertyKey, hidden)}
127
+ />
128
+ );
129
+ } else if (baseProperty.dataType === "number" || baseProperty.dataType === "string") {
130
+ const enumValues = baseProperty.enumValues ? enumToObjectEntries(baseProperty.enumValues) : undefined;
131
+ return (
132
+ <StringNumberFilterField
133
+ value={filterValue}
134
+ setValue={setValue}
135
+ name={propertyKey}
136
+ dataType={baseProperty.dataType}
137
+ isArray={isArray}
138
+ enumValues={enumValues}
139
+ title={property.name}
140
+ />
141
+ );
142
+ } else if (baseProperty.dataType === "boolean") {
143
+ return (
144
+ <BooleanFilterField
145
+ value={filterValue}
146
+ setValue={setValue}
147
+ name={propertyKey}
148
+ title={property.name}
149
+ />
150
+ );
151
+ } else if (baseProperty.dataType === "date") {
152
+ return (
153
+ <DateTimeFilterField
154
+ value={filterValue}
155
+ setValue={setValue}
156
+ name={propertyKey}
157
+ mode={baseProperty.mode}
158
+ isArray={isArray}
159
+ title={property.name}
160
+ />
161
+ );
162
+ }
163
+
164
+ return null;
165
+ }, [localFilters, handleFilterChange, hiddenFields, setHiddenForField]);
166
+
167
+ return (
168
+ <Dialog
169
+ open={open}
170
+ onOpenChange={onOpenChange}
171
+ maxWidth="3xl"
172
+ fullWidth
173
+ containerClassName={isAnyFieldHidden ? "hidden" : undefined}
174
+ >
175
+ <DialogTitle className="flex items-center gap-2">
176
+ <Typography variant="h6">Filters</Typography>
177
+ {activeFilterCount > 0 && (
178
+ <span className="ml-2 px-2 py-0.5 text-xs rounded-full bg-primary text-white">
179
+ {activeFilterCount}
180
+ </span>
181
+ )}
182
+ </DialogTitle>
183
+
184
+ <DialogContent >
185
+ {filterableProperties.length === 0 ? (
186
+ <Typography color="secondary" className="py-8 text-center">
187
+ No filterable properties available
188
+ </Typography>
189
+ ) : (
190
+ <table className="w-full border-collapse">
191
+ <tbody>
192
+ {filterableProperties.map(([propertyKey, property], index) => {
193
+ const hasFilter = propertyKey in localFilters;
194
+
195
+ return (
196
+ <tr key={propertyKey} className={cls(
197
+ index > 0 && "border-t",
198
+ defaultBorderMixin
199
+ )}>
200
+ {/* Property name on the left */}
201
+ <td className="py-3 pr-4 align-middle w-[160px]">
202
+ <Typography
203
+ variant="body2"
204
+ className={cls(
205
+ "font-medium",
206
+ hasFilter && "text-primary"
207
+ )}
208
+ >
209
+ {property.name || propertyKey}
210
+ </Typography>
211
+ </td>
212
+
213
+ {/* Filter field on the right */}
214
+ <td className="py-3">
215
+ {renderFilterField(propertyKey, property)}
216
+ </td>
217
+ </tr>
218
+ );
219
+ })}
220
+ </tbody>
221
+ </table>
222
+ )}
223
+ </DialogContent>
224
+
225
+ <DialogActions>
226
+ <Button
227
+ variant="text"
228
+ onClick={handleClearAll}
229
+ disabled={activeFilterCount === 0}
230
+ >
231
+ Clear all
232
+ </Button>
233
+ <div className="flex-grow" />
234
+ <Button
235
+ variant="text"
236
+ onClick={() => onOpenChange(false)}
237
+ >
238
+ Cancel
239
+ </Button>
240
+ <Button
241
+ variant="filled"
242
+ onClick={handleApply}
243
+ >
244
+ Apply filters
245
+ </Button>
246
+ </DialogActions>
247
+ </Dialog>
248
+ );
249
+ }