@firecms/core 3.1.0-canary.1df3b2c → 3.1.0-canary.75005e4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/EntityCollectionTable/internal/popup_field/useDraggable.d.ts +2 -2
- package/dist/components/EntityCollectionView/CollectionDataErrorBanner.d.ts +4 -0
- package/dist/components/EntityCollectionView/ViewModeToggle.d.ts +5 -10
- package/dist/components/ErrorBoundary.d.ts +4 -2
- package/dist/components/HomePage/DefaultHomePage.d.ts +0 -1
- package/dist/components/LanguageToggle.d.ts +1 -0
- package/dist/components/UnsavedChangesDialog.d.ts +1 -0
- package/dist/components/VirtualTable/VirtualTableHeader.d.ts +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/core/DrawerNavigationGroup.d.ts +2 -2
- package/dist/editor/components/SlashCommandMenu.d.ts +6 -0
- package/dist/editor/components/editor-bubble-item.d.ts +8 -0
- package/dist/editor/components/editor-bubble.d.ts +8 -0
- package/dist/editor/components/index.d.ts +14 -0
- package/dist/editor/editor.d.ts +30 -0
- package/dist/editor/extensions/HighlightDecorationExtension.d.ts +24 -0
- package/dist/editor/extensions/Image/index.d.ts +6 -0
- package/dist/editor/extensions/Image.d.ts +6 -0
- package/dist/editor/extensions/TextLoadingDecorationExtension.d.ts +16 -0
- package/dist/editor/extensions/clipboard.d.ts +7 -0
- package/dist/editor/extensions/custom-keymap.d.ts +1 -0
- package/dist/editor/extensions/drag-and-drop.d.ts +9 -0
- package/dist/editor/hooks/useProseMirror.d.ts +14 -0
- package/dist/editor/hooks/useProseMirrorContext.d.ts +9 -0
- package/dist/editor/index.d.ts +2 -0
- package/dist/editor/markdown.d.ts +5 -0
- package/dist/editor/nodeViews/ImageComponent.d.ts +3 -0
- package/dist/editor/nodeViews/ReactNodeView.d.ts +29 -0
- package/dist/editor/nodeViews/TaskItemComponent.d.ts +3 -0
- package/dist/editor/nodeViews/index.d.ts +6 -0
- package/dist/editor/plugins/index.d.ts +2 -0
- package/dist/editor/plugins/inputrules.d.ts +6 -0
- package/dist/editor/plugins/placeholderPlugin.d.ts +3 -0
- package/dist/editor/plugins/slashCommandPlugin.d.ts +11 -0
- package/dist/editor/schema.d.ts +2 -0
- package/dist/editor/selectors/ai-selector.d.ts +0 -0
- package/dist/editor/selectors/color-selector.d.ts +10 -0
- package/dist/editor/selectors/link-selector.d.ts +8 -0
- package/dist/editor/selectors/node-selector.d.ts +15 -0
- package/dist/editor/selectors/text-buttons.d.ts +1 -0
- package/dist/editor/types.d.ts +5 -0
- package/dist/editor/useProseMirror.d.ts +16 -0
- package/dist/editor/utils/prosemirror-utils.d.ts +6 -0
- package/dist/editor/utils/remove_classes.d.ts +1 -0
- package/dist/editor/utils/useDebouncedCallback.d.ts +1 -0
- package/dist/form/components/ErrorFocus.d.ts +1 -1
- package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +1 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/useBuildNavigationController.d.ts +0 -1
- package/dist/hooks/useCollapsedGroups.d.ts +3 -3
- package/dist/hooks/useTranslation.d.ts +17 -0
- package/dist/i18n/FireCMSi18nProvider.d.ts +33 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.es.js +11441 -2215
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +11423 -2216
- package/dist/index.umd.js.map +1 -1
- package/dist/internal/useRestoreScroll.d.ts +1 -1
- package/dist/locales/de.d.ts +2 -0
- package/dist/locales/en.d.ts +10 -0
- package/dist/locales/es.d.ts +10 -0
- package/dist/locales/fr.d.ts +2 -0
- package/dist/locales/hi.d.ts +2 -0
- package/dist/locales/it.d.ts +2 -0
- package/dist/locales/pt.d.ts +7 -0
- package/dist/types/analytics.d.ts +1 -1
- package/dist/types/collections.d.ts +8 -0
- package/dist/types/customization_controller.d.ts +2 -1
- package/dist/types/firecms.d.ts +2 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/navigation.d.ts +2 -2
- package/dist/types/plugins.d.ts +23 -0
- package/dist/types/translations.d.ts +646 -0
- package/dist/util/entities.d.ts +1 -1
- package/dist/util/resolutions.d.ts +2 -2
- package/package.json +47 -13
- package/src/app/Scaffold.tsx +7 -5
- package/src/components/AIIcon.tsx +3 -1
- package/src/components/ArrayContainer.tsx +6 -4
- package/src/components/ClearFilterSortButton.tsx +6 -3
- package/src/components/ConfirmationDialog.tsx +4 -2
- package/src/components/DeleteEntityDialog.tsx +10 -7
- package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +6 -3
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +3 -1
- package/src/components/EntityCollectionTable/internal/EntityTableCellActions.tsx +1 -1
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +3 -2
- package/src/components/EntityCollectionTable/internal/popup_field/useDraggable.tsx +11 -11
- package/src/components/EntityCollectionView/BoardSortableList.tsx +3 -1
- package/src/components/EntityCollectionView/CollectionDataErrorBanner.tsx +43 -0
- package/src/components/EntityCollectionView/EntityBoardCard.tsx +1 -1
- package/src/components/EntityCollectionView/EntityCard.tsx +4 -0
- package/src/components/EntityCollectionView/EntityCollectionBoardView.tsx +39 -46
- package/src/components/EntityCollectionView/EntityCollectionCardView.tsx +17 -25
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +73 -31
- package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +4 -3
- package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +4 -2
- package/src/components/EntityCollectionView/FiltersDialog.tsx +8 -5
- package/src/components/EntityCollectionView/ViewModeToggle.tsx +37 -37
- package/src/components/EntityView.tsx +3 -2
- package/src/components/ErrorBoundary.tsx +27 -15
- package/src/components/HomePage/DefaultHomePage.tsx +19 -13
- package/src/components/HomePage/HomePageDnD.tsx +3 -1
- package/src/components/HomePage/NavigationGroup.tsx +3 -1
- package/src/components/HomePage/RenameGroupDialog.tsx +15 -13
- package/src/components/LanguageToggle.tsx +66 -0
- package/src/components/NotFoundPage.tsx +5 -3
- package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +9 -7
- package/src/components/ReferenceWidget.tsx +3 -2
- package/src/components/SearchIconsView.tsx +3 -1
- package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +11 -0
- package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +15 -2
- package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +11 -0
- package/src/components/UnsavedChangesDialog.tsx +6 -4
- package/src/components/VirtualTable/VirtualTable.performance.test.tsx +1 -0
- package/src/components/VirtualTable/VirtualTable.tsx +116 -113
- package/src/components/VirtualTable/VirtualTableHeader.tsx +54 -52
- package/src/components/VirtualTable/VirtualTableHeaderRow.tsx +1 -1
- package/src/components/VirtualTable/fields/VirtualTableSelect.tsx +3 -3
- package/src/components/common/default_entity_actions.tsx +4 -0
- package/src/components/common/useDataSourceTableController.tsx +12 -4
- package/src/components/index.tsx +1 -0
- package/src/core/DefaultAppBar.tsx +15 -11
- package/src/core/DefaultDrawer.tsx +8 -2
- package/src/core/DrawerNavigationGroup.tsx +5 -3
- package/src/core/EntityEditView.tsx +4 -3
- package/src/core/EntityEditViewFormActions.tsx +24 -17
- package/src/core/EntitySidePanel.tsx +32 -29
- package/src/core/FireCMS.tsx +33 -6
- package/src/core/field_configs.tsx +14 -9
- package/src/editor/components/SlashCommandMenu.tsx +348 -0
- package/src/editor/components/editor-bubble-item.tsx +32 -0
- package/src/editor/components/editor-bubble.tsx +118 -0
- package/src/editor/components/index.ts +12 -0
- package/src/editor/editor.tsx +307 -0
- package/src/editor/extensions/HighlightDecorationExtension.ts +114 -0
- package/src/editor/extensions/Image/index.ts +133 -0
- package/src/editor/extensions/Image.ts +144 -0
- package/src/editor/extensions/TextLoadingDecorationExtension.tsx +107 -0
- package/src/editor/extensions/clipboard.ts +72 -0
- package/src/editor/extensions/custom-keymap.ts +24 -0
- package/src/editor/extensions/drag-and-drop.tsx +472 -0
- package/src/editor/hooks/useProseMirror.ts +115 -0
- package/src/editor/hooks/useProseMirrorContext.ts +15 -0
- package/src/editor/index.ts +2 -0
- package/src/editor/markdown.ts +110 -0
- package/src/editor/nodeViews/ImageComponent.tsx +20 -0
- package/src/editor/nodeViews/ReactNodeView.tsx +89 -0
- package/src/editor/nodeViews/TaskItemComponent.tsx +29 -0
- package/src/editor/nodeViews/index.ts +35 -0
- package/src/editor/plugins/index.ts +55 -0
- package/src/editor/plugins/inputrules.ts +82 -0
- package/src/editor/plugins/placeholderPlugin.ts +55 -0
- package/src/editor/plugins/slashCommandPlugin.ts +49 -0
- package/src/editor/schema.ts +228 -0
- package/src/editor/selectors/ai-selector.tsx +111 -0
- package/src/editor/selectors/color-selector.tsx +200 -0
- package/src/editor/selectors/link-selector.tsx +118 -0
- package/src/editor/selectors/node-selector.tsx +157 -0
- package/src/editor/selectors/text-buttons.tsx +86 -0
- package/src/editor/types.ts +6 -0
- package/src/editor/useProseMirror.ts +126 -0
- package/src/editor/utils/prosemirror-utils.ts +78 -0
- package/src/editor/utils/remove_classes.ts +17 -0
- package/src/editor/utils/useDebouncedCallback.ts +25 -0
- package/src/form/EntityForm.tsx +76 -63
- package/src/form/EntityFormActions.tsx +19 -12
- package/src/form/PropertyFieldBinding.tsx +6 -5
- package/src/form/components/ErrorFocus.tsx +3 -3
- package/src/form/components/LocalChangesMenu.tsx +13 -13
- package/src/form/components/StorageItemPreview.tsx +3 -2
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +4 -4
- package/src/form/field_bindings/BlockFieldBinding.tsx +5 -2
- package/src/form/field_bindings/KeyValueFieldBinding.tsx +23 -18
- package/src/form/field_bindings/MapFieldBinding.tsx +4 -3
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +4 -4
- package/src/form/field_bindings/RepeatFieldBinding.tsx +3 -1
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +87 -85
- package/src/hooks/index.tsx +1 -0
- package/src/hooks/useBuildNavigationController.tsx +49 -22
- package/src/hooks/useCollapsedGroups.ts +7 -6
- package/src/hooks/useTranslation.ts +31 -0
- package/src/hooks/useValidateAuthenticator.tsx +1 -1
- package/src/i18n/FireCMSi18nProvider.tsx +160 -0
- package/src/index.ts +4 -0
- package/src/internal/useBuildDataSource.ts +1 -2
- package/src/internal/useBuildSideEntityController.tsx +22 -20
- package/src/locales/de.ts +691 -0
- package/src/locales/en.ts +703 -0
- package/src/locales/es.ts +703 -0
- package/src/locales/fr.ts +691 -0
- package/src/locales/hi.ts +691 -0
- package/src/locales/it.ts +691 -0
- package/src/locales/pt.ts +700 -0
- package/src/preview/PropertyPreview.tsx +1 -0
- package/src/preview/components/UrlComponentPreview.tsx +4 -2
- package/src/preview/components/UserPreview.tsx +3 -1
- package/src/types/analytics.ts +10 -0
- package/src/types/collections.ts +9 -0
- package/src/types/customization_controller.tsx +2 -1
- package/src/types/firecms.tsx +2 -1
- package/src/types/index.ts +1 -0
- package/src/types/navigation.ts +2 -2
- package/src/types/plugins.tsx +26 -0
- package/src/types/translations.ts +725 -0
- package/src/util/entities.ts +1 -1
- package/src/util/join_collections.ts +10 -8
- package/src/util/previews.ts +2 -2
- package/src/util/property_utils.tsx +1 -1
- package/src/util/resolutions.ts +5 -3
|
@@ -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">
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
241
|
+
{t("cancel")}
|
|
239
242
|
</Button>
|
|
240
243
|
<Button
|
|
241
244
|
variant="filled"
|
|
242
245
|
onClick={handleApply}
|
|
243
246
|
>
|
|
244
|
-
|
|
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;
|
|
@@ -22,16 +23,11 @@ export type ViewModeToggleProps = {
|
|
|
22
23
|
viewMode?: ViewMode;
|
|
23
24
|
onViewModeChange?: (mode: ViewMode) => void;
|
|
24
25
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
26
|
+
* Which view modes are enabled for this collection.
|
|
27
|
+
* Only these modes will appear in the toggle.
|
|
28
|
+
* Defaults to all three: ["table", "cards", "kanban"].
|
|
27
29
|
*/
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Whether a plugin exists that can configure Kanban (e.g., collection editor).
|
|
31
|
-
* When true, Kanban option is always shown (enabled or not based on kanbanEnabled).
|
|
32
|
-
* When false, Kanban option is shown but disabled.
|
|
33
|
-
*/
|
|
34
|
-
hasKanbanConfigPlugin?: boolean;
|
|
30
|
+
enabledViews?: ViewMode[];
|
|
35
31
|
/**
|
|
36
32
|
* Current size for card/table views
|
|
37
33
|
*/
|
|
@@ -62,11 +58,12 @@ export type ViewModeToggleProps = {
|
|
|
62
58
|
onKanbanPropertyChange?: (property: string) => void;
|
|
63
59
|
}
|
|
64
60
|
|
|
61
|
+
const ALL_VIEW_MODES: ViewMode[] = ["table", "cards", "kanban"];
|
|
62
|
+
|
|
65
63
|
export function ViewModeToggle({
|
|
66
64
|
viewMode = "table",
|
|
67
65
|
onViewModeChange,
|
|
68
|
-
|
|
69
|
-
hasKanbanConfigPlugin = false,
|
|
66
|
+
enabledViews = ALL_VIEW_MODES,
|
|
70
67
|
size,
|
|
71
68
|
onSizeChanged,
|
|
72
69
|
open,
|
|
@@ -76,6 +73,8 @@ export function ViewModeToggle({
|
|
|
76
73
|
onKanbanPropertyChange
|
|
77
74
|
}: ViewModeToggleProps) {
|
|
78
75
|
|
|
76
|
+
const { t } = useTranslation();
|
|
77
|
+
|
|
79
78
|
if (!onViewModeChange) {
|
|
80
79
|
return null;
|
|
81
80
|
}
|
|
@@ -88,44 +87,44 @@ export function ViewModeToggle({
|
|
|
88
87
|
};
|
|
89
88
|
|
|
90
89
|
const getViewModeName = () => {
|
|
91
|
-
if (viewMode === "kanban") return "
|
|
92
|
-
if (viewMode === "cards") return "
|
|
93
|
-
return "
|
|
90
|
+
if (viewMode === "kanban") return t("board");
|
|
91
|
+
if (viewMode === "cards") return t("cards");
|
|
92
|
+
return t("list");
|
|
94
93
|
};
|
|
95
94
|
|
|
96
|
-
const showKanban = kanbanEnabled || hasKanbanConfigPlugin;
|
|
97
95
|
const showSizeSelector = size && onSizeChanged && (viewMode === "table" || viewMode === "cards");
|
|
98
96
|
const showKanbanPropertySelector = viewMode === "kanban" &&
|
|
99
97
|
kanbanPropertyOptions &&
|
|
100
98
|
kanbanPropertyOptions.length > 0 &&
|
|
101
99
|
onKanbanPropertyChange;
|
|
102
100
|
|
|
103
|
-
// Build toggle options
|
|
101
|
+
// Build toggle options based on enabledViews
|
|
104
102
|
const viewModeOptions: ToggleButtonOption<ViewMode>[] = useMemo(() => {
|
|
105
|
-
const
|
|
103
|
+
const allOptions: ToggleButtonOption<ViewMode>[] = [
|
|
106
104
|
{
|
|
107
105
|
value: "table",
|
|
108
|
-
label: "
|
|
106
|
+
label: t("list"),
|
|
109
107
|
icon: <ListIcon size="small" />
|
|
110
108
|
},
|
|
111
109
|
{
|
|
112
110
|
value: "cards",
|
|
113
|
-
label: "
|
|
111
|
+
label: t("cards"),
|
|
114
112
|
icon: <AppsIcon size="small" />
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
value: "kanban",
|
|
116
|
+
label: t("board"),
|
|
117
|
+
icon: <ViewKanbanIcon size="small" />
|
|
115
118
|
}
|
|
116
119
|
];
|
|
117
120
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
value: "kanban",
|
|
121
|
-
label: "Board",
|
|
122
|
-
icon: <ViewKanbanIcon size="small" />,
|
|
123
|
-
disabled: !kanbanEnabled && !hasKanbanConfigPlugin
|
|
124
|
-
});
|
|
125
|
-
}
|
|
121
|
+
return allOptions.filter(option => enabledViews.includes(option.value));
|
|
122
|
+
}, [enabledViews]);
|
|
126
123
|
|
|
127
|
-
|
|
128
|
-
|
|
124
|
+
// Don't render if only one view is enabled
|
|
125
|
+
if (viewModeOptions.length <= 1 && !showSizeSelector) {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
129
128
|
|
|
130
129
|
return (
|
|
131
130
|
<Popover
|
|
@@ -141,18 +140,20 @@ export function ViewModeToggle({
|
|
|
141
140
|
>
|
|
142
141
|
<div className="p-3 flex flex-col gap-3 min-w-[240px]">
|
|
143
142
|
{/* View mode toggle using ToggleButtonGroup */}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
143
|
+
{viewModeOptions.length > 1 && (
|
|
144
|
+
<ToggleButtonGroup
|
|
145
|
+
value={viewMode}
|
|
146
|
+
onValueChange={onViewModeChange}
|
|
147
|
+
options={viewModeOptions}
|
|
148
|
+
/>
|
|
149
|
+
)}
|
|
149
150
|
|
|
150
151
|
{/* Size selector */}
|
|
151
152
|
{showSizeSelector && (
|
|
152
153
|
<div className="flex flex-row items-center justify-between gap-2">
|
|
153
154
|
<div className="flex items-center gap-2 text-sm text-surface-600 dark:text-surface-300">
|
|
154
155
|
<ViewColumnIcon size="small" />
|
|
155
|
-
<span>
|
|
156
|
+
<span>{t("size_label")}</span>
|
|
156
157
|
</div>
|
|
157
158
|
<Select
|
|
158
159
|
value={size}
|
|
@@ -175,7 +176,7 @@ export function ViewModeToggle({
|
|
|
175
176
|
<div className="flex flex-row items-center justify-between gap-2">
|
|
176
177
|
<div className="flex items-center gap-2 text-sm text-surface-600 dark:text-surface-300">
|
|
177
178
|
<ViewKanbanIcon size="small" />
|
|
178
|
-
<span>
|
|
179
|
+
<span>{t("group_by")}</span>
|
|
179
180
|
</div>
|
|
180
181
|
<Select
|
|
181
182
|
value={selectedKanbanProperty}
|
|
@@ -199,4 +200,3 @@ export function ViewModeToggle({
|
|
|
199
200
|
</Popover>
|
|
200
201
|
);
|
|
201
202
|
}
|
|
202
|
-
|
|
@@ -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
|
-
|
|
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
|
-
|
|
7
|
+
hasError: boolean,
|
|
8
|
+
error?: Error
|
|
7
9
|
}> {
|
|
8
10
|
constructor(props: any) {
|
|
9
11
|
super(props);
|
|
10
|
-
this.state = {
|
|
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.
|
|
25
|
-
|
|
26
|
-
|
|
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
|
+
}
|
|
@@ -30,8 +30,13 @@ import { NavigationCardBinding } from "./NavigationCardBinding";
|
|
|
30
30
|
import { SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable";
|
|
31
31
|
import { restrictToVerticalAxis, restrictToWindowEdges } from "@dnd-kit/modifiers";
|
|
32
32
|
import { RenameGroupDialog } from "./RenameGroupDialog";
|
|
33
|
+
import { useTranslation } from "../../hooks/useTranslation";
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Internal sentinel key for ungrouped navigation entries.
|
|
37
|
+
* Not displayed — display components use t("views_group") when group is undefined.
|
|
38
|
+
*/
|
|
39
|
+
const DEFAULT_GROUP_KEY = "__default__";
|
|
35
40
|
export const ADMIN_GROUP_NAME = "Admin";
|
|
36
41
|
|
|
37
42
|
export function DefaultHomePage({
|
|
@@ -47,6 +52,7 @@ export function DefaultHomePage({
|
|
|
47
52
|
const context = useFireCMSContext();
|
|
48
53
|
const customizationController = useCustomizationController();
|
|
49
54
|
const navigationController = useNavigationController();
|
|
55
|
+
const { t } = useTranslation();
|
|
50
56
|
|
|
51
57
|
if (!navigationController.topLevelNavigation)
|
|
52
58
|
throw Error("Navigation not ready");
|
|
@@ -108,7 +114,7 @@ export function DefaultHomePage({
|
|
|
108
114
|
const g =
|
|
109
115
|
e.type === "admin"
|
|
110
116
|
? ADMIN_GROUP_NAME
|
|
111
|
-
: e.group ??
|
|
117
|
+
: e.group ?? DEFAULT_GROUP_KEY;
|
|
112
118
|
(entriesByGroup[g] ??= []).push(e);
|
|
113
119
|
});
|
|
114
120
|
|
|
@@ -119,7 +125,7 @@ export function DefaultHomePage({
|
|
|
119
125
|
|
|
120
126
|
if (performingSearch) {
|
|
121
127
|
const ordered = [
|
|
122
|
-
...new Set(src.map((e) => e.group ??
|
|
128
|
+
...new Set(src.map((e) => e.group ?? DEFAULT_GROUP_KEY))
|
|
123
129
|
];
|
|
124
130
|
allProcessed = ordered
|
|
125
131
|
.map((name) => ({
|
|
@@ -129,11 +135,11 @@ export function DefaultHomePage({
|
|
|
129
135
|
.filter((g) => g.entries.length);
|
|
130
136
|
} else {
|
|
131
137
|
allProcessed = groupOrderFromNavController.map((g) => ({
|
|
132
|
-
name: g,
|
|
133
|
-
entries: entriesByGroup[g] || []
|
|
138
|
+
name: g ?? DEFAULT_GROUP_KEY,
|
|
139
|
+
entries: entriesByGroup[g ?? DEFAULT_GROUP_KEY] || []
|
|
134
140
|
}));
|
|
135
141
|
Object.keys(entriesByGroup).forEach((g) => {
|
|
136
|
-
if (!groupOrderFromNavController.includes(g))
|
|
142
|
+
if (!groupOrderFromNavController.map(x => x ?? DEFAULT_GROUP_KEY).includes(g))
|
|
137
143
|
allProcessed.push({
|
|
138
144
|
name: g,
|
|
139
145
|
entries: entriesByGroup[g]
|
|
@@ -141,9 +147,9 @@ export function DefaultHomePage({
|
|
|
141
147
|
});
|
|
142
148
|
|
|
143
149
|
// Ensure default group exists if there are plugin additional cards but no collections
|
|
144
|
-
if (hasPluginAdditionalCards && !allProcessed.some(g => g.name ===
|
|
150
|
+
if (hasPluginAdditionalCards && !allProcessed.some(g => g.name === DEFAULT_GROUP_KEY)) {
|
|
145
151
|
allProcessed.push({
|
|
146
|
-
name:
|
|
152
|
+
name: DEFAULT_GROUP_KEY,
|
|
147
153
|
entries: []
|
|
148
154
|
});
|
|
149
155
|
}
|
|
@@ -151,7 +157,7 @@ export function DefaultHomePage({
|
|
|
151
157
|
allProcessed = allProcessed.filter(
|
|
152
158
|
(g) =>
|
|
153
159
|
g.entries.length ||
|
|
154
|
-
(g.name ===
|
|
160
|
+
(g.name === DEFAULT_GROUP_KEY && hasPluginAdditionalCards)
|
|
155
161
|
);
|
|
156
162
|
}
|
|
157
163
|
|
|
@@ -362,7 +368,7 @@ export function DefaultHomePage({
|
|
|
362
368
|
>
|
|
363
369
|
<SearchBar
|
|
364
370
|
onTextSearch={updateSearch}
|
|
365
|
-
placeholder="
|
|
371
|
+
placeholder={t("search_collections")}
|
|
366
372
|
autoFocus
|
|
367
373
|
innerClassName="w-full"
|
|
368
374
|
className="w-full flex-grow"
|
|
@@ -412,7 +418,7 @@ export function DefaultHomePage({
|
|
|
412
418
|
|
|
413
419
|
const actionProps: PluginHomePageAdditionalCardsProps = {
|
|
414
420
|
group:
|
|
415
|
-
groupKey ===
|
|
421
|
+
groupKey === DEFAULT_GROUP_KEY
|
|
416
422
|
? undefined
|
|
417
423
|
: groupKey,
|
|
418
424
|
context
|
|
@@ -432,7 +438,7 @@ export function DefaultHomePage({
|
|
|
432
438
|
>
|
|
433
439
|
<NavigationGroup
|
|
434
440
|
group={
|
|
435
|
-
groupKey ===
|
|
441
|
+
groupKey === DEFAULT_GROUP_KEY
|
|
436
442
|
? undefined
|
|
437
443
|
: groupKey
|
|
438
444
|
}
|
|
@@ -535,7 +541,7 @@ export function DefaultHomePage({
|
|
|
535
541
|
<NavigationGroup
|
|
536
542
|
group={
|
|
537
543
|
activeGroupData.name ===
|
|
538
|
-
|
|
544
|
+
DEFAULT_GROUP_KEY
|
|
539
545
|
? undefined
|
|
540
546
|
: activeGroupData.name
|
|
541
547
|
}
|
|
@@ -30,6 +30,7 @@ import { CSS } from "@dnd-kit/utilities";
|
|
|
30
30
|
import { NavigationCardBinding } from "./NavigationCardBinding";
|
|
31
31
|
import { NavigationEntry } from "../../types";
|
|
32
32
|
import { cls, defaultBorderMixin } from "@firecms/ui";
|
|
33
|
+
import { useTranslation } from "../../hooks";
|
|
33
34
|
|
|
34
35
|
const animateLayoutChanges: AnimateLayoutChanges = (args) =>
|
|
35
36
|
defaultAnimateLayoutChanges({
|
|
@@ -669,6 +670,7 @@ export function NewGroupDropZone({
|
|
|
669
670
|
disabled: boolean;
|
|
670
671
|
setIsHovering: (v: boolean) => void;
|
|
671
672
|
}) {
|
|
673
|
+
const { t } = useTranslation();
|
|
672
674
|
const {
|
|
673
675
|
setNodeRef,
|
|
674
676
|
isOver
|
|
@@ -709,7 +711,7 @@ export function NewGroupDropZone({
|
|
|
709
711
|
)}>
|
|
710
712
|
<div className="text-center p-4">
|
|
711
713
|
<span className="block font-medium text-sm">
|
|
712
|
-
|
|
714
|
+
{t("drop_here_create_group")}
|
|
713
715
|
</span>
|
|
714
716
|
</div>
|
|
715
717
|
</div>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { PropsWithChildren, useState } from "react";
|
|
2
2
|
import { cls, EditIcon, IconButton, Typography, ExpandablePanel } from "@firecms/ui";
|
|
3
|
+
import { useTranslation } from "../../hooks/useTranslation";
|
|
3
4
|
|
|
4
5
|
export function NavigationGroup({
|
|
5
6
|
children,
|
|
@@ -22,8 +23,9 @@ export function NavigationGroup({
|
|
|
22
23
|
onToggleCollapsed?: () => void;
|
|
23
24
|
}>) {
|
|
24
25
|
|
|
26
|
+
const { t } = useTranslation();
|
|
25
27
|
const [isHovered, setIsHovered] = useState(false);
|
|
26
|
-
const currentGroupName = group ?? "
|
|
28
|
+
const currentGroupName = group ?? t("views_group");
|
|
27
29
|
|
|
28
30
|
// Show caret only when not in preview and there is a toggle handler
|
|
29
31
|
const showCaret = !isPreview && !!onToggleCollapsed;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { useEffect, useRef, useState } from "react";
|
|
2
2
|
import { Button, Dialog, DialogActions, DialogContent, DialogTitle, TextField } from "@firecms/ui";
|
|
3
|
+
import { useTranslation } from "../../hooks/useTranslation";
|
|
3
4
|
|
|
4
5
|
interface RenameGroupDialogProps {
|
|
5
6
|
open: boolean;
|
|
@@ -18,7 +19,8 @@ export function RenameGroupDialog({
|
|
|
18
19
|
}: RenameGroupDialogProps) {
|
|
19
20
|
const [name, setName] = useState(initialName);
|
|
20
21
|
const [error, setError] = useState<string | null>(null);
|
|
21
|
-
const inputRef = useRef<HTMLInputElement | HTMLTextAreaElement | null>(null);
|
|
22
|
+
const inputRef = useRef<HTMLInputElement | HTMLTextAreaElement | null>(null);
|
|
23
|
+
const { t } = useTranslation();
|
|
22
24
|
|
|
23
25
|
useEffect(() => {
|
|
24
26
|
if (open) {
|
|
@@ -38,9 +40,9 @@ export function RenameGroupDialog({
|
|
|
38
40
|
const newName = event.target.value;
|
|
39
41
|
setName(newName);
|
|
40
42
|
if (!newName.trim()) {
|
|
41
|
-
setError("
|
|
43
|
+
setError(t("group_name_empty_error"));
|
|
42
44
|
} else if (existingGroupNames.includes(newName.trim())) {
|
|
43
|
-
setError("
|
|
45
|
+
setError(t("group_name_exists_error"));
|
|
44
46
|
} else {
|
|
45
47
|
setError(null);
|
|
46
48
|
}
|
|
@@ -49,11 +51,11 @@ export function RenameGroupDialog({
|
|
|
49
51
|
const handleSave = () => {
|
|
50
52
|
const trimmedName = name.trim();
|
|
51
53
|
if (!trimmedName) {
|
|
52
|
-
setError("
|
|
54
|
+
setError(t("group_name_empty_error"));
|
|
53
55
|
return;
|
|
54
56
|
}
|
|
55
57
|
if (existingGroupNames.includes(trimmedName)) {
|
|
56
|
-
setError("
|
|
58
|
+
setError(t("group_name_exists_error"));
|
|
57
59
|
return;
|
|
58
60
|
}
|
|
59
61
|
if (!error) {
|
|
@@ -70,9 +72,9 @@ export function RenameGroupDialog({
|
|
|
70
72
|
// because the error state might not have updated if the user types and immediately hits enter.
|
|
71
73
|
let currentError = null;
|
|
72
74
|
if (!trimmedName) {
|
|
73
|
-
currentError = "
|
|
75
|
+
currentError = t("group_name_empty_error");
|
|
74
76
|
} else if (existingGroupNames.includes(trimmedName)) {
|
|
75
|
-
currentError = "
|
|
77
|
+
currentError = t("group_name_exists_error");
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
if (!currentError && trimmedName) {
|
|
@@ -93,14 +95,14 @@ export function RenameGroupDialog({
|
|
|
93
95
|
|
|
94
96
|
return (
|
|
95
97
|
<Dialog open={open}>
|
|
96
|
-
<DialogTitle>
|
|
98
|
+
<DialogTitle>{t("rename_group")}</DialogTitle>
|
|
97
99
|
<DialogContent>
|
|
98
100
|
<TextField
|
|
99
|
-
inputRef={inputRef}
|
|
100
|
-
label="
|
|
101
|
+
inputRef={inputRef}
|
|
102
|
+
label={t("group_name_label")}
|
|
101
103
|
value={name}
|
|
102
104
|
onChange={handleNameChange}
|
|
103
|
-
onKeyDown={handleKeyDown}
|
|
105
|
+
onKeyDown={handleKeyDown}
|
|
104
106
|
error={!!error}
|
|
105
107
|
aria-describedby={error ? "group-name-error" : undefined}
|
|
106
108
|
/>
|
|
@@ -109,11 +111,11 @@ export function RenameGroupDialog({
|
|
|
109
111
|
<DialogActions>
|
|
110
112
|
<Button onClick={onClose}
|
|
111
113
|
variant="text">
|
|
112
|
-
|
|
114
|
+
{t("cancel")}
|
|
113
115
|
</Button>
|
|
114
116
|
<Button onClick={handleSave}
|
|
115
117
|
disabled={!!error || !name.trim()}>
|
|
116
|
-
|
|
118
|
+
{t("save")}
|
|
117
119
|
</Button>
|
|
118
120
|
</DialogActions>
|
|
119
121
|
</Dialog>
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { CheckIcon, IconButton, Menu, MenuItem, TranslateIcon, Typography } from "@firecms/ui";
|
|
3
|
+
import { useTranslation } from "../hooks";
|
|
4
|
+
|
|
5
|
+
export function LanguageToggle() {
|
|
6
|
+
const { i18n } = useTranslation();
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<Menu
|
|
10
|
+
trigger={<IconButton
|
|
11
|
+
color="inherit"
|
|
12
|
+
aria-label="Change language">
|
|
13
|
+
<TranslateIcon size="small" />
|
|
14
|
+
</IconButton>}>
|
|
15
|
+
<MenuItem onClick={() => i18n.changeLanguage("en")}>
|
|
16
|
+
<div className="flex w-full items-center justify-between gap-4">
|
|
17
|
+
{/* eslint-disable-next-line i18next/no-literal-string */}
|
|
18
|
+
<Typography variant="body2" className={i18n.language === "en" ? "font-bold" : ""}>English</Typography>
|
|
19
|
+
{i18n.language === "en" && <CheckIcon size="small" />}
|
|
20
|
+
</div>
|
|
21
|
+
</MenuItem>
|
|
22
|
+
<MenuItem onClick={() => i18n.changeLanguage("es")}>
|
|
23
|
+
<div className="flex w-full items-center justify-between gap-4">
|
|
24
|
+
{/* eslint-disable-next-line i18next/no-literal-string */}
|
|
25
|
+
<Typography variant="body2" className={i18n.language === "es" ? "font-bold" : ""}>Español</Typography>
|
|
26
|
+
{i18n.language === "es" && <CheckIcon size="small" />}
|
|
27
|
+
</div>
|
|
28
|
+
</MenuItem>
|
|
29
|
+
<MenuItem onClick={() => i18n.changeLanguage("de")}>
|
|
30
|
+
<div className="flex w-full items-center justify-between gap-4">
|
|
31
|
+
{/* eslint-disable-next-line i18next/no-literal-string */}
|
|
32
|
+
<Typography variant="body2" className={i18n.language === "de" ? "font-bold" : ""}>Deutsch</Typography>
|
|
33
|
+
{i18n.language === "de" && <CheckIcon size="small" />}
|
|
34
|
+
</div>
|
|
35
|
+
</MenuItem>
|
|
36
|
+
<MenuItem onClick={() => i18n.changeLanguage("fr")}>
|
|
37
|
+
<div className="flex w-full items-center justify-between gap-4">
|
|
38
|
+
{/* eslint-disable-next-line i18next/no-literal-string */}
|
|
39
|
+
<Typography variant="body2" className={i18n.language === "fr" ? "font-bold" : ""}>Français</Typography>
|
|
40
|
+
{i18n.language === "fr" && <CheckIcon size="small" />}
|
|
41
|
+
</div>
|
|
42
|
+
</MenuItem>
|
|
43
|
+
<MenuItem onClick={() => i18n.changeLanguage("it")}>
|
|
44
|
+
<div className="flex w-full items-center justify-between gap-4">
|
|
45
|
+
{/* eslint-disable-next-line i18next/no-literal-string */}
|
|
46
|
+
<Typography variant="body2" className={i18n.language === "it" ? "font-bold" : ""}>Italiano</Typography>
|
|
47
|
+
{i18n.language === "it" && <CheckIcon size="small" />}
|
|
48
|
+
</div>
|
|
49
|
+
</MenuItem>
|
|
50
|
+
<MenuItem onClick={() => i18n.changeLanguage("hi")}>
|
|
51
|
+
<div className="flex w-full items-center justify-between gap-4">
|
|
52
|
+
{/* eslint-disable-next-line i18next/no-literal-string */}
|
|
53
|
+
<Typography variant="body2" className={i18n.language === "hi" ? "font-bold" : ""}>हिन्दी</Typography>
|
|
54
|
+
{i18n.language === "hi" && <CheckIcon size="small" />}
|
|
55
|
+
</div>
|
|
56
|
+
</MenuItem>
|
|
57
|
+
<MenuItem onClick={() => i18n.changeLanguage("pt")}>
|
|
58
|
+
<div className="flex w-full items-center justify-between gap-4">
|
|
59
|
+
{/* eslint-disable-next-line i18next/no-literal-string */}
|
|
60
|
+
<Typography variant="body2" className={i18n.language === "pt" ? "font-bold" : ""}>Português</Typography>
|
|
61
|
+
{i18n.language === "pt" && <CheckIcon size="small" />}
|
|
62
|
+
</div>
|
|
63
|
+
</MenuItem>
|
|
64
|
+
</Menu>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Link } from "react-router-dom";
|
|
3
3
|
import { Button, Typography } from "@firecms/ui";
|
|
4
|
+
import { useTranslation } from "../hooks/useTranslation";
|
|
4
5
|
|
|
5
6
|
export function NotFoundPage() {
|
|
7
|
+
const { t } = useTranslation();
|
|
6
8
|
|
|
7
9
|
return (
|
|
8
10
|
<div className="flex w-full h-full">
|
|
@@ -10,15 +12,15 @@ export function NotFoundPage() {
|
|
|
10
12
|
>
|
|
11
13
|
<Typography variant={"h4"} align={"center"}
|
|
12
14
|
gutterBottom={true}>
|
|
13
|
-
|
|
15
|
+
{t("page_not_found")}
|
|
14
16
|
</Typography>
|
|
15
17
|
<Typography align={"center"} gutterBottom={true}>
|
|
16
|
-
|
|
18
|
+
{t("page_not_found_body")}
|
|
17
19
|
</Typography>
|
|
18
20
|
<Button
|
|
19
21
|
variant={"text"}
|
|
20
22
|
component={Link}
|
|
21
|
-
to={"/"}>
|
|
23
|
+
to={"/"}>{t("back_to_home")}</Button>
|
|
22
24
|
</div>
|
|
23
25
|
</div>
|
|
24
26
|
);
|