@firecms/core 3.3.0-canary.3afa809 → 3.3.0-canary.42ba35b
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/app/useApp.d.ts +1 -0
- package/dist/components/EntityCollectionTable/column_utils.d.ts +4 -3
- package/dist/components/EntityCollectionView/FiltersDialog.d.ts +2 -1
- package/dist/components/EntityPreview.d.ts +3 -1
- package/dist/core/DefaultDrawer.d.ts +10 -3
- package/dist/core/field_configs.d.ts +1 -1
- package/dist/form/field_bindings/GeopointFieldBinding.d.ts +5 -0
- package/dist/form/index.d.ts +1 -0
- package/dist/index.es.js +2042 -728
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +2041 -727
- package/dist/index.umd.js.map +1 -1
- package/dist/locales/pl.d.ts +2 -0
- package/dist/preview/index.d.ts +1 -0
- package/dist/preview/property_previews/GeopointPropertyPreview.d.ts +4 -0
- package/dist/types/collections.d.ts +17 -0
- package/dist/types/translations.d.ts +9 -1
- package/dist/util/entities.d.ts +1 -0
- package/dist/util/geopoint.d.ts +22 -0
- package/dist/util/index.d.ts +1 -0
- package/dist/util/navigation_blocking.d.ts +22 -0
- package/dist/util/navigation_from_path.d.ts +10 -0
- package/dist/util/navigation_utils.d.ts +20 -0
- package/package.json +16 -9
- package/src/app/Scaffold.tsx +34 -44
- package/src/app/useApp.tsx +1 -0
- package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +2 -1
- package/src/components/EntityCollectionTable/column_utils.tsx +11 -19
- package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +1 -1
- package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +4 -1
- package/src/components/EntityCollectionView/FiltersDialog.tsx +39 -28
- package/src/components/EntityPreview.tsx +41 -40
- package/src/components/ReferenceWidget.tsx +1 -1
- package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +2 -2
- package/src/components/VirtualTable/VirtualTable.tsx +19 -8
- package/src/components/common/useDataSourceTableController.tsx +66 -10
- package/src/core/DefaultDrawer.tsx +150 -64
- package/src/core/DrawerNavigationGroup.tsx +27 -29
- package/src/core/DrawerNavigationItem.tsx +10 -12
- package/src/core/EntityEditView.tsx +57 -32
- package/src/core/field_configs.tsx +15 -0
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +1 -1
- package/src/form/field_bindings/GeopointFieldBinding.tsx +139 -0
- package/src/form/index.tsx +1 -0
- package/src/i18n/FireCMSi18nProvider.tsx +2 -0
- package/src/internal/useBuildSideEntityController.tsx +2 -1
- package/src/locales/de.ts +11 -3
- package/src/locales/en.ts +11 -3
- package/src/locales/es.ts +11 -3
- package/src/locales/fr.ts +11 -3
- package/src/locales/hi.ts +11 -3
- package/src/locales/it.ts +11 -3
- package/src/locales/pl.ts +730 -0
- package/src/locales/pt.ts +11 -3
- package/src/preview/PropertyPreview.tsx +12 -0
- package/src/preview/index.ts +1 -0
- package/src/preview/property_previews/GeopointPropertyPreview.tsx +23 -0
- package/src/routes/FireCMSRoute.tsx +44 -25
- package/src/types/collections.ts +18 -0
- package/src/types/translations.ts +9 -1
- package/src/util/entities.ts +13 -0
- package/src/util/geopoint.ts +81 -0
- package/src/util/index.ts +1 -0
- package/src/util/navigation_blocking.ts +45 -0
- package/src/util/navigation_from_path.ts +23 -6
- package/src/util/navigation_utils.ts +36 -2
- package/src/util/parent_references_from_path.ts +4 -2
|
@@ -62,43 +62,41 @@ export function DrawerNavigationGroup({
|
|
|
62
62
|
const { t } = useTranslation();
|
|
63
63
|
return (
|
|
64
64
|
<div
|
|
65
|
-
className={"
|
|
65
|
+
className={"my-2 mx-2 flex flex-col"}
|
|
66
66
|
key={`drawer_group_${group}`}
|
|
67
67
|
>
|
|
68
68
|
{/* Group Header */}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
/>
|
|
81
|
-
<Typography
|
|
82
|
-
variant={"caption"}
|
|
83
|
-
color={"secondary"}
|
|
84
|
-
className="font-medium flex-grow line-clamp-1"
|
|
85
|
-
>
|
|
86
|
-
{(group && group !== "__default__" ? group : t("views_group")).toUpperCase()}
|
|
87
|
-
</Typography>
|
|
88
|
-
{headerActions && (
|
|
89
|
-
<div onClick={(e) => e.stopPropagation()}>
|
|
90
|
-
{headerActions}
|
|
91
|
-
</div>
|
|
69
|
+
<div
|
|
70
|
+
className={cls("pl-3 pr-2 py-0.5 flex flex-row items-center transition-colors",
|
|
71
|
+
drawerOpen ? "cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-800/40 rounded-lg" : "opacity-0 invisible pointer-events-none"
|
|
72
|
+
)}
|
|
73
|
+
onClick={drawerOpen ? onToggleCollapsed : undefined}
|
|
74
|
+
>
|
|
75
|
+
<ExpandMoreIcon
|
|
76
|
+
size={"smallest"}
|
|
77
|
+
className={cls(
|
|
78
|
+
"text-surface-400 dark:text-surface-400 transition-transform duration-200 mr-1",
|
|
79
|
+
collapsed ? "-rotate-90" : "rotate-0"
|
|
92
80
|
)}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
81
|
+
/>
|
|
82
|
+
<Typography
|
|
83
|
+
variant={"caption"}
|
|
84
|
+
color={"secondary"}
|
|
85
|
+
className="font-semibold text-[11px] uppercase tracking-wider flex-grow line-clamp-1 text-surface-400 dark:text-surface-400"
|
|
86
|
+
>
|
|
87
|
+
{(group && group !== "__default__" ? group : t("views_group"))}
|
|
88
|
+
</Typography>
|
|
89
|
+
{headerActions && (
|
|
90
|
+
<div onClick={(e) => e.stopPropagation()}>
|
|
91
|
+
{headerActions}
|
|
92
|
+
</div>
|
|
93
|
+
)}
|
|
94
|
+
</div>
|
|
97
95
|
|
|
98
96
|
{/* Collapsible Content */}
|
|
99
97
|
<div
|
|
100
98
|
className={cls(
|
|
101
|
-
"
|
|
99
|
+
"transition-all duration-200 ease-in-out overflow-hidden rounded-lg",
|
|
102
100
|
collapsed ? "max-h-0 opacity-0" : "max-h-[2000px] opacity-100"
|
|
103
101
|
)}
|
|
104
102
|
>
|
|
@@ -22,7 +22,7 @@ export function DrawerNavigationItem({
|
|
|
22
22
|
}) {
|
|
23
23
|
|
|
24
24
|
const iconWrap = <div
|
|
25
|
-
className={"
|
|
25
|
+
className={"shrink-0 flex items-center justify-center w-[44px] h-[30px] text-surface-500 dark:text-text-secondary-dark group-hover/nav:text-primary transition-colors duration-150"}>
|
|
26
26
|
{icon}
|
|
27
27
|
</div>;
|
|
28
28
|
|
|
@@ -33,14 +33,12 @@ export function DrawerNavigationItem({
|
|
|
33
33
|
width: "100%",
|
|
34
34
|
transition: drawerOpen ? "width 150ms ease-in" : undefined
|
|
35
35
|
}}
|
|
36
|
-
className={({ isActive }:
|
|
37
|
-
"hover:bg-
|
|
38
|
-
"flex flex-row items-center
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
"font-semibold text-xs",
|
|
43
|
-
isActive ? "bg-surface-accent-200 bg-opacity-60 dark:bg-surface-800 dark:bg-opacity-50 bg-surface-accent-200/60 dark:bg-surface-800/50" : ""
|
|
36
|
+
className={({ isActive }: { isActive: boolean }) => cls("rounded-lg truncate group/nav",
|
|
37
|
+
"hover:bg-primary/5 dark:hover:bg-primary/5 text-surface-700 dark:text-surface-300 hover:text-surface-900 dark:hover:text-white",
|
|
38
|
+
"flex flex-row items-center",
|
|
39
|
+
drawerOpen ? "pr-4 h-[30px]" : "h-[30px]",
|
|
40
|
+
"font-medium text-[13px]",
|
|
41
|
+
isActive ? "bg-primary/8 dark:bg-primary/10 text-primary dark:text-primary [&_div]:text-primary" : ""
|
|
44
42
|
)}
|
|
45
43
|
to={url}
|
|
46
44
|
>
|
|
@@ -49,11 +47,11 @@ export function DrawerNavigationItem({
|
|
|
49
47
|
|
|
50
48
|
<div
|
|
51
49
|
className={cls(
|
|
52
|
-
"text-
|
|
50
|
+
"text-surface-700 dark:text-surface-300",
|
|
53
51
|
drawerOpen ? "opacity-100" : "opacity-0 hidden",
|
|
54
|
-
"
|
|
52
|
+
"font-inherit truncate space-x-2"
|
|
55
53
|
)}>
|
|
56
|
-
{name
|
|
54
|
+
{name}
|
|
57
55
|
</div>
|
|
58
56
|
</NavLink>
|
|
59
57
|
</div>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useEffect, useMemo, useState } from "react";
|
|
1
|
+
import React, { useEffect, useMemo, useRef, useState } from "react";
|
|
2
2
|
import {
|
|
3
3
|
Entity,
|
|
4
4
|
EntityCollection,
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
import { CircularProgressCenter, EntityCollectionView, EntityView, ErrorBoundary } from "../components";
|
|
14
14
|
import {
|
|
15
15
|
canEditEntity,
|
|
16
|
+
encodeEntityId,
|
|
16
17
|
removeInitialAndTrailingSlashes,
|
|
17
18
|
resolveCollection,
|
|
18
19
|
resolveDefaultSelectedView,
|
|
@@ -246,6 +247,47 @@ export function EntityEditViewInner<M extends Record<string, any>>({
|
|
|
246
247
|
|
|
247
248
|
const authController = useAuthController();
|
|
248
249
|
|
|
250
|
+
// Track which tabs have been visited so we keep them mounted (preserving
|
|
251
|
+
// their state) without eagerly mounting tabs that were never opened.
|
|
252
|
+
const mountedTabsRef = useRef<Set<string>>(new Set());
|
|
253
|
+
if (selectedTab) {
|
|
254
|
+
mountedTabsRef.current.add(selectedTab);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Memoize the read-only fallback form context so it is not recreated on
|
|
258
|
+
// every render when there is no real formContext (read-only entity views).
|
|
259
|
+
const readOnlyFormContext = useMemo<FormContext | undefined>(() => {
|
|
260
|
+
if (formContext) return undefined; // real formContext takes precedence
|
|
261
|
+
if (!entityId) return undefined;
|
|
262
|
+
const formexStub = createFormexStub<M>(usedEntity?.values ?? {} as M);
|
|
263
|
+
return {
|
|
264
|
+
entityId,
|
|
265
|
+
disabled: false,
|
|
266
|
+
openEntityMode: layout,
|
|
267
|
+
status: status,
|
|
268
|
+
values: usedEntity?.values ?? {},
|
|
269
|
+
setFieldValue: (key: string, value: any) => {
|
|
270
|
+
throw new Error("You can't update values in read only mode");
|
|
271
|
+
},
|
|
272
|
+
save: () => {
|
|
273
|
+
throw new Error("You can't save in read only mode");
|
|
274
|
+
},
|
|
275
|
+
collection: resolveCollection<M>({
|
|
276
|
+
collection,
|
|
277
|
+
path,
|
|
278
|
+
entityId,
|
|
279
|
+
values: usedEntity?.values ?? {},
|
|
280
|
+
previousValues: usedEntity?.values ?? {},
|
|
281
|
+
propertyConfigs: customizationController.propertyConfigs,
|
|
282
|
+
authController
|
|
283
|
+
}),
|
|
284
|
+
path,
|
|
285
|
+
entity: usedEntity,
|
|
286
|
+
savingError: undefined,
|
|
287
|
+
formex: formexStub
|
|
288
|
+
};
|
|
289
|
+
}, [formContext, entityId, layout, status, usedEntity, collection, path, customizationController.propertyConfigs, authController]);
|
|
290
|
+
|
|
249
291
|
const customViewsView: React.ReactNode[] | undefined = customViews && resolvedEntityViews
|
|
250
292
|
.filter(e => !e.includeActions)
|
|
251
293
|
.map((customView) => {
|
|
@@ -262,38 +304,18 @@ export function EntityEditViewInner<M extends Record<string, any>>({
|
|
|
262
304
|
return null;
|
|
263
305
|
}
|
|
264
306
|
|
|
265
|
-
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
setFieldValue: (key: string, value: any) => {
|
|
273
|
-
throw new Error("You can't update values in read only mode");
|
|
274
|
-
},
|
|
275
|
-
save: () => {
|
|
276
|
-
throw new Error("You can't save in read only mode");
|
|
277
|
-
},
|
|
278
|
-
collection: resolveCollection<M>({
|
|
279
|
-
collection,
|
|
280
|
-
path,
|
|
281
|
-
entityId,
|
|
282
|
-
values: usedEntity?.values ?? {},
|
|
283
|
-
previousValues: usedEntity?.values ?? {},
|
|
284
|
-
propertyConfigs: customizationController.propertyConfigs,
|
|
285
|
-
authController
|
|
286
|
-
}),
|
|
287
|
-
path,
|
|
288
|
-
entity: usedEntity,
|
|
289
|
-
savingError: undefined,
|
|
290
|
-
formex: formexStub
|
|
291
|
-
};
|
|
307
|
+
// Only mount tabs that are active or have been visited before.
|
|
308
|
+
const isActive = selectedTab === customView.key;
|
|
309
|
+
if (!isActive && !mountedTabsRef.current.has(customView.key)) {
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const usedFormContext: FormContext = formContext ?? readOnlyFormContext!;
|
|
292
314
|
|
|
293
315
|
return <div
|
|
294
316
|
className={cls(defaultBorderMixin,
|
|
295
317
|
"relative flex-1 w-full h-full overflow-auto",
|
|
296
|
-
{ "hidden":
|
|
318
|
+
{ "hidden": !isActive }
|
|
297
319
|
)}
|
|
298
320
|
key={`custom_view_${customView.key}`}
|
|
299
321
|
role="tabpanel">
|
|
@@ -311,7 +333,8 @@ export function EntityEditViewInner<M extends Record<string, any>>({
|
|
|
311
333
|
|
|
312
334
|
const globalLoading = dataLoading && !usedEntity;
|
|
313
335
|
|
|
314
|
-
|
|
336
|
+
// Only mount the JSON view once its tab has been selected at least once.
|
|
337
|
+
const jsonView = (selectedTab === JSON_TAB_VALUE || mountedTabsRef.current.has(JSON_TAB_VALUE)) ? <div
|
|
315
338
|
className={cls("relative flex-1 h-full overflow-auto w-full",
|
|
316
339
|
{ "hidden": selectedTab !== JSON_TAB_VALUE })}
|
|
317
340
|
key={"json_view"}
|
|
@@ -320,12 +343,14 @@ export function EntityEditViewInner<M extends Record<string, any>>({
|
|
|
320
343
|
<EntityJsonPreview
|
|
321
344
|
values={formContext?.values ?? entity?.values ?? {}} />
|
|
322
345
|
</ErrorBoundary>
|
|
323
|
-
</div
|
|
346
|
+
</div> : null;
|
|
324
347
|
|
|
325
348
|
const subCollectionsViews = subcollections && subcollections.map((subcollection) => {
|
|
326
349
|
const subcollectionId = subcollection.id ?? subcollection.path;
|
|
350
|
+
// `newFullPath` addresses the datasource, so the parent id stays raw.
|
|
327
351
|
const newFullPath = usedEntity ? `${path}/${usedEntity?.id}/${removeInitialAndTrailingSlashes(subcollection.path)}` : undefined;
|
|
328
|
-
|
|
352
|
+
// `newFullIdPath` is fed to buildUrlCollectionPath downstream, so it stays escaped.
|
|
353
|
+
const newFullIdPath = fullIdPath && usedEntity ? `${fullIdPath}/${encodeEntityId(usedEntity.id)}/${removeInitialAndTrailingSlashes(subcollectionId)}` : undefined;
|
|
329
354
|
|
|
330
355
|
if (selectedTab !== subcollectionId) return null;
|
|
331
356
|
return (
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
ArrayOfReferencesFieldBinding,
|
|
7
7
|
BlockFieldBinding,
|
|
8
8
|
DateTimeFieldBinding,
|
|
9
|
+
GeopointFieldBinding,
|
|
9
10
|
KeyValueFieldBinding,
|
|
10
11
|
MapFieldBinding,
|
|
11
12
|
MultiSelectFieldBinding,
|
|
@@ -34,6 +35,7 @@ import {
|
|
|
34
35
|
ListAltIcon,
|
|
35
36
|
ListIcon,
|
|
36
37
|
MailIcon,
|
|
38
|
+
LocationOnIcon,
|
|
37
39
|
NumbersIcon, PersonIcon,
|
|
38
40
|
RepeatIcon,
|
|
39
41
|
ScheduleIcon,
|
|
@@ -273,6 +275,17 @@ export const DEFAULT_FIELD_CONFIGS: Record<string, PropertyConfig<any>> = {
|
|
|
273
275
|
Field: DateTimeFieldBinding
|
|
274
276
|
}
|
|
275
277
|
},
|
|
278
|
+
geopoint: {
|
|
279
|
+
key: "geopoint",
|
|
280
|
+
name: "Geopoint",
|
|
281
|
+
description: "Latitude and longitude pair",
|
|
282
|
+
Icon: LocationOnIcon,
|
|
283
|
+
color: "#0ea5e9",
|
|
284
|
+
property: {
|
|
285
|
+
dataType: "geopoint",
|
|
286
|
+
Field: GeopointFieldBinding
|
|
287
|
+
}
|
|
288
|
+
},
|
|
276
289
|
group: {
|
|
277
290
|
key: "group",
|
|
278
291
|
name: "Group",
|
|
@@ -419,6 +432,8 @@ export function getDefaultFieldId(property: Property | ResolvedProperty) {
|
|
|
419
432
|
return "switch";
|
|
420
433
|
} else if (property.dataType === "date") {
|
|
421
434
|
return "date_time";
|
|
435
|
+
} else if (property.dataType === "geopoint") {
|
|
436
|
+
return "geopoint";
|
|
422
437
|
} else if (property.dataType === "reference") {
|
|
423
438
|
return "reference";
|
|
424
439
|
}
|
|
@@ -58,7 +58,7 @@ export function ArrayOfReferencesFieldBinding({
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
const onMultipleEntitiesSelected = useCallback((entities: Entity<any>[]) => {
|
|
61
|
-
setValue(entities.map(e => getReferenceFrom(e)));
|
|
61
|
+
setValue(entities.filter(Boolean).map(e => getReferenceFrom(e)));
|
|
62
62
|
}, [setValue]);
|
|
63
63
|
|
|
64
64
|
const referenceDialogController = useReferenceDialog({
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
2
|
+
|
|
3
|
+
import { CloseIcon, IconButton, TextField } from "@firecms/ui";
|
|
4
|
+
import { FieldProps, GeoPoint } from "../../types";
|
|
5
|
+
import { FieldHelperText, LabelWithIcon } from "../components";
|
|
6
|
+
import { PropertyIdCopyTooltip } from "../../components";
|
|
7
|
+
import { useClearRestoreValue } from "../useClearRestoreValue";
|
|
8
|
+
import { getIconForProperty } from "../../util";
|
|
9
|
+
import { formatGeoPoint, getGeoPointCoordinates, parseGeoPoint } from "../../util/geopoint";
|
|
10
|
+
|
|
11
|
+
type GeopointFieldBindingProps = FieldProps<GeoPoint>;
|
|
12
|
+
|
|
13
|
+
export function GeopointFieldBinding({
|
|
14
|
+
propertyKey,
|
|
15
|
+
value,
|
|
16
|
+
setValue,
|
|
17
|
+
error,
|
|
18
|
+
showError,
|
|
19
|
+
disabled,
|
|
20
|
+
autoFocus,
|
|
21
|
+
property,
|
|
22
|
+
includeDescription,
|
|
23
|
+
size = "large"
|
|
24
|
+
}: GeopointFieldBindingProps) {
|
|
25
|
+
|
|
26
|
+
const coordinates = getGeoPointCoordinates(value);
|
|
27
|
+
const canClear = Boolean((property as any).clearable);
|
|
28
|
+
const [latitude, setLatitude] = useState<string>(coordinates ? coordinates.latitude.toString() : "");
|
|
29
|
+
const [longitude, setLongitude] = useState<string>(coordinates ? coordinates.longitude.toString() : "");
|
|
30
|
+
const [localError, setLocalError] = useState<string | undefined>();
|
|
31
|
+
const skipSyncRef = useRef(false);
|
|
32
|
+
|
|
33
|
+
useClearRestoreValue({
|
|
34
|
+
property,
|
|
35
|
+
value,
|
|
36
|
+
setValue
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (skipSyncRef.current) {
|
|
41
|
+
skipSyncRef.current = false;
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const nextCoordinates = getGeoPointCoordinates(value);
|
|
45
|
+
setLatitude(nextCoordinates ? nextCoordinates.latitude.toString() : "");
|
|
46
|
+
setLongitude(nextCoordinates ? nextCoordinates.longitude.toString() : "");
|
|
47
|
+
}, [value]);
|
|
48
|
+
|
|
49
|
+
const updateGeoPoint = (nextLatitude: string, nextLongitude: string) => {
|
|
50
|
+
skipSyncRef.current = true;
|
|
51
|
+
setLatitude(nextLatitude);
|
|
52
|
+
setLongitude(nextLongitude);
|
|
53
|
+
|
|
54
|
+
const trimmedLatitude = nextLatitude.trim();
|
|
55
|
+
const trimmedLongitude = nextLongitude.trim();
|
|
56
|
+
|
|
57
|
+
if (!trimmedLatitude && !trimmedLongitude) {
|
|
58
|
+
setLocalError(undefined);
|
|
59
|
+
setValue(null);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const parsed = parseGeoPoint(`${trimmedLatitude}, ${trimmedLongitude}`);
|
|
64
|
+
|
|
65
|
+
if (parsed.error) {
|
|
66
|
+
setLocalError(parsed.error);
|
|
67
|
+
setValue(null);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
setLocalError(undefined);
|
|
72
|
+
setValue(parsed.point);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const handleClear = (event?: React.MouseEvent) => {
|
|
76
|
+
if (event) {
|
|
77
|
+
event.preventDefault();
|
|
78
|
+
event.stopPropagation();
|
|
79
|
+
}
|
|
80
|
+
updateGeoPoint("", "");
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const resolvedError = localError ?? error;
|
|
84
|
+
const shouldShowError = Boolean(resolvedError) || Boolean(showError && error);
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<>
|
|
88
|
+
<PropertyIdCopyTooltip propertyKey={propertyKey}>
|
|
89
|
+
<div className="flex flex-col gap-2">
|
|
90
|
+
<div className="mt-1">
|
|
91
|
+
<LabelWithIcon
|
|
92
|
+
icon={getIconForProperty(property, "small")}
|
|
93
|
+
required={property.validation?.required}
|
|
94
|
+
title={property.name}
|
|
95
|
+
className={shouldShowError ? "text-red-500 dark:text-red-500" : "text-text-secondary dark:text-text-secondary-dark"}
|
|
96
|
+
/>
|
|
97
|
+
</div>
|
|
98
|
+
<div className="grid grid-cols-1 gap-2 md:grid-cols-2">
|
|
99
|
+
<TextField
|
|
100
|
+
size={size}
|
|
101
|
+
value={latitude}
|
|
102
|
+
onChange={(event) => updateGeoPoint(event.target.value, longitude)}
|
|
103
|
+
autoFocus={autoFocus}
|
|
104
|
+
label={"Latitude"}
|
|
105
|
+
type="number"
|
|
106
|
+
disabled={disabled}
|
|
107
|
+
endAdornment={canClear ? (
|
|
108
|
+
<IconButton onClick={handleClear}>
|
|
109
|
+
<CloseIcon />
|
|
110
|
+
</IconButton>
|
|
111
|
+
) : undefined}
|
|
112
|
+
error={shouldShowError && Boolean(resolvedError)}
|
|
113
|
+
/>
|
|
114
|
+
<TextField
|
|
115
|
+
size={size}
|
|
116
|
+
value={longitude}
|
|
117
|
+
onChange={(event) => updateGeoPoint(latitude, event.target.value)}
|
|
118
|
+
label={"Longitude"}
|
|
119
|
+
type="number"
|
|
120
|
+
disabled={disabled}
|
|
121
|
+
error={shouldShowError && Boolean(resolvedError)}
|
|
122
|
+
/>
|
|
123
|
+
</div>
|
|
124
|
+
{value && !resolvedError && (
|
|
125
|
+
<div className="text-xs text-text-secondary dark:text-text-secondary-dark font-mono">
|
|
126
|
+
{formatGeoPoint(value)}
|
|
127
|
+
</div>
|
|
128
|
+
)}
|
|
129
|
+
</div>
|
|
130
|
+
</PropertyIdCopyTooltip>
|
|
131
|
+
|
|
132
|
+
<FieldHelperText includeDescription={includeDescription}
|
|
133
|
+
showError={shouldShowError}
|
|
134
|
+
error={resolvedError}
|
|
135
|
+
disabled={disabled}
|
|
136
|
+
property={property}/>
|
|
137
|
+
</>
|
|
138
|
+
);
|
|
139
|
+
}
|
package/src/form/index.tsx
CHANGED
|
@@ -11,6 +11,7 @@ export { StorageUploadFieldBinding } from "./field_bindings/StorageUploadFieldBi
|
|
|
11
11
|
export { TextFieldBinding } from "./field_bindings/TextFieldBinding";
|
|
12
12
|
export { SwitchFieldBinding } from "./field_bindings/SwitchFieldBinding";
|
|
13
13
|
export { DateTimeFieldBinding } from "./field_bindings/DateTimeFieldBinding";
|
|
14
|
+
export { GeopointFieldBinding } from "./field_bindings/GeopointFieldBinding";
|
|
14
15
|
export { ReferenceFieldBinding } from "./field_bindings/ReferenceFieldBinding";
|
|
15
16
|
export { ReferenceAsStringFieldBinding } from "./field_bindings/ReferenceAsStringFieldBinding";
|
|
16
17
|
export { MapFieldBinding } from "./field_bindings/MapFieldBinding";
|
|
@@ -8,6 +8,7 @@ import { fr } from "../locales/fr";
|
|
|
8
8
|
import { it } from "../locales/it";
|
|
9
9
|
import { hi } from "../locales/hi";
|
|
10
10
|
import { pt } from "../locales/pt";
|
|
11
|
+
import { pl } from "../locales/pl";
|
|
11
12
|
import { FireCMSTranslations } from "../types/translations";
|
|
12
13
|
|
|
13
14
|
const FIRECMS_NS = "firecms_core";
|
|
@@ -139,6 +140,7 @@ function buildResources(
|
|
|
139
140
|
it: { [FIRECMS_NS]: { ...it } },
|
|
140
141
|
hi: { [FIRECMS_NS]: { ...hi } },
|
|
141
142
|
pt: { [FIRECMS_NS]: { ...pt } },
|
|
143
|
+
pl: { [FIRECMS_NS]: { ...pl } },
|
|
142
144
|
};
|
|
143
145
|
|
|
144
146
|
if (!translations) return resources;
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
import { getNavigationEntriesFromPath, NavigationViewInternal } from "../util/navigation_from_path";
|
|
14
14
|
import { useLocation } from "react-router-dom";
|
|
15
15
|
import {
|
|
16
|
+
encodeEntityId,
|
|
16
17
|
removeInitialAndTrailingSlashes,
|
|
17
18
|
resolveCollection,
|
|
18
19
|
resolveDefaultSelectedView,
|
|
@@ -277,7 +278,7 @@ const propsToSidePanel = (props: EntitySidePanelProps,
|
|
|
277
278
|
const collectionPath = removeInitialAndTrailingSlashes(props.path);
|
|
278
279
|
|
|
279
280
|
const urlPath = props.entityId
|
|
280
|
-
? buildUrlCollectionPath(`${collectionPath}/${props.entityId}${props.selectedTab ? "/" + props.selectedTab : ""}${locationSearch}#${SIDE_URL_HASH}`)
|
|
281
|
+
? buildUrlCollectionPath(`${collectionPath}/${encodeEntityId(props.entityId)}${props.selectedTab ? "/" + props.selectedTab : ""}${locationSearch}#${SIDE_URL_HASH}`)
|
|
281
282
|
: buildUrlCollectionPath(`${collectionPath}${locationSearch}#${NEW_URL_HASH}`);
|
|
282
283
|
|
|
283
284
|
const resolvedPanelProps: EntitySidePanelProps<any> = {
|
package/src/locales/de.ts
CHANGED
|
@@ -146,8 +146,8 @@ export const de: FireCMSTranslations = {
|
|
|
146
146
|
// ─── Error states ─────────────────────────────────────────────
|
|
147
147
|
error: "Fehler",
|
|
148
148
|
error_uploading_file: "Fehler beim Hochladen der Datei",
|
|
149
|
-
error_deleting: "Fehler beim Löschen",
|
|
150
|
-
error_before_delete: "Fehler vor dem Löschen",
|
|
149
|
+
error_deleting: "Fehler beim Löschen: {{message}}",
|
|
150
|
+
error_before_delete: "Fehler vor dem Löschen: {{message}}",
|
|
151
151
|
error_updating_asset: "Fehler beim Aktualisieren des Assets",
|
|
152
152
|
error_deleting_asset: "Fehler beim Löschen des Assets",
|
|
153
153
|
error_firestore_index: "Für diese Abfrage ist ein Firestore-Index erforderlich.",
|
|
@@ -514,10 +514,18 @@ export const de: FireCMSTranslations = {
|
|
|
514
514
|
auto_setup_collections_button: "Sammlungen automatisch einrichten",
|
|
515
515
|
auto_setup_collections_title: "Sammlungen automatisch einrichten?",
|
|
516
516
|
auto_setup_collections_desc: "Dadurch werden automatisch Sammlungskonfigurationen für Sammlungen erstellt, die noch <b>NICHT</b> zugeordnet sind.",
|
|
517
|
-
|
|
517
|
+
setting_up_collections: "Sammlungen werden eingerichtet",
|
|
518
|
+
setting_up_collection: "{{name}} wird eingerichtet",
|
|
518
519
|
no_collections_found_to_setup: "Keine einzurichtenden Sammlungen gefunden",
|
|
519
520
|
collections_have_been_setup: "Sammlungen wurden automatisch eingerichtet",
|
|
520
521
|
error_setting_up_collections: "Fehler beim automatischen Einrichten der Sammlungen",
|
|
522
|
+
setup_collections_title: "Set up collections",
|
|
523
|
+
setup_collections_select_desc: "Select which collections to automatically set up:",
|
|
524
|
+
select_all: "Select all",
|
|
525
|
+
deselect_all: "Deselect all",
|
|
526
|
+
setup_collections_confirm: "Set up ({{count}})",
|
|
527
|
+
collection_setup_success: "{{name}} has been set up",
|
|
528
|
+
go_to_collection: "Go to collection",
|
|
521
529
|
add_your: "Fügen Sie Ihre",
|
|
522
530
|
database_collections: "Datenbanksammlungen",
|
|
523
531
|
to_firecms: "zu FireCMS hinzu",
|
package/src/locales/en.ts
CHANGED
|
@@ -154,8 +154,8 @@ export const en: FireCMSTranslations = {
|
|
|
154
154
|
// ─── Error states ─────────────────────────────────────────────
|
|
155
155
|
error: "Error",
|
|
156
156
|
error_uploading_file: "Error uploading file",
|
|
157
|
-
error_deleting: "Error deleting",
|
|
158
|
-
error_before_delete: "Error before delete",
|
|
157
|
+
error_deleting: "Error deleting: {{message}}",
|
|
158
|
+
error_before_delete: "Error before delete: {{message}}",
|
|
159
159
|
error_updating_asset: "Error updating asset",
|
|
160
160
|
error_deleting_asset: "Error deleting asset",
|
|
161
161
|
error_firestore_index: "A Firestore index is required for this query.",
|
|
@@ -522,10 +522,18 @@ export const en: FireCMSTranslations = {
|
|
|
522
522
|
auto_setup_collections_button: "Automatically set up collections",
|
|
523
523
|
auto_setup_collections_title: "Automatically set up collections?",
|
|
524
524
|
auto_setup_collections_desc: "This will automatically create collection configs for collections that are <b>NOT</b> already mapped",
|
|
525
|
-
|
|
525
|
+
setting_up_collections: "Setting up collections",
|
|
526
|
+
setting_up_collection: "Setting up {{name}}",
|
|
526
527
|
no_collections_found_to_setup: "No collections found to setup.",
|
|
527
528
|
collections_have_been_setup: "Collections have been automatically setup.",
|
|
528
529
|
error_setting_up_collections: "Error automatically setting up collections",
|
|
530
|
+
setup_collections_title: "Set up collections",
|
|
531
|
+
setup_collections_select_desc: "Select which collections to automatically set up:",
|
|
532
|
+
select_all: "Select all",
|
|
533
|
+
deselect_all: "Deselect all",
|
|
534
|
+
setup_collections_confirm: "Set up ({{count}})",
|
|
535
|
+
collection_setup_success: "{{name}} has been set up",
|
|
536
|
+
go_to_collection: "Go to collection",
|
|
529
537
|
|
|
530
538
|
// --- Home Suggestions ---
|
|
531
539
|
add_your: "Add your",
|
package/src/locales/es.ts
CHANGED
|
@@ -152,8 +152,8 @@ export const es: FireCMSTranslations = {
|
|
|
152
152
|
// ─── Error states ─────────────────────────────────────────────
|
|
153
153
|
error: "Error",
|
|
154
154
|
error_uploading_file: "Error al subir archivo",
|
|
155
|
-
error_deleting: "Error al eliminar",
|
|
156
|
-
error_before_delete: "Error antes de eliminar",
|
|
155
|
+
error_deleting: "Error al eliminar: {{message}}",
|
|
156
|
+
error_before_delete: "Error antes de eliminar: {{message}}",
|
|
157
157
|
error_updating_asset: "Error al actualizar recurso",
|
|
158
158
|
error_deleting_asset: "Error al eliminar recurso",
|
|
159
159
|
error_firestore_index: "Se requiere un índice de Firestore para esta consulta.",
|
|
@@ -522,10 +522,18 @@ export const es: FireCMSTranslations = {
|
|
|
522
522
|
auto_setup_collections_button: "Configurar colecciones automáticamente",
|
|
523
523
|
auto_setup_collections_title: "¿Configurar colecciones automáticamente?",
|
|
524
524
|
auto_setup_collections_desc: "Esto creará automáticamente la configuración de las colecciones que <b>NO</b> estén mapeadas",
|
|
525
|
-
|
|
525
|
+
setting_up_collections: "Configurando colecciones",
|
|
526
|
+
setting_up_collection: "Configurando {{name}}",
|
|
526
527
|
no_collections_found_to_setup: "No se encontraron colecciones para configurar",
|
|
527
528
|
collections_have_been_setup: "¡Tus colecciones han sido configuradas!",
|
|
528
529
|
error_setting_up_collections: "Error al configurar colecciones",
|
|
530
|
+
setup_collections_title: "Set up collections",
|
|
531
|
+
setup_collections_select_desc: "Select which collections to automatically set up:",
|
|
532
|
+
select_all: "Select all",
|
|
533
|
+
deselect_all: "Deselect all",
|
|
534
|
+
setup_collections_confirm: "Set up ({{count}})",
|
|
535
|
+
collection_setup_success: "{{name}} has been set up",
|
|
536
|
+
go_to_collection: "Go to collection",
|
|
529
537
|
|
|
530
538
|
// --- Home Suggestions ---
|
|
531
539
|
add_your: "Añade tus",
|
package/src/locales/fr.ts
CHANGED
|
@@ -146,8 +146,8 @@ export const fr: FireCMSTranslations = {
|
|
|
146
146
|
// ─── Error states ─────────────────────────────────────────────
|
|
147
147
|
error: "Erreur",
|
|
148
148
|
error_uploading_file: "Erreur lors du téléchargement du fichier",
|
|
149
|
-
error_deleting: "Erreur lors de la suppression",
|
|
150
|
-
error_before_delete: "Erreur avant la suppression",
|
|
149
|
+
error_deleting: "Erreur lors de la suppression: {{message}}",
|
|
150
|
+
error_before_delete: "Erreur avant la suppression: {{message}}",
|
|
151
151
|
error_updating_asset: "Erreur lors de la mise à jour de l'actif",
|
|
152
152
|
error_deleting_asset: "Erreur lors de la suppression de l'actif",
|
|
153
153
|
error_firestore_index: "Un index Firestore est requis pour cette requête.",
|
|
@@ -514,10 +514,18 @@ export const fr: FireCMSTranslations = {
|
|
|
514
514
|
auto_setup_collections_button: "Configurer les collections automatiquement",
|
|
515
515
|
auto_setup_collections_title: "Configurer les collections automatiquement ?",
|
|
516
516
|
auto_setup_collections_desc: "Cela créera automatiquement des configurations de collection pour les collections qui ne sont <b>PAS</b> déjà mappées",
|
|
517
|
-
|
|
517
|
+
setting_up_collections: "Configuration des collections",
|
|
518
|
+
setting_up_collection: "Configuration de {{name}}",
|
|
518
519
|
no_collections_found_to_setup: "Aucune collection à configurer trouvée",
|
|
519
520
|
collections_have_been_setup: "Les collections ont été configurées",
|
|
520
521
|
error_setting_up_collections: "Erreur lors de la configuration des collections",
|
|
522
|
+
setup_collections_title: "Set up collections",
|
|
523
|
+
setup_collections_select_desc: "Select which collections to automatically set up:",
|
|
524
|
+
select_all: "Select all",
|
|
525
|
+
deselect_all: "Deselect all",
|
|
526
|
+
setup_collections_confirm: "Set up ({{count}})",
|
|
527
|
+
collection_setup_success: "{{name}} has been set up",
|
|
528
|
+
go_to_collection: "Go to collection",
|
|
521
529
|
add_your: "Ajoutez vos",
|
|
522
530
|
database_collections: "collections de base de données",
|
|
523
531
|
to_firecms: "à FireCMS",
|
package/src/locales/hi.ts
CHANGED
|
@@ -146,8 +146,8 @@ export const hi: FireCMSTranslations = {
|
|
|
146
146
|
// ─── Error states ─────────────────────────────────────────────
|
|
147
147
|
error: "त्रुटि",
|
|
148
148
|
error_uploading_file: "फ़ाइल अपलोड करने में त्रुटि",
|
|
149
|
-
error_deleting: "हटाने में
|
|
150
|
-
error_before_delete: "हटाने से पहले
|
|
149
|
+
error_deleting: "हटाने में त्रुटि: {{message}}",
|
|
150
|
+
error_before_delete: "हटाने से पहले त्रुटि: {{message}}",
|
|
151
151
|
error_updating_asset: "एसेट अपडेट करने में त्रुटि",
|
|
152
152
|
error_deleting_asset: "एसेट हटाने में त्रुटि",
|
|
153
153
|
error_firestore_index: "इस क्वेरी के लिए Firestore इंडेक्स आवश्यक है।",
|
|
@@ -514,10 +514,18 @@ export const hi: FireCMSTranslations = {
|
|
|
514
514
|
auto_setup_collections_button: "संग्रहों को स्वचालित रूप से सेट करें",
|
|
515
515
|
auto_setup_collections_title: "संग्रहों की स्वचालित सेटअप",
|
|
516
516
|
auto_setup_collections_desc: "अपने मौजूदा Firestore डेटा के आधार पर संग्रहों को स्वचालित रूप से सेट करें। FireCMS को आपके लिए परफेक्ट CMS कॉन्फ़िगर करने दें।",
|
|
517
|
-
|
|
517
|
+
setting_up_collections: "संग्रह सेट किए जा रहे हैं",
|
|
518
|
+
setting_up_collection: "{{name}} सेट किया जा रहा है",
|
|
518
519
|
no_collections_found_to_setup: "सेट करने के लिए कोई संग्रह नहीं मिला",
|
|
519
520
|
collections_have_been_setup: "संग्रहों को सेट कर दिया गया है",
|
|
520
521
|
error_setting_up_collections: "संग्रह सेट करने में त्रुटि",
|
|
522
|
+
setup_collections_title: "Set up collections",
|
|
523
|
+
setup_collections_select_desc: "Select which collections to automatically set up:",
|
|
524
|
+
select_all: "Select all",
|
|
525
|
+
deselect_all: "Deselect all",
|
|
526
|
+
setup_collections_confirm: "Set up ({{count}})",
|
|
527
|
+
collection_setup_success: "{{name}} has been set up",
|
|
528
|
+
go_to_collection: "Go to collection",
|
|
521
529
|
add_your: "अपने",
|
|
522
530
|
database_collections: "डेटाबेस संग्रह",
|
|
523
531
|
to_firecms: "को FireCMS में जोड़ें",
|