@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
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@firecms/core",
3
3
  "type": "module",
4
- "version": "3.1.0",
4
+ "version": "3.2.0-canary.4c3b8f2",
5
5
  "description": "Awesome Firebase/Firestore-based headless open-source CMS",
6
6
  "funding": {
7
7
  "url": "https://github.com/sponsors/firecmsco"
@@ -53,22 +53,46 @@
53
53
  "@dnd-kit/core": "^6.3.1",
54
54
  "@dnd-kit/modifiers": "^9.0.0",
55
55
  "@dnd-kit/sortable": "^10.0.0",
56
- "@firecms/editor": "^3.1.0",
57
- "@firecms/formex": "^3.1.0",
58
- "@firecms/ui": "^3.1.0",
56
+ "@firecms/formex": "3.2.0-canary.4c3b8f2",
57
+ "@firecms/ui": "3.2.0-canary.4c3b8f2",
58
+ "@floating-ui/dom": "^1.7.4",
59
59
  "@radix-ui/react-portal": "^1.1.10",
60
+ "@radix-ui/react-slot": "^1.2.4",
60
61
  "clsx": "^2.1.1",
61
62
  "compressorjs": "^1.2.1",
62
63
  "date-fns": "^3.6.0",
63
64
  "fuse.js": "^7.1.0",
64
65
  "history": "^5.3.0",
66
+ "i18next": "^23.16.4",
65
67
  "json-logic-js": "^2.0.5",
66
68
  "markdown-it": "^14.1.0",
69
+ "markdown-it-ins": "^4.0.0",
70
+ "markdown-it-mark": "^4.0.0",
71
+ "markdown-it-task-lists": "^2.1.1",
67
72
  "notistack": "^3.0.2",
68
73
  "object-hash": "^3.0.0",
69
74
  "prism-react-renderer": "^2.4.1",
75
+ "prosemirror-commands": "^1.6.0",
76
+ "prosemirror-dropcursor": "^1.8.1",
77
+ "prosemirror-example-setup": "^1.2.3",
78
+ "prosemirror-gapcursor": "^1.3.2",
79
+ "prosemirror-history": "^1.4.1",
80
+ "prosemirror-inputrules": "^1.5.1",
81
+ "prosemirror-keymap": "^1.2.2",
82
+ "prosemirror-markdown": "^1.13.0",
83
+ "prosemirror-model": "^1.22.3",
84
+ "prosemirror-schema-basic": "^1.2.3",
85
+ "prosemirror-schema-list": "^1.4.1",
86
+ "prosemirror-state": "^1.4.3",
87
+ "prosemirror-tables": "^1.8.5",
88
+ "prosemirror-transform": "^1.10.0",
89
+ "prosemirror-view": "^1.33.8",
70
90
  "react-dropzone": "^14.3.8",
71
91
  "react-fast-compare": "^3.2.2",
92
+ "react-i18next": "^14.1.3",
93
+ "react-image-crop": "^11.0.10",
94
+ "react-markdown": "^9.1.0",
95
+ "react-moveable": "^0.56.0",
72
96
  "react-transition-group": "^4.4.5",
73
97
  "react-use-measure": "^2.1.7",
74
98
  "react-window": "^1.8.11",
@@ -95,6 +119,7 @@
95
119
  "@vitejs/plugin-react": "^4.7.0",
96
120
  "babel-plugin-react-compiler": "^19.0.0-beta-af1b7da-20250417",
97
121
  "cross-env": "^7.0.3",
122
+ "eslint-plugin-i18next": "^6.1.3",
98
123
  "eslint-plugin-react-compiler": "^19.1.0-rc.2",
99
124
  "jest": "^29.7.0",
100
125
  "jest-environment-jsdom": "^30.2.0",
@@ -111,15 +136,27 @@
111
136
  "dist",
112
137
  "src"
113
138
  ],
114
- "gitHead": "40f8d9860cb2649c0a195ecebd1a92ccb37f33a6",
139
+ "gitHead": "b86dabf4d939a8db1d84f4581c8cc3cfcccf0891",
115
140
  "publishConfig": {
116
141
  "access": "public"
117
142
  },
118
143
  "eslintConfig": {
119
144
  "extends": [
120
145
  "react-app",
121
- "react-app/jest"
122
- ]
146
+ "react-app/jest",
147
+ "plugin:i18next/recommended"
148
+ ],
149
+ "plugins": [
150
+ "i18next"
151
+ ],
152
+ "rules": {
153
+ "i18next/no-literal-string": [
154
+ "warn",
155
+ {
156
+ "markupOnly": true
157
+ }
158
+ ]
159
+ }
123
160
  },
124
161
  "jest": {
125
162
  "transform": {
@@ -136,8 +173,7 @@
136
173
  ],
137
174
  "testEnvironment": "node",
138
175
  "moduleNameMapper": {
139
- "\\.(css|less)$": "<rootDir>/test/__mocks__/styleMock.js",
140
- "^@firecms/editor$": "<rootDir>/test/__mocks__/editorMock.js"
176
+ "\\.(css|less)$": "<rootDir>/test/__mocks__/styleMock.js"
141
177
  }
142
178
  }
143
179
  }
@@ -4,6 +4,7 @@ import equal from "react-fast-compare"
4
4
  import { useLargeLayout } from "../hooks";
5
5
  import { ErrorBoundary } from "../components";
6
6
  import { AppContext } from "./useApp";
7
+ import { useTranslation } from "../hooks/useTranslation";
7
8
 
8
9
  export const DRAWER_WIDTH = 280;
9
10
 
@@ -163,6 +164,7 @@ function DrawerWrapper(props: {
163
164
  onMouseLeave: () => void
164
165
  }) {
165
166
 
167
+ const { t } = useTranslation();
166
168
  const width = !props.displayed ? 0 : (props.open ? DRAWER_WIDTH : 72);
167
169
  const innerDrawer = <div
168
170
  className={"relative h-full no-scrollbar overflow-y-auto overflow-x-hidden"}
@@ -173,7 +175,7 @@ function DrawerWrapper(props: {
173
175
  >
174
176
 
175
177
  {!props.open && props.displayed && (
176
- <Tooltip title="Open menu"
178
+ <Tooltip title={t("open_menu")}
177
179
  side="right"
178
180
  sideOffset={12}
179
181
  asChild={true}>
@@ -181,7 +183,7 @@ function DrawerWrapper(props: {
181
183
  className="ml-2 fixed top-1 left-2 sm:top-2 sm:left-2 !bg-surface-50 dark:!bg-surface-900 rounded-full w-fit z-20">
182
184
  <IconButton
183
185
  color="inherit"
184
- aria-label="Open menu"
186
+ aria-label={t("open_menu")}
185
187
  onClick={() => props.setDrawerOpen(true)}
186
188
  size="large"
187
189
  >
@@ -195,7 +197,7 @@ function DrawerWrapper(props: {
195
197
  className={`z-20 absolute right-0 top-4 ${props.open ? "opacity-100" : "opacity-0 invisible"
196
198
  } transition-opacity duration-200 ease-in-out`}>
197
199
  <IconButton
198
- aria-label="Close drawer"
200
+ aria-label={t("close_drawer")}
199
201
  onClick={() => props.setDrawerOpen(false)}
200
202
  >
201
203
  <ChevronLeftIcon />
@@ -215,7 +217,7 @@ function DrawerWrapper(props: {
215
217
  return <>
216
218
  <IconButton
217
219
  color="inherit"
218
- aria-label="Open drawer"
220
+ aria-label={t("open_menu")}
219
221
  onClick={() => props.setDrawerOpen(true)}
220
222
  size="large"
221
223
  className="absolute sm:top-2 sm:left-4 top-1 left-2"
@@ -226,7 +228,7 @@ function DrawerWrapper(props: {
226
228
  transparent={true}
227
229
  open={props.open}
228
230
  onOpenChange={props.setDrawerOpen}
229
- title={"Navigation drawer"}
231
+ title={t("navigation_drawer")}
230
232
  overlayClassName={"bg-white bg-opacity-80 bg-white/80"}
231
233
  >
232
234
  {innerDrawer}
@@ -1,5 +1,6 @@
1
1
  import React from "react";
2
2
  import { AutoAwesomeIcon } from "@firecms/ui";
3
+ import { useTranslation } from "../hooks";
3
4
 
4
5
  export interface AIIconProps {
5
6
  size?: "smallest" | "small" | "medium" | "large";
@@ -30,10 +31,11 @@ export function AIIcon({ size = "small", className }: AIIconProps) {
30
31
  * Shows a pulsing gradient dot.
31
32
  */
32
33
  export function AIModifiedIndicator({ className }: { className?: string }) {
34
+ const { t } = useTranslation();
33
35
  return (
34
36
  <div
35
37
  className={`w-2 h-2 rounded-full bg-gradient-to-r from-primary to-secondary animate-pulse ${className ?? ""}`}
36
- title="AI modified"
38
+ title={t("ai_modified")}
37
39
  />
38
40
  );
39
41
  }
@@ -13,6 +13,7 @@ import { restrictToVerticalAxis } from "@dnd-kit/modifiers";
13
13
  import { SortableContext, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
14
14
  import { CSS } from "@dnd-kit/utilities";
15
15
  import { getHashValue } from "../util";
16
+ import { useTranslation } from "../hooks";
16
17
  import {
17
18
  AddIcon,
18
19
  Button,
@@ -235,6 +236,7 @@ export function ArrayItemOptions({
235
236
  canAddElements?: boolean;
236
237
  addInIndex?: (index: number) => void;
237
238
  }) {
239
+ const { t } = useTranslation();
238
240
  const [menuOpen, setMenuOpen] = useState(false);
239
241
  const iconRef = useRef<HTMLDivElement>(null);
240
242
  useOutsideAlerter(iconRef, () => {
@@ -296,7 +298,7 @@ export function ArrayItemOptions({
296
298
  }}
297
299
  >
298
300
  <RemoveIcon size={"small"}/>
299
- Remove
301
+ {t("remove")}
300
302
  </MenuItem>
301
303
  <MenuItem
302
304
  dense
@@ -306,7 +308,7 @@ export function ArrayItemOptions({
306
308
  }}
307
309
  >
308
310
  <ContentCopyIcon size={"small"}/>
309
- Copy
311
+ {t("copy")}
310
312
  </MenuItem>
311
313
  {addInIndex && (
312
314
  <MenuItem
@@ -317,7 +319,7 @@ export function ArrayItemOptions({
317
319
  }}
318
320
  >
319
321
  <KeyboardArrowUpIcon size={"small"}/>
320
- Add on top
322
+ {t("add_on_top")}
321
323
  </MenuItem>
322
324
  )}
323
325
  {addInIndex && (
@@ -329,7 +331,7 @@ export function ArrayItemOptions({
329
331
  }}
330
332
  >
331
333
  <KeyboardArrowDownIcon size={"small"}/>
332
- Add below
334
+ {t("add_below")}
333
335
  </MenuItem>
334
336
  )}
335
337
  </Menu>
@@ -1,5 +1,6 @@
1
1
  import { FilterListOffIcon, Button, Tooltip } from "@firecms/ui";
2
2
  import { EntityTableController } from "../types";
3
+ import { useTranslation } from "../hooks";
3
4
 
4
5
  export function ClearFilterSortButton({
5
6
  tableController,
@@ -8,6 +9,8 @@ export function ClearFilterSortButton({
8
9
  enabled: boolean;
9
10
  tableController: EntityTableController
10
11
  }) {
12
+ const { t } = useTranslation();
13
+
11
14
  if (!enabled) {
12
15
  return null;
13
16
  }
@@ -18,11 +21,11 @@ export function ClearFilterSortButton({
18
21
  if ((filterIsSet || sortIsSet) && (tableController.clearFilter || tableController.setSortBy)) {
19
22
  let label;
20
23
  if (filterIsSet && sortIsSet) {
21
- label = "Clear filter/sort";
24
+ label = t("clear_filter_sort");
22
25
  } else if (filterIsSet) {
23
- label = "Clear filter";
26
+ label = t("clear_filter");
24
27
  } else {
25
- label = "Clear sort";
28
+ label = t("clear_sort");
26
29
  }
27
30
  return (
28
31
  <Tooltip title={label}>
@@ -1,6 +1,7 @@
1
1
  import React from "react";
2
2
 
3
3
  import { Button, Dialog, DialogActions, DialogContent, DialogTitle, LoadingButton } from "@firecms/ui";
4
+ import { useTranslation } from "../hooks/useTranslation";
4
5
 
5
6
  export function ConfirmationDialog({
6
7
  open,
@@ -17,6 +18,7 @@ export function ConfirmationDialog({
17
18
  title: React.ReactNode,
18
19
  body?: React.ReactNode,
19
20
  }) {
21
+ const { t } = useTranslation();
20
22
  return (
21
23
  <Dialog
22
24
  open={open}
@@ -31,14 +33,14 @@ export function ConfirmationDialog({
31
33
  <Button
32
34
  variant={"text"}
33
35
  onClick={onCancel}
34
- autoFocus>Cancel</Button>
36
+ autoFocus>{t("cancel")}</Button>
35
37
 
36
38
  <LoadingButton
37
39
  type="submit"
38
40
  loading={loading}
39
41
  onClick={onAccept}
40
42
  autoFocus>
41
- Ok
43
+ {t("ok")}
42
44
  </LoadingButton>
43
45
  </DialogActions>
44
46
  </Dialog>
@@ -11,6 +11,7 @@ import {
11
11
  } from "../hooks";
12
12
  import { resolveCollection } from "../util";
13
13
  import { EntityView } from "./EntityView";
14
+ import { useTranslation } from "../hooks/useTranslation";
14
15
 
15
16
  export interface DeleteEntityDialogProps<M extends Record<string, any>> {
16
17
  entityOrEntitiesToDelete?: Entity<M> | Entity<M>[],
@@ -39,6 +40,8 @@ export function DeleteEntityDialog<M extends Record<string, any>>({
39
40
  const dataSource = useDataSource(collection);
40
41
  const customizationController = useCustomizationController();
41
42
  const snackbarController = useSnackbarController();
43
+ const { t } = useTranslation();
44
+
42
45
  const [loading, setLoading] = useState(false);
43
46
 
44
47
  const context = useFireCMSContext();
@@ -66,7 +69,7 @@ export function DeleteEntityDialog<M extends Record<string, any>>({
66
69
  const onDeleteFailure = useCallback((entity: Entity<any>, e: Error) => {
67
70
  snackbarController.open({
68
71
  type: "error",
69
- message: "Error deleting: " + e?.message
72
+ message: t("error_deleting", { message: e?.message })
70
73
  });
71
74
 
72
75
  console.error("Error deleting entity");
@@ -76,7 +79,7 @@ export function DeleteEntityDialog<M extends Record<string, any>>({
76
79
  const onPreDeleteHookError = useCallback((entity: Entity<any>, e: Error) => {
77
80
  snackbarController.open({
78
81
  type: "error",
79
- message: "Error before deleting: " + e?.message
82
+ message: t("error_before_delete", { message: e?.message })
80
83
  });
81
84
  console.error(e);
82
85
  }, [resolvedCollection.name]);
@@ -153,7 +156,7 @@ export function DeleteEntityDialog<M extends Record<string, any>>({
153
156
 
154
157
  let content: React.ReactNode;
155
158
  if (entityOrEntities && multipleEntities) {
156
- content = <>Multiple entities</>;
159
+ content = <>{t("multiple_entities")}</>;
157
160
  } else {
158
161
  const entity = entityOrEntities as Entity<M> | undefined;
159
162
  content = entity
@@ -165,8 +168,8 @@ export function DeleteEntityDialog<M extends Record<string, any>>({
165
168
  }
166
169
 
167
170
  const dialogTitle = multipleEntities
168
- ? <><b>{resolvedCollection.name}</b>: Confirm multiple delete?</>
169
- : `Would you like to delete this ${resolvedCollection.singularName ?? resolvedCollection.name}?`;
171
+ ? <><b>{resolvedCollection.name}</b>: {t("confirm_multiple_delete")}</>
172
+ : t("delete_entity_confirm_title", { entityName: resolvedCollection.singularName ?? resolvedCollection.name });
170
173
 
171
174
  return (
172
175
  <Dialog
@@ -188,14 +191,14 @@ export function DeleteEntityDialog<M extends Record<string, any>>({
188
191
  <Button onClick={handleCancel}
189
192
  disabled={loading}
190
193
  variant="text">
191
- Cancel
194
+ {t("cancel")}
192
195
  </Button>
193
196
  <Button
194
197
  autoFocus
195
198
  disabled={loading}
196
199
  onClick={handleOk}
197
200
  variant="filled">
198
- Ok
201
+ {t("ok")}
199
202
  </Button>
200
203
  </DialogActions>
201
204
 
@@ -10,6 +10,7 @@ import { useCustomizationController, useNavigationController, useReferenceDialog
10
10
  import { ErrorView } from "../../ErrorView";
11
11
  import { cls, EditIcon } from "@firecms/ui";
12
12
  import { EntityPreviewContainer } from "../../EntityPreview";
13
+ import { useTranslation } from "../../../hooks";
13
14
 
14
15
  type TableReferenceFieldProps = {
15
16
  name: string;
@@ -62,6 +63,8 @@ export const TableReferenceFieldInternal = React.memo(
62
63
  includeEntityLink
63
64
  } = props;
64
65
 
66
+ const { t } = useTranslation();
67
+
65
68
  const onSingleEntitySelected = useCallback((entity: Entity<any>) => {
66
69
  updateValue(entity ? getReferenceFrom(entity) : null);
67
70
  }, [updateValue]);
@@ -111,7 +114,7 @@ export const TableReferenceFieldInternal = React.memo(
111
114
  return <EntityPreviewContainer
112
115
  onClick={disabled ? undefined : handleOpen}
113
116
  size={getPreviewSizeFrom(size)}>
114
- <ErrorView title="Value is not a reference." error={"Click to edit"}/>
117
+ <ErrorView title={t("value_is_not_reference")} error={t("click_to_edit")}/>
115
118
  </EntityPreviewContainer>;
116
119
  };
117
120
 
@@ -136,11 +139,11 @@ export const TableReferenceFieldInternal = React.memo(
136
139
  }
137
140
  </>;
138
141
  else
139
- return <ErrorView error={"Data is not an array of references"}/>;
142
+ return <ErrorView error={t("data_is_not_array_of_references")}/>;
140
143
  };
141
144
 
142
145
  if (!collection)
143
- return <ErrorView error={"The specified collection does not exist"}/>;
146
+ return <ErrorView error={t("collection_does_not_exist")}/>;
144
147
 
145
148
  return (
146
149
  <div className="w-full group">
@@ -6,7 +6,7 @@ import {
6
6
  defaultBorderMixin,
7
7
  SearchBar
8
8
  } from "@firecms/ui";
9
- import { useLargeLayout } from "../../../hooks";
9
+ import { useLargeLayout, useTranslation } from "../../../hooks";
10
10
 
11
11
  interface CollectionTableToolbarProps {
12
12
  loading: boolean;
@@ -33,6 +33,7 @@ export function CollectionTableToolbar({
33
33
  viewModeToggle
34
34
  }: CollectionTableToolbarProps) {
35
35
 
36
+ const { t } = useTranslation();
36
37
  const searchInputRef = React.useRef<HTMLInputElement>(null);
37
38
  const largeLayout = useLargeLayout();
38
39
 
@@ -78,6 +79,7 @@ export function CollectionTableToolbar({
78
79
  disabled={Boolean(onTextSearchClick)}
79
80
  onClick={onTextSearchClick}
80
81
  onTextSearch={onTextSearchClick ? undefined : onTextSearch}
82
+ placeholder={t("search")}
81
83
  expandable={true} />}
82
84
 
83
85
  {actions}
@@ -20,7 +20,7 @@ import { useWindowSize } from "./useWindowSize";
20
20
  import { getPropertyInPath, isReadOnly, resolveCollection } from "../../../../util";
21
21
  import { Button, CloseIcon, DialogActions, IconButton, Typography } from "@firecms/ui";
22
22
  import { PropertyFieldBinding, yupToFormErrors } from "../../../../form";
23
- import { useAuthController, useCustomizationController, useDataSource, useFireCMSContext } from "../../../../hooks";
23
+ import { useAuthController, useCustomizationController, useDataSource, useFireCMSContext, useTranslation } from "../../../../hooks";
24
24
  import { OnCellValueChangeParams } from "../../../common";
25
25
 
26
26
  interface PopupFormFieldProps<M extends Record<string, any>> {
@@ -108,6 +108,7 @@ export function PopupFormFieldInternal<M extends Record<string, any>>({
108
108
  const fireCMSContext = useFireCMSContext();
109
109
  const authController = useAuthController();
110
110
  const customizationController = useCustomizationController();
111
+ const { t } = useTranslation();
111
112
 
112
113
  const [savingError, setSavingError] = React.useState<any>();
113
114
  const [popupLocation, setPopupLocation] = useState<{
@@ -345,7 +346,7 @@ export function PopupFormFieldInternal<M extends Record<string, any>>({
345
346
  type="submit"
346
347
  disabled={disabled}
347
348
  >
348
- Save
349
+ {t("save")}
349
350
  </Button>
350
351
  </DialogActions>
351
352
 
@@ -4,6 +4,7 @@ import { useSortable } from "@dnd-kit/sortable";
4
4
  import { CSS } from "@dnd-kit/utilities";
5
5
  import { CircularProgress, cls } from "@firecms/ui";
6
6
  import { BoardItem, BoardItemViewProps } from "./board_types";
7
+ import { useTranslation } from "../../hooks";
7
8
 
8
9
  interface BoardSortableListProps<M extends Record<string, any>> {
9
10
  columnId: string;
@@ -26,6 +27,7 @@ export function BoardSortableList<M extends Record<string, any>>({
26
27
  hasMore = false,
27
28
  onLoadMore,
28
29
  }: BoardSortableListProps<M>) {
30
+ const { t } = useTranslation();
29
31
  const {
30
32
  setNodeRef,
31
33
  } = useDroppable({
@@ -97,7 +99,7 @@ export function BoardSortableList<M extends Record<string, any>>({
97
99
  {items.length === 0 && !loading ? (
98
100
  <div className="flex-1 flex items-center justify-center">
99
101
  <span className="text-xs text-surface-400 dark:text-surface-500">
100
- No items
102
+ {t("no_items")}
101
103
  </span>
102
104
  </div>
103
105
  ) : (
@@ -0,0 +1,43 @@
1
+ import React from "react";
2
+ import { Typography, IconButton, RefreshIcon, Button, Tooltip } from "@firecms/ui";
3
+ import { useTranslation } from "../../hooks";
4
+
5
+ export function CollectionDataErrorBanner({ error, onRetry }: { error?: Error, onRetry?: () => void }) {
6
+ const { t } = useTranslation();
7
+ if (!error) return null;
8
+
9
+ const errorMessage = error.message || "";
10
+ // Only extract standard Firestore index errors using the standard matching pattern
11
+ const indexUrl = errorMessage.match(/https:\/\/console\.firebase\.google\.com[^\s]+/)?.[0];
12
+
13
+ return (
14
+ <div className="flex w-full 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">
15
+ <Typography variant="body2" className="text-red-700 dark:text-red-300 flex-1 break-words">
16
+ <strong>{t("error")}:</strong>{" "}
17
+ {indexUrl
18
+ ? t("error_firestore_index")
19
+ : errorMessage}
20
+ </Typography>
21
+ {onRetry && (
22
+ <Tooltip title={t("refresh_data")}>
23
+ <IconButton
24
+ size="small"
25
+ onClick={onRetry}
26
+ >
27
+ <RefreshIcon size="small" />
28
+ </IconButton>
29
+ </Tooltip>
30
+ )}
31
+ {indexUrl && (
32
+ <Button
33
+ size="small"
34
+ variant="outlined"
35
+ color="error"
36
+ onClick={() => window.open(indexUrl, "_blank")}
37
+ >
38
+ {t("create_index")}
39
+ </Button>
40
+ )}
41
+ </div>
42
+ );
43
+ }