@firecms/core 3.1.0 → 3.2.0-canary.4c3b8f2

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 (191) hide show
  1. package/dist/components/EntityCollectionView/CollectionDataErrorBanner.d.ts +4 -0
  2. package/dist/components/ErrorBoundary.d.ts +3 -1
  3. package/dist/components/HomePage/DefaultHomePage.d.ts +0 -1
  4. package/dist/components/LanguageToggle.d.ts +1 -0
  5. package/dist/components/UnsavedChangesDialog.d.ts +1 -0
  6. package/dist/components/index.d.ts +1 -0
  7. package/dist/core/DrawerNavigationGroup.d.ts +2 -2
  8. package/dist/editor/components/SlashCommandMenu.d.ts +6 -0
  9. package/dist/editor/components/editor-bubble-item.d.ts +8 -0
  10. package/dist/editor/components/editor-bubble.d.ts +8 -0
  11. package/dist/editor/components/image-bubble.d.ts +5 -0
  12. package/dist/editor/components/index.d.ts +16 -0
  13. package/dist/editor/components/table-bubble.d.ts +5 -0
  14. package/dist/editor/editor.d.ts +30 -0
  15. package/dist/editor/extensions/HighlightDecorationExtension.d.ts +24 -0
  16. package/dist/editor/extensions/Image/index.d.ts +6 -0
  17. package/dist/editor/extensions/Image.d.ts +6 -0
  18. package/dist/editor/extensions/TextLoadingDecorationExtension.d.ts +16 -0
  19. package/dist/editor/extensions/clipboard.d.ts +7 -0
  20. package/dist/editor/extensions/custom-keymap.d.ts +1 -0
  21. package/dist/editor/extensions/drag-and-drop.d.ts +9 -0
  22. package/dist/editor/hooks/useProseMirror.d.ts +13 -0
  23. package/dist/editor/hooks/useProseMirrorContext.d.ts +9 -0
  24. package/dist/editor/index.d.ts +2 -0
  25. package/dist/editor/markdown.d.ts +5 -0
  26. package/dist/editor/nodeViews/ImageComponent.d.ts +3 -0
  27. package/dist/editor/nodeViews/ReactNodeView.d.ts +29 -0
  28. package/dist/editor/nodeViews/TaskItemComponent.d.ts +3 -0
  29. package/dist/editor/nodeViews/index.d.ts +6 -0
  30. package/dist/editor/plugins/index.d.ts +2 -0
  31. package/dist/editor/plugins/inputrules.d.ts +6 -0
  32. package/dist/editor/plugins/placeholderPlugin.d.ts +3 -0
  33. package/dist/editor/plugins/slashCommandPlugin.d.ts +12 -0
  34. package/dist/editor/schema.d.ts +2 -0
  35. package/dist/editor/selectors/ai-selector.d.ts +0 -0
  36. package/dist/editor/selectors/color-selector.d.ts +10 -0
  37. package/dist/editor/selectors/link-selector.d.ts +8 -0
  38. package/dist/editor/selectors/node-selector.d.ts +15 -0
  39. package/dist/editor/selectors/text-buttons.d.ts +1 -0
  40. package/dist/editor/types.d.ts +5 -0
  41. package/dist/editor/useProseMirror.d.ts +16 -0
  42. package/dist/editor/utils/prosemirror-utils.d.ts +6 -0
  43. package/dist/editor/utils/remove_classes.d.ts +1 -0
  44. package/dist/editor/utils/useDebouncedCallback.d.ts +1 -0
  45. package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +1 -1
  46. package/dist/hooks/index.d.ts +1 -0
  47. package/dist/hooks/useBuildNavigationController.d.ts +0 -1
  48. package/dist/hooks/useCollapsedGroups.d.ts +3 -3
  49. package/dist/hooks/useTranslation.d.ts +17 -0
  50. package/dist/i18n/FireCMSi18nProvider.d.ts +33 -0
  51. package/dist/index.d.ts +4 -0
  52. package/dist/index.es.js +12898 -2265
  53. package/dist/index.es.js.map +1 -1
  54. package/dist/index.umd.js +12877 -2264
  55. package/dist/index.umd.js.map +1 -1
  56. package/dist/locales/de.d.ts +2 -0
  57. package/dist/locales/en.d.ts +10 -0
  58. package/dist/locales/es.d.ts +10 -0
  59. package/dist/locales/fr.d.ts +2 -0
  60. package/dist/locales/hi.d.ts +2 -0
  61. package/dist/locales/it.d.ts +2 -0
  62. package/dist/locales/pt.d.ts +7 -0
  63. package/dist/types/customization_controller.d.ts +2 -1
  64. package/dist/types/firecms.d.ts +2 -1
  65. package/dist/types/index.d.ts +1 -0
  66. package/dist/types/navigation.d.ts +2 -2
  67. package/dist/types/plugins.d.ts +7 -0
  68. package/dist/types/storage.d.ts +1 -0
  69. package/dist/types/translations.d.ts +646 -0
  70. package/dist/util/useStorageUploadController.d.ts +10 -1
  71. package/package.json +45 -9
  72. package/src/app/Scaffold.tsx +7 -5
  73. package/src/components/AIIcon.tsx +3 -1
  74. package/src/components/ArrayContainer.tsx +6 -4
  75. package/src/components/ClearFilterSortButton.tsx +6 -3
  76. package/src/components/ConfirmationDialog.tsx +4 -2
  77. package/src/components/DeleteEntityDialog.tsx +10 -7
  78. package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +6 -3
  79. package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +3 -1
  80. package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +3 -2
  81. package/src/components/EntityCollectionView/BoardSortableList.tsx +3 -1
  82. package/src/components/EntityCollectionView/CollectionDataErrorBanner.tsx +43 -0
  83. package/src/components/EntityCollectionView/EntityCollectionBoardView.tsx +16 -43
  84. package/src/components/EntityCollectionView/EntityCollectionCardView.tsx +17 -25
  85. package/src/components/EntityCollectionView/EntityCollectionView.tsx +26 -18
  86. package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +4 -3
  87. package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +4 -2
  88. package/src/components/EntityCollectionView/FiltersDialog.tsx +8 -5
  89. package/src/components/EntityCollectionView/ViewModeToggle.tsx +11 -8
  90. package/src/components/EntityView.tsx +3 -2
  91. package/src/components/ErrorBoundary.tsx +27 -15
  92. package/src/components/HomePage/DefaultHomePage.tsx +19 -13
  93. package/src/components/HomePage/HomePageDnD.tsx +3 -1
  94. package/src/components/HomePage/NavigationGroup.tsx +3 -1
  95. package/src/components/HomePage/RenameGroupDialog.tsx +15 -13
  96. package/src/components/LanguageToggle.tsx +66 -0
  97. package/src/components/NotFoundPage.tsx +5 -3
  98. package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +9 -7
  99. package/src/components/ReferenceWidget.tsx +3 -2
  100. package/src/components/SearchIconsView.tsx +3 -1
  101. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +11 -0
  102. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +15 -2
  103. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +11 -0
  104. package/src/components/UnsavedChangesDialog.tsx +6 -4
  105. package/src/components/VirtualTable/VirtualTable.performance.test.tsx +1 -0
  106. package/src/components/VirtualTable/VirtualTableHeader.tsx +12 -10
  107. package/src/components/common/default_entity_actions.tsx +4 -0
  108. package/src/components/common/useDataSourceTableController.tsx +12 -4
  109. package/src/components/index.tsx +1 -0
  110. package/src/core/DefaultAppBar.tsx +14 -10
  111. package/src/core/DefaultDrawer.tsx +8 -2
  112. package/src/core/DrawerNavigationGroup.tsx +5 -3
  113. package/src/core/EntityEditView.tsx +4 -3
  114. package/src/core/EntityEditViewFormActions.tsx +24 -17
  115. package/src/core/EntitySidePanel.tsx +6 -5
  116. package/src/core/FireCMS.tsx +33 -6
  117. package/src/editor/components/SlashCommandMenu.tsx +516 -0
  118. package/src/editor/components/editor-bubble-item.tsx +32 -0
  119. package/src/editor/components/editor-bubble.tsx +118 -0
  120. package/src/editor/components/image-bubble.tsx +156 -0
  121. package/src/editor/components/index.ts +14 -0
  122. package/src/editor/components/table-bubble.tsx +165 -0
  123. package/src/editor/editor.tsx +455 -0
  124. package/src/editor/extensions/HighlightDecorationExtension.ts +114 -0
  125. package/src/editor/extensions/Image/index.ts +133 -0
  126. package/src/editor/extensions/Image.ts +159 -0
  127. package/src/editor/extensions/TextLoadingDecorationExtension.tsx +107 -0
  128. package/src/editor/extensions/clipboard.ts +72 -0
  129. package/src/editor/extensions/custom-keymap.ts +24 -0
  130. package/src/editor/extensions/drag-and-drop.tsx +480 -0
  131. package/src/editor/hooks/useProseMirror.ts +124 -0
  132. package/src/editor/hooks/useProseMirrorContext.ts +15 -0
  133. package/src/editor/index.ts +2 -0
  134. package/src/editor/markdown.ts +172 -0
  135. package/src/editor/nodeViews/ImageComponent.tsx +20 -0
  136. package/src/editor/nodeViews/ReactNodeView.tsx +89 -0
  137. package/src/editor/nodeViews/TaskItemComponent.tsx +29 -0
  138. package/src/editor/nodeViews/index.ts +35 -0
  139. package/src/editor/plugins/index.ts +58 -0
  140. package/src/editor/plugins/inputrules.ts +82 -0
  141. package/src/editor/plugins/placeholderPlugin.ts +55 -0
  142. package/src/editor/plugins/slashCommandPlugin.ts +61 -0
  143. package/src/editor/schema.ts +240 -0
  144. package/src/editor/selectors/ai-selector.tsx +111 -0
  145. package/src/editor/selectors/color-selector.tsx +200 -0
  146. package/src/editor/selectors/link-selector.tsx +118 -0
  147. package/src/editor/selectors/node-selector.tsx +157 -0
  148. package/src/editor/selectors/text-buttons.tsx +86 -0
  149. package/src/editor/types.ts +6 -0
  150. package/src/editor/useProseMirror.ts +126 -0
  151. package/src/editor/utils/prosemirror-utils.ts +108 -0
  152. package/src/editor/utils/remove_classes.ts +17 -0
  153. package/src/editor/utils/useDebouncedCallback.ts +25 -0
  154. package/src/form/EntityForm.tsx +16 -3
  155. package/src/form/EntityFormActions.tsx +19 -12
  156. package/src/form/PropertyFieldBinding.tsx +3 -2
  157. package/src/form/components/LocalChangesMenu.tsx +13 -13
  158. package/src/form/components/StorageItemPreview.tsx +3 -2
  159. package/src/form/components/StorageUploadProgress.tsx +18 -3
  160. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +4 -4
  161. package/src/form/field_bindings/BlockFieldBinding.tsx +5 -2
  162. package/src/form/field_bindings/KeyValueFieldBinding.tsx +23 -18
  163. package/src/form/field_bindings/MapFieldBinding.tsx +4 -3
  164. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +33 -19
  165. package/src/form/field_bindings/RepeatFieldBinding.tsx +3 -1
  166. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +4 -3
  167. package/src/hooks/index.tsx +1 -0
  168. package/src/hooks/useBuildNavigationController.tsx +45 -18
  169. package/src/hooks/useCollapsedGroups.ts +7 -6
  170. package/src/hooks/useTranslation.ts +31 -0
  171. package/src/i18n/FireCMSi18nProvider.tsx +160 -0
  172. package/src/index.ts +4 -0
  173. package/src/internal/useBuildSideEntityController.tsx +22 -20
  174. package/src/locales/de.ts +691 -0
  175. package/src/locales/en.ts +703 -0
  176. package/src/locales/es.ts +703 -0
  177. package/src/locales/fr.ts +691 -0
  178. package/src/locales/hi.ts +691 -0
  179. package/src/locales/it.ts +691 -0
  180. package/src/locales/pt.ts +700 -0
  181. package/src/preview/components/UrlComponentPreview.tsx +4 -2
  182. package/src/preview/components/UserPreview.tsx +3 -1
  183. package/src/types/customization_controller.tsx +2 -1
  184. package/src/types/firecms.tsx +2 -1
  185. package/src/types/index.ts +1 -0
  186. package/src/types/navigation.ts +2 -2
  187. package/src/types/plugins.tsx +8 -0
  188. package/src/types/properties.ts +1 -0
  189. package/src/types/storage.ts +2 -1
  190. package/src/types/translations.ts +725 -0
  191. package/src/util/useStorageUploadController.tsx +23 -29
@@ -20,9 +20,6 @@ import {
20
20
  DialogActions,
21
21
  DialogContent,
22
22
  getColorSchemeForSeed,
23
- IconButton,
24
- RefreshIcon,
25
- Tooltip,
26
23
  Typography
27
24
  } from "@firecms/ui";
28
25
  import { getPropertyInPath, resolveCollection, resolveEnumValues } from "../../util";
@@ -38,6 +35,8 @@ import { useAnalyticsController } from "../../hooks/useAnalyticsController";
38
35
  import { SaveEntityProps } from "../../types/datasource";
39
36
  import { setIn } from "@firecms/formex";
40
37
  import { useBoardDataController } from "./useBoardDataController";
38
+ import { CollectionDataErrorBanner } from "./CollectionDataErrorBanner";
39
+ import { useTranslation } from "../../hooks/useTranslation";
41
40
 
42
41
  export type EntityCollectionBoardViewProps<M extends Record<string, any> = any> = {
43
42
  collection: EntityCollection<M>;
@@ -77,6 +76,7 @@ export function EntityCollectionBoardView<M extends Record<string, any> = any>({
77
76
  const sideEntityController = useSideEntityController();
78
77
  const analyticsController = useAnalyticsController();
79
78
  const plugins = customizationController.plugins ?? [];
79
+ const { t } = useTranslation();
80
80
 
81
81
  // State for backfill dialog
82
82
  const [showBackfillDialog, setShowBackfillDialog] = useState(false);
@@ -579,19 +579,16 @@ export function EntityCollectionBoardView<M extends Record<string, any> = any>({
579
579
 
580
580
  // Check for loading error
581
581
  const hasError = Boolean(dataLoadingError);
582
- const errorMessage = dataLoadingError?.message || "";
583
- const indexUrl = errorMessage.match(/https:\/\/console\.firebase\.google\.com[^\s]+/)?.[0];
584
582
 
585
583
  // Error: no enum properties available for Kanban columns
586
584
  if (!columnProperty || enumColumns.length === 0) {
587
585
  return (
588
586
  <div className="flex-1 flex flex-col items-center justify-center p-8 gap-4">
589
587
  <Typography variant="h6">
590
- Kanban view is not available
588
+ {t("kanban_view_not_available")}
591
589
  </Typography>
592
590
  <Typography variant="body2" color="secondary" className="text-center max-w-md">
593
- Kanban view requires a string property with enum values to group entities into columns.
594
- Please add an enum property to your collection schema to use this view.
591
+ {t("kanban_view_requires_enum")}
595
592
  </Typography>
596
593
  {KanbanSetupComponent && (
597
594
  <KanbanSetupComponent
@@ -612,7 +609,7 @@ export function EntityCollectionBoardView<M extends Record<string, any> = any>({
612
609
  return (
613
610
  <div className="flex-1 flex items-center justify-center p-8">
614
611
  <Typography variant="label" color="secondary">
615
- No enum values configured for property "{columnProperty}"
612
+ {t("no_enum_values_configured", { property: columnProperty })}
616
613
  </Typography>
617
614
  </div>
618
615
  );
@@ -622,33 +619,10 @@ export function EntityCollectionBoardView<M extends Record<string, any> = any>({
622
619
  <div className="flex-1 flex flex-col overflow-hidden">
623
620
  {/* Error banner - only show when no data loaded */}
624
621
  {hasError && allEntities.length === 0 && (
625
- <div
626
- className="flex items-center gap-4 px-4 py-3 bg-red-50 dark:bg-red-900/20 border-b border-red-200 dark:border-red-800">
627
- <Typography variant="body2" className="text-red-700 dark:text-red-300 flex-1">
628
- <strong>Error:</strong>{" "}
629
- {indexUrl
630
- ? "A Firestore index is required for this query."
631
- : errorMessage}
632
- </Typography>
633
- <Tooltip title="Refresh data">
634
- <IconButton
635
- size="small"
636
- onClick={() => boardDataController.refreshAll()}
637
- >
638
- <RefreshIcon size="small" />
639
- </IconButton>
640
- </Tooltip>
641
- {indexUrl && (
642
- <Button
643
- size="small"
644
- variant="outlined"
645
- color="error"
646
- onClick={() => window.open(indexUrl, "_blank")}
647
- >
648
- Create Index
649
- </Button>
650
- )}
651
- </div>
622
+ <CollectionDataErrorBanner
623
+ error={dataLoadingError}
624
+ onRetry={() => boardDataController.refreshAll()}
625
+ />
652
626
  )}
653
627
 
654
628
  {/* Backfill info bar - non-blocking */}
@@ -656,14 +630,14 @@ export function EntityCollectionBoardView<M extends Record<string, any> = any>({
656
630
  <div
657
631
  className="flex items-center justify-between gap-4 px-4 py-2 bg-amber-50 dark:bg-amber-900/20 border-b border-amber-200 dark:border-amber-800">
658
632
  <Typography variant="body2" color="secondary">
659
- Some items don't have order values. Initialize to enable drag-and-drop reordering.
633
+ {t("items_need_backfill")}
660
634
  </Typography>
661
635
  <Button
662
636
  size="small"
663
637
  variant="text"
664
638
  onClick={() => setShowBackfillDialog(true)}
665
639
  >
666
- Initialize Order
640
+ {t("initialize")}
667
641
  </Button>
668
642
  </div>
669
643
  )}
@@ -713,18 +687,17 @@ export function EntityCollectionBoardView<M extends Record<string, any> = any>({
713
687
  {/* Backfill dialog */}
714
688
  <Dialog open={showBackfillDialog} onOpenChange={setShowBackfillDialog}>
715
689
  <DialogContent>
716
- <Typography variant="h6" className="mb-4">Initialize Kanban Order</Typography>
690
+ <Typography variant="h6" className="mb-4">{t("initialize_kanban_order")}</Typography>
717
691
  <Typography variant="body2">
718
- This will assign sequential order values to all items that don't have one.
719
- Items will maintain their current order within each column.
692
+ {t("initialize_kanban_order_desc")}
720
693
  </Typography>
721
694
  </DialogContent>
722
695
  <DialogActions>
723
696
  <Button variant="text" onClick={() => setShowBackfillDialog(false)} disabled={backfillLoading}>
724
- Cancel
697
+ {t("cancel")}
725
698
  </Button>
726
699
  <Button onClick={handleBackfill} disabled={backfillLoading}>
727
- {backfillLoading ? <CircularProgress size="smallest" /> : "Initialize"}
700
+ {backfillLoading ? <CircularProgress size="smallest" /> : t("initialize")}
728
701
  </Button>
729
702
  </DialogActions>
730
703
  </Dialog>
@@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useRef } from "react";
2
2
  import { CollectionSize, Entity, EntityCollection, EntityTableController, SelectionController } from "../../types";
3
3
  import { EntityCard } from "./EntityCard";
4
4
  import { CircularProgress, cls, Typography } from "@firecms/ui";
5
- import { useAuthController, useCustomizationController } from "../../hooks";
5
+ import { useAuthController, useCustomizationController, useTranslation } from "../../hooks";
6
6
 
7
7
  export type EntityCollectionCardViewProps<M extends Record<string, any> = any> = {
8
8
  collection: EntityCollection<M>;
@@ -61,17 +61,18 @@ function getGridColumnsClass(size: CollectionSize): string {
61
61
  * Alternative to the EntityCollectionTable for visual browsing.
62
62
  */
63
63
  export function EntityCollectionCardView<M extends Record<string, any> = any>({
64
- collection,
65
- tableController,
66
- onEntityClick,
67
- selectionController,
68
- selectionEnabled = true,
69
- highlightedEntities,
70
- emptyComponent,
71
- onScroll,
72
- initialScroll,
73
- size = "m"
74
- }: EntityCollectionCardViewProps<M>) {
64
+ collection,
65
+ tableController,
66
+ onEntityClick,
67
+ selectionController,
68
+ selectionEnabled = true,
69
+ highlightedEntities,
70
+ emptyComponent,
71
+ onScroll,
72
+ initialScroll,
73
+ size = "m"
74
+ }: EntityCollectionCardViewProps<M>) {
75
+ const { t } = useTranslation();
75
76
  const authController = useAuthController();
76
77
  const customizationController = useCustomizationController();
77
78
 
@@ -178,23 +179,14 @@ export function EntityCollectionCardView<M extends Record<string, any> = any>({
178
179
  <div className="flex-1 flex items-center justify-center p-8">
179
180
  {emptyComponent ?? (
180
181
  <Typography variant="label" color="secondary">
181
- No entries found
182
+ {t("no_entries_found")}
182
183
  </Typography>
183
184
  )}
184
185
  </div>
185
186
  );
186
187
  }
187
188
 
188
- // Show error state
189
- if (dataLoadingError) {
190
- return (
191
- <div className="flex-1 flex items-center justify-center p-8">
192
- <Typography className="text-red-500">
193
- Error loading data: {dataLoadingError.message}
194
- </Typography>
195
- </div>
196
- );
197
- }
189
+
198
190
 
199
191
  const gridColumnsClass = getGridColumnsClass(size);
200
192
 
@@ -230,11 +222,11 @@ export function EntityCollectionCardView<M extends Record<string, any> = any>({
230
222
  className="flex items-center justify-center py-8"
231
223
  >
232
224
  {dataLoading && (
233
- <CircularProgress size="small"/>
225
+ <CircularProgress size="small" />
234
226
  )}
235
227
  {!dataLoading && noMoreToLoad && data.length > 0 && (
236
228
  <Typography variant="caption" color="secondary">
237
- All {data.length} entries loaded
229
+ {t("all_entries_loaded", { count: data.length.toString() })}
238
230
  </Typography>
239
231
  )}
240
232
  </div>
@@ -45,13 +45,15 @@ import {
45
45
  useFireCMSContext,
46
46
  useLargeLayout,
47
47
  useNavigationController,
48
- useSideEntityController
48
+ useSideEntityController,
49
+ useTranslation
49
50
  } from "../../hooks";
50
51
  import { useBreadcrumbsController } from "../../hooks/useBreadcrumbsController";
51
52
  import { useUserConfigurationPersistence } from "../../hooks/useUserConfigurationPersistence";
52
53
  import { EntityCollectionViewActions } from "./EntityCollectionViewActions";
53
54
  import { EntityCollectionCardView } from "./EntityCollectionCardView";
54
55
  import { EntityCollectionBoardView } from "./EntityCollectionBoardView";
56
+ import { CollectionDataErrorBanner } from "./CollectionDataErrorBanner";
55
57
  import { ViewModeToggle, KanbanPropertyOption } from "./ViewModeToggle";
56
58
  import {
57
59
  AddIcon,
@@ -159,6 +161,7 @@ export const EntityCollectionView = React.memo(
159
161
  ) {
160
162
 
161
163
  const context = useFireCMSContext();
164
+ const { t } = useTranslation();
162
165
  const navigation = useNavigationController();
163
166
  const breadcrumbs = useBreadcrumbsController();
164
167
  const fullPath = fullPathProp ?? collectionProp.path;
@@ -695,11 +698,13 @@ export const EntityCollectionView = React.memo(
695
698
  customEntityActions?: EntityAction[]
696
699
  }): EntityAction[] => {
697
700
  const deleteEnabled = entity ? canDeleteEntity(collection, authController, fullPath, entity) : true;
698
- const actions: EntityAction[] = [editEntityAction];
701
+ const actions: EntityAction[] = [
702
+ { ...editEntityAction, name: t("edit") }
703
+ ];
699
704
  if (createEnabled)
700
- actions.push(copyEntityAction);
705
+ actions.push({ ...copyEntityAction, name: t("copy") });
701
706
  if (deleteEnabled)
702
- actions.push(deleteEntityAction);
707
+ actions.push({ ...deleteEntityAction, name: t("delete") });
703
708
  if (customEntityActions)
704
709
  return mergeEntityActions(actions, customEntityActions);
705
710
  return actions;
@@ -899,9 +904,11 @@ export const EntityCollectionView = React.memo(
899
904
  />
900
905
 
901
906
  {/* View content - only the view-specific content changes */}
902
- {tableController.dataLoadingError && pluginErrorView
903
- ? pluginErrorView
904
- : viewMode === "kanban" && enabledViews.includes("kanban") ? (
907
+ {tableController.dataLoadingError && pluginErrorView}
908
+ {tableController.dataLoadingError && !pluginErrorView && (
909
+ <CollectionDataErrorBanner error={tableController.dataLoadingError} />
910
+ )}
911
+ {viewMode === "kanban" && enabledViews.includes("kanban") ? (
905
912
  <EntityCollectionBoardView
906
913
  key={`kanban-view-${fullPath}-${selectedKanbanProperty}`}
907
914
  collection={collection}
@@ -916,16 +923,16 @@ export const EntityCollectionView = React.memo(
916
923
  deletedEntities={deletedEntities}
917
924
  emptyComponent={canCreateEntities && tableController.filterValues === undefined && tableController.sortBy === undefined
918
925
  ? <div className="flex flex-col items-center justify-center">
919
- <Typography variant={"subtitle2"}>So empty...</Typography>
926
+ <Typography variant={"subtitle2"}>{t("so_empty")}</Typography>
920
927
  <Button
921
928
  onClick={onNewClick}
922
929
  className="mt-4"
923
930
  >
924
931
  <AddIcon />
925
- Create your first entry
932
+ {t("create_your_first_entry")}
926
933
  </Button>
927
934
  </div>
928
- : <Typography variant={"label"}>No results with the applied filter/sort</Typography>
935
+ : <Typography variant={"label"}>{t("no_results_filter_sort")}</Typography>
929
936
  }
930
937
  />
931
938
  ) : viewMode === "cards" ? (
@@ -942,16 +949,16 @@ export const EntityCollectionView = React.memo(
942
949
  size={cardSize}
943
950
  emptyComponent={canCreateEntities && tableController.filterValues === undefined && tableController.sortBy === undefined
944
951
  ? <div className="flex flex-col items-center justify-center">
945
- <Typography variant={"subtitle2"}>So empty...</Typography>
952
+ <Typography variant={"subtitle2"}>{t("so_empty")}</Typography>
946
953
  <Button
947
954
  onClick={onNewClick}
948
955
  className="mt-4"
949
956
  >
950
957
  <AddIcon />
951
- Create your first entry
958
+ {t("create_your_first_entry")}
952
959
  </Button>
953
960
  </div>
954
- : <Typography variant={"label"}>No results with the applied filter/sort</Typography>
961
+ : <Typography variant={"label"}>{t("no_results_filter_sort")}</Typography>
955
962
  }
956
963
  />
957
964
  ) : (
@@ -980,16 +987,16 @@ export const EntityCollectionView = React.memo(
980
987
  textSearchEnabled={textSearchEnabled}
981
988
  emptyComponent={canCreateEntities && tableController.filterValues === undefined && tableController.sortBy === undefined
982
989
  ? <div className="flex flex-col items-center justify-center">
983
- <Typography variant={"subtitle2"}>So empty...</Typography>
990
+ <Typography variant={"subtitle2"}>{t("so_empty")}</Typography>
984
991
  <Button
985
992
  onClick={onNewClick}
986
993
  className="mt-4"
987
994
  >
988
995
  <AddIcon />
989
- Create your first entry
996
+ {t("create_your_first_entry")}
990
997
  </Button>
991
998
  </div>
992
- : <Typography variant={"label"}>No results with the applied filter/sort</Typography>
999
+ : <Typography variant={"label"}>{t("no_results_filter_sort")}</Typography>
993
1000
  }
994
1001
  hoverRow={hoverRow}
995
1002
  inlineEditing={checkInlineEditing()}
@@ -1160,11 +1167,12 @@ function EntityIdHeaderWidget({
1160
1167
  const [searchString, setSearchString] = React.useState("");
1161
1168
  const [recentIds, setRecentIds] = React.useState<string[]>(getRecentIds(collection.id));
1162
1169
  const sideEntityController = useSideEntityController();
1170
+ const { t } = useTranslation();
1163
1171
 
1164
1172
  const openEntityMode = collection?.openEntityMode ?? DEFAULT_ENTITY_OPEN_MODE;
1165
1173
 
1166
1174
  return (
1167
- <Tooltip title={!openPopup ? "Find by ID" : undefined} asChild={false}>
1175
+ <Tooltip title={!openPopup ? t("find_by_id") : undefined} asChild={false}>
1168
1176
  <Popover
1169
1177
  open={openPopup}
1170
1178
  onOpenChange={setOpenPopup}
@@ -1200,7 +1208,7 @@ function EntityIdHeaderWidget({
1200
1208
  <div className="flex p-2 w-full gap-2">
1201
1209
  <input
1202
1210
  autoFocus={openPopup}
1203
- placeholder={"Find entity by ID"}
1211
+ placeholder={t("find_entity_by_id")}
1204
1212
  // size={"small"}
1205
1213
  onChange={(e) => {
1206
1214
  setSearchString(e.target.value);
@@ -17,6 +17,7 @@ import {
17
17
  } from "@firecms/ui";
18
18
  import { toArray } from "../../util/arrays";
19
19
  import { ErrorBoundary } from "../ErrorBoundary";
20
+ import { useTranslation } from "../../hooks/useTranslation";
20
21
 
21
22
  export type EntityCollectionViewActionsProps<M extends Record<string, any>> = {
22
23
  collection: EntityCollection<M>;
@@ -45,9 +46,9 @@ export function EntityCollectionViewActions<M extends Record<string, any>>({
45
46
  }: EntityCollectionViewActionsProps<M>) {
46
47
 
47
48
  const context = useFireCMSContext();
48
-
49
49
  const customizationController = useCustomizationController();
50
50
  const plugins = customizationController.plugins ?? [];
51
+ const { t } = useTranslation();
51
52
 
52
53
  const authController = useAuthController();
53
54
 
@@ -63,7 +64,7 @@ export function EntityCollectionViewActions<M extends Record<string, any>>({
63
64
  startIcon={<AddIcon size={"small"} />}
64
65
  variant="filled"
65
66
  color="primary">
66
- Add {collection.singularName ?? collection.name}
67
+ {t("add")} {collection.singularName ?? collection.name}
67
68
  </Button>
68
69
  : <Button
69
70
  id={`add_entity_${path}`}
@@ -98,7 +99,7 @@ export function EntityCollectionViewActions<M extends Record<string, any>>({
98
99
  </IconButton>;
99
100
  multipleDeleteButton =
100
101
  <Tooltip
101
- title={multipleDeleteEnabled ? "Delete" : "You have selected at least one entity you cannot delete"}>
102
+ title={multipleDeleteEnabled ? t("delete_selected") : t("cannot_delete_selected")}>
102
103
  {button}
103
104
  </Tooltip>
104
105
  }
@@ -12,6 +12,7 @@ import { ErrorBoundary } from "../ErrorBoundary";
12
12
  import { ClearFilterSortButton } from "../ClearFilterSortButton";
13
13
  import { FiltersDialog } from "./FiltersDialog";
14
14
  import { Badge, Button, cls, FilterListIcon, IconButton, Tooltip } from "@firecms/ui";
15
+ import { useTranslation } from "../../hooks/useTranslation";
15
16
 
16
17
  export type EntityCollectionViewStartActionsProps<M extends Record<string, any>> = {
17
18
  collection: EntityCollection<M>;
@@ -42,6 +43,7 @@ export function EntityCollectionViewStartActions<M extends Record<string, any>>(
42
43
  const customizationController = useCustomizationController();
43
44
  const plugins = customizationController.plugins ?? [];
44
45
  const largeLayout = useLargeLayout();
46
+ const { t } = useTranslation();
45
47
 
46
48
  // Filters dialog state
47
49
  const [filtersDialogOpen, setFiltersDialogOpen] = useState(false);
@@ -66,7 +68,7 @@ export function EntityCollectionViewStartActions<M extends Record<string, any>>(
66
68
 
67
69
  // Filters button
68
70
  const filtersButton = resolvedProperties && tableController.setFilterValues && (
69
- <Tooltip title="Filters"
71
+ <Tooltip title={t("filters")}
70
72
  key={"filters_tooltip"}>
71
73
  <Badge
72
74
  color="primary"
@@ -80,7 +82,7 @@ export function EntityCollectionViewStartActions<M extends Record<string, any>>(
80
82
  startIcon={<FilterListIcon size="small" />}
81
83
  className={cls(activeFilterCount > 0 && "text-primary")}
82
84
  >
83
- Filters{activeFilterCount > 0 ? ` (${activeFilterCount})` : ""}
85
+ {t("filters")}{activeFilterCount > 0 ? ` (${activeFilterCount})` : ""}
84
86
  </Button>
85
87
  ) : (
86
88
  <IconButton
@@ -15,6 +15,7 @@ import {
15
15
  FilterListIcon,
16
16
  Typography
17
17
  } from "@firecms/ui";
18
+ import { useTranslation } from "../../hooks/useTranslation";
18
19
  import { StringNumberFilterField } from "../SelectableTable/filters/StringNumberFilterField";
19
20
  import { BooleanFilterField } from "../SelectableTable/filters/BooleanFilterField";
20
21
  import { DateTimeFilterField } from "../SelectableTable/filters/DateTimeFilterField";
@@ -43,6 +44,8 @@ export function FiltersDialog({
43
44
  setFilterValues,
44
45
  forceFilter
45
46
  }: FiltersDialogProps) {
47
+ const { t } = useTranslation();
48
+
46
49
  // Local state for filters being edited
47
50
  const [localFilters, setLocalFilters] = useState<FilterValues<any>>(filterValues ?? {});
48
51
 
@@ -173,7 +176,7 @@ export function FiltersDialog({
173
176
  containerClassName={isAnyFieldHidden ? "hidden" : undefined}
174
177
  >
175
178
  <DialogTitle className="flex items-center gap-2">
176
- <Typography variant="h6">Filters</Typography>
179
+ <Typography variant="h6">{t("filters")}</Typography>
177
180
  {activeFilterCount > 0 && (
178
181
  <span className="ml-2 px-2 py-0.5 text-xs rounded-full bg-primary text-white">
179
182
  {activeFilterCount}
@@ -184,7 +187,7 @@ export function FiltersDialog({
184
187
  <DialogContent >
185
188
  {filterableProperties.length === 0 ? (
186
189
  <Typography color="secondary" className="py-8 text-center">
187
- No filterable properties available
190
+ {t("no_filterable_properties")}
188
191
  </Typography>
189
192
  ) : (
190
193
  <table className="w-full border-collapse">
@@ -228,20 +231,20 @@ export function FiltersDialog({
228
231
  onClick={handleClearAll}
229
232
  disabled={activeFilterCount === 0}
230
233
  >
231
- Clear all
234
+ {t("clear")}
232
235
  </Button>
233
236
  <div className="flex-grow" />
234
237
  <Button
235
238
  variant="text"
236
239
  onClick={() => onOpenChange(false)}
237
240
  >
238
- Cancel
241
+ {t("cancel")}
239
242
  </Button>
240
243
  <Button
241
244
  variant="filled"
242
245
  onClick={handleApply}
243
246
  >
244
- Apply filters
247
+ {t("apply_filters")}
245
248
  </Button>
246
249
  </DialogActions>
247
250
  </Dialog>
@@ -12,6 +12,7 @@ import {
12
12
  ViewColumnIcon,
13
13
  ViewKanbanIcon
14
14
  } from "@firecms/ui";
15
+ import { useTranslation } from "../../hooks/useTranslation";
15
16
 
16
17
  export type KanbanPropertyOption = {
17
18
  key: string;
@@ -72,6 +73,8 @@ export function ViewModeToggle({
72
73
  onKanbanPropertyChange
73
74
  }: ViewModeToggleProps) {
74
75
 
76
+ const { t } = useTranslation();
77
+
75
78
  if (!onViewModeChange) {
76
79
  return null;
77
80
  }
@@ -84,9 +87,9 @@ export function ViewModeToggle({
84
87
  };
85
88
 
86
89
  const getViewModeName = () => {
87
- if (viewMode === "kanban") return "Board";
88
- if (viewMode === "cards") return "Cards";
89
- return "List";
90
+ if (viewMode === "kanban") return t("board");
91
+ if (viewMode === "cards") return t("cards");
92
+ return t("list");
90
93
  };
91
94
 
92
95
  const showSizeSelector = size && onSizeChanged && (viewMode === "table" || viewMode === "cards");
@@ -100,17 +103,17 @@ export function ViewModeToggle({
100
103
  const allOptions: ToggleButtonOption<ViewMode>[] = [
101
104
  {
102
105
  value: "table",
103
- label: "List",
106
+ label: t("list"),
104
107
  icon: <ListIcon size="small" />
105
108
  },
106
109
  {
107
110
  value: "cards",
108
- label: "Cards",
111
+ label: t("cards"),
109
112
  icon: <AppsIcon size="small" />
110
113
  },
111
114
  {
112
115
  value: "kanban",
113
- label: "Board",
116
+ label: t("board"),
114
117
  icon: <ViewKanbanIcon size="small" />
115
118
  }
116
119
  ];
@@ -150,7 +153,7 @@ export function ViewModeToggle({
150
153
  <div className="flex flex-row items-center justify-between gap-2">
151
154
  <div className="flex items-center gap-2 text-sm text-surface-600 dark:text-surface-300">
152
155
  <ViewColumnIcon size="small" />
153
- <span>Size</span>
156
+ <span>{t("size_label")}</span>
154
157
  </div>
155
158
  <Select
156
159
  value={size}
@@ -173,7 +176,7 @@ export function ViewModeToggle({
173
176
  <div className="flex flex-row items-center justify-between gap-2">
174
177
  <div className="flex items-center gap-2 text-sm text-surface-600 dark:text-surface-300">
175
178
  <ViewKanbanIcon size="small" />
176
- <span>Group by</span>
179
+ <span>{t("group_by")}</span>
177
180
  </div>
178
181
  <Select
179
182
  value={selectedKanbanProperty}
@@ -4,7 +4,7 @@ import { resolveCollection } from "../util";
4
4
  import { cls, defaultBorderMixin, IconButton, OpenInNewIcon, Typography } from "@firecms/ui";
5
5
  import { CustomizationController } from "../types/customization_controller";
6
6
  import { useCustomizationController } from "../hooks/useCustomizationController";
7
- import { useAuthController } from "../hooks";
7
+ import { useAuthController, useTranslation } from "../hooks";
8
8
  import { PropertyCollectionView } from "./PropertyCollectionView";
9
9
 
10
10
  /**
@@ -26,6 +26,7 @@ export function EntityView<M extends Record<string, any>>(
26
26
  }: EntityViewProps<M>) {
27
27
 
28
28
  const authController = useAuthController();
29
+ const { t } = useTranslation();
29
30
  const customizationController: CustomizationController = useCustomizationController();
30
31
  const resolvedCollection: ResolvedEntityCollection<M> = useMemo(() => resolveCollection<M>({
31
32
  collection,
@@ -48,7 +49,7 @@ export function EntityView<M extends Record<string, any>>(
48
49
  color={"secondary"}
49
50
  component={"span"}
50
51
  className="break-words">
51
- Id
52
+ {t("id")}
52
53
  </Typography>
53
54
  </div>
54
55
  <div className="col-span-8">
@@ -1,18 +1,20 @@
1
1
  import React, { ErrorInfo, PropsWithChildren } from "react";
2
+ import { useTranslation } from "../hooks/useTranslation";
2
3
 
3
4
  import { ErrorIcon, Typography } from "@firecms/ui";
4
5
 
5
6
  export class ErrorBoundary extends React.Component<PropsWithChildren<Record<string, unknown>>, {
6
- error: Error | null
7
+ hasError: boolean,
8
+ error?: Error
7
9
  }> {
8
10
  constructor(props: any) {
9
11
  super(props);
10
- this.state = { error: null };
12
+ this.state = { hasError: false };
11
13
  }
12
14
 
13
15
  // eslint-disable-next-line n/handle-callback-err
14
16
  static getDerivedStateFromError(error: Error) {
15
- return { error };
17
+ return { hasError: true, error };
16
18
  }
17
19
 
18
20
  componentDidCatch(error: Error, errorInfo: ErrorInfo) {
@@ -21,20 +23,30 @@ export class ErrorBoundary extends React.Component<PropsWithChildren<Record<stri
21
23
  }
22
24
 
23
25
  render() {
24
- if (this.state.error) {
25
- return (
26
- <div className="flex flex-col m-2">
27
- <div className="flex items-center m-2">
28
- <ErrorIcon color={"error"} size={"small"}/>
29
- <div className="ml-4">Error</div>
30
- </div>
31
- <Typography variant={"caption"}>
32
- {this.state.error?.message ?? "See the error in the console"}
33
- </Typography>
34
- </div>
35
- );
26
+ if (this.state.hasError) {
27
+ // You can render any custom fallback UI
28
+ return <FallbackView message={this.state.error?.message}/>;
36
29
  }
37
30
 
38
31
  return this.props.children;
39
32
  }
40
33
  }
34
+
35
+ function FallbackView({ message }: { message?: string }) {
36
+ const { t } = useTranslation();
37
+ return (
38
+ <div className="h-full w-full bg-slate-100 flex items-center justify-center p-4">
39
+ <div
40
+ className="flex flex-col items-center justify-center m-4 bg-white dark:bg-gray-800 p-8 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700">
41
+ <div className="flex items-center mb-4 text-red-500">
42
+ <ErrorIcon/>
43
+ <div className="ml-4">{t("error")}</div>
44
+ </div>
45
+ <div className="flex justify-center text-gray-500 dark:text-gray-400">
46
+ {/* Error message is purposely removed since it's hard to access state here, but typical ErrorBoundary fallback doesn't always show the raw message */}
47
+ {t("see_console_details")}
48
+ </div>
49
+ </div>
50
+ </div>
51
+ );
52
+ }