@rebasepro/admin 0.13.1-canary.gf57a27e → 0.14.0
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/{CollectionEditorDialog-BnfOcqM_.js → CollectionEditorDialog-CSheud_E.js} +19 -9
- package/dist/CollectionEditorDialog-CSheud_E.js.map +1 -0
- package/dist/{PropertyEditView-vLXglzIy.js → PropertyEditView-CWZlC1gq.js} +5 -5
- package/dist/PropertyEditView-CWZlC1gq.js.map +1 -0
- package/dist/{RouterCollectionsStudioView-DF3aiOuv.js → RouterCollectionsStudioView-CwTpdzWF.js} +4 -4
- package/dist/{RouterCollectionsStudioView-DF3aiOuv.js.map → RouterCollectionsStudioView-CwTpdzWF.js.map} +1 -1
- package/dist/collection_editor_ui.js +4 -4
- package/dist/components/ClearFilterSortButton.d.ts +7 -1
- package/dist/components/CollectionViewBinding/SortButton.d.ts +7 -1
- package/dist/components/RelationSelector.d.ts +13 -0
- package/dist/data_export/export/export.d.ts +7 -0
- package/dist/data_export/export/fetch_export_data.d.ts +48 -0
- package/dist/data_export/export/index.d.ts +1 -0
- package/dist/data_import/utils/csv.d.ts +30 -0
- package/dist/data_import/utils/index.d.ts +2 -0
- package/dist/data_import/utils/save_entities.d.ts +38 -0
- package/dist/data_import/utils/transforms.d.ts +9 -0
- package/dist/editor.js +3 -0
- package/dist/editor.js.map +1 -1
- package/dist/{export-C26xQN-g.js → export-L53WektO.js} +175 -39
- package/dist/export-L53WektO.js.map +1 -0
- package/dist/{history-DSS-Ebwh.js → history-D5SKygpJ.js} +2 -2
- package/dist/{history-DSS-Ebwh.js.map → history-D5SKygpJ.js.map} +1 -1
- package/dist/{import-Dhx96B6x.js → import-C_4edkoD.js} +5 -4
- package/dist/import-C_4edkoD.js.map +1 -0
- package/dist/index.js +93 -74
- package/dist/index.js.map +1 -1
- package/dist/preview/components/date_fns_locales.d.ts +9 -0
- package/dist/{util-Bt6VadeZ.js → util-C-D5yD2n.js} +567 -113
- package/dist/util-C-D5yD2n.js.map +1 -0
- package/package.json +10 -9
- package/src/collection_editor/ui/collection_editor/AICollectionGeneratorPopover.tsx +8 -3
- package/src/collection_editor/ui/collection_editor/CollectionDetailsForm.tsx +5 -2
- package/src/collection_editor/ui/collection_editor/CollectionJsonImportDialog.tsx +1 -0
- package/src/collection_editor/ui/collection_editor/CollectionRLSTab.tsx +9 -8
- package/src/collection_editor/ui/collection_editor/GeneralSettingsForm.tsx +5 -2
- package/src/collection_editor/ui/collection_editor/properties/MapPropertyField.tsx +1 -1
- package/src/collection_editor/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +5 -2
- package/src/collection_editor/ui/collection_editor/properties/conditions/ConditionsEditor.tsx +1 -0
- package/src/collection_editor/useJsonCollectionsConfigController.tsx +92 -84
- package/src/collection_editor/useLocalCollectionsConfigController.tsx +10 -3
- package/src/components/ClearFilterSortButton.tsx +16 -7
- package/src/components/CollectionTableBinding/table_bindings.tsx +7 -1
- package/src/components/CollectionViewBinding/CollectionBoardViewBinding.tsx +13 -7
- package/src/components/CollectionViewBinding/CollectionViewBinding.tsx +1 -0
- package/src/components/CollectionViewBinding/CollectionViewStartActions.tsx +9 -0
- package/src/components/CollectionViewBinding/SortButton.tsx +16 -4
- package/src/components/CollectionViewBinding/ViewModeToggle.tsx +10 -5
- package/src/components/CollectionViewBinding/useBoardDataController.tsx +11 -3
- package/src/components/DefaultDrawer.tsx +7 -0
- package/src/components/DrawerNavigationItem.tsx +12 -0
- package/src/components/RelationSelector.tsx +52 -5
- package/src/components/SearchIconsView.tsx +14 -33
- package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +1 -0
- package/src/components/UserSelector.tsx +47 -5
- package/src/data_export/export/ExportCollectionAction.tsx +101 -48
- package/src/data_export/export/export.ts +63 -16
- package/src/data_export/export/fetch_export_data.ts +90 -0
- package/src/data_export/export/index.ts +1 -0
- package/src/data_import/components/DataNewPropertiesMapping.tsx +20 -3
- package/src/data_import/components/ImportNewPropertyFieldPreview.tsx +1 -0
- package/src/data_import/components/ImportSaveInProgress.tsx +35 -36
- package/src/data_import/import/ImportCollectionAction.tsx +13 -2
- package/src/data_import/utils/csv.ts +153 -0
- package/src/data_import/utils/data.ts +7 -1
- package/src/data_import/utils/file_to_json.ts +77 -6
- package/src/data_import/utils/index.ts +2 -0
- package/src/data_import/utils/save_entities.ts +138 -0
- package/src/data_import/utils/transforms.ts +23 -0
- package/src/editor/components/image-bubble.tsx +2 -0
- package/src/editor/selectors/link-selector.tsx +1 -0
- package/src/form/field_bindings/BinaryFieldBinding.tsx +1 -0
- package/src/form/field_bindings/DateTimeFieldBinding.tsx +4 -0
- package/src/form/field_bindings/KeyValueFieldBinding.tsx +3 -0
- package/src/form/field_bindings/TextFieldBinding.tsx +18 -0
- package/src/form/field_bindings/VectorFieldBinding.tsx +1 -0
- package/src/preview/components/DatePreview.tsx +15 -2
- package/src/preview/components/ReferencePreview.tsx +5 -8
- package/src/preview/components/RelationPreview.tsx +8 -7
- package/src/preview/components/date_fns_locales.ts +125 -0
- package/dist/CollectionEditorDialog-BnfOcqM_.js.map +0 -1
- package/dist/PropertyEditView-vLXglzIy.js.map +0 -1
- package/dist/export-C26xQN-g.js.map +0 -1
- package/dist/import-Dhx96B6x.js.map +0 -1
- package/dist/util-Bt6VadeZ.js.map +0 -1
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
import * as React$1 from "react";
|
|
2
2
|
import React, { Suspense, createContext, createElement, forwardRef, lazy, memo, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
3
3
|
import { deepEqual } from "fast-equals";
|
|
4
|
-
import { Alert, AlertCircleIcon, AlertTriangleIcon, AlignLeftIcon, ArrowDownIcon, ArrowLeftIcon, ArrowRightToLineIcon, ArrowUpDownIcon, ArrowUpIcon, Badge, BooleanSwitchWithLabel, Button, CHIP_COLORS, CHIP_SEED_KEYS, CalendarIcon, Card, CardView, CenteredView, CheckCircleIcon, CheckIcon, Checkbox, ChevronDownIcon, ChevronRightIcon, ChevronUpIcon, ChevronsUpDownIcon, Chip, CircleIcon, CircularProgress, CircularProgressCenter, CodeIcon, Collapse, ColumnsIcon, CopyIcon, DateTimeField, Dialog, DialogActions, DialogContent, DialogTitle, ErrorBoundary, ExpandablePanel, ExternalLinkIcon, EyeIcon, EyeOffIcon, FileTextIcon, FileUpload, FilterChip, FilterIcon, FilterXIcon, FlagIcon, FolderUpIcon, FunctionSquareIcon, GlobeIcon, HandleIcon, HashIcon, HistoryIcon, IconButton, ImageOffIcon, KanbanIcon, KanbanView, KeyRoundIcon, Label, LayoutGridIcon, Link2Icon, LinkIcon, ListIcon, ListOrderedIcon, ListView, LoadingButton, MailIcon, Markdown, Maximize2Icon, Menu, MenuItem, MinusIcon, MoreVerticalIcon, MultiSelect, MultiSelectItem, PanelLeftCloseIcon, PanelLeftOpenIcon, PencilIcon, PlusIcon, Popover, PopoverPrimitive, Portal, QuoteIcon, RadioGroup, RadioGroupItem, RefreshCwIcon, RepeatIcon, ResizablePanels, Rows3Icon, SearchBar, SearchIcon, Select, SelectItem, Separator, SettingsIcon, Sheet, Skeleton, Tab, Table, TableBody, TableCell, TableHeader, TableIcon, TableRow, Tabs, TextField, TextIcon, TextareaAutosize, ToggleButtonGroup, Tooltip, Trash2Icon, Typography, Unlink2Icon, UploadIcon, UserIcon, VirtualTable, VirtualTableDateField, VirtualTableInput, VirtualTableInput as VirtualTableInput$1, VirtualTableNumberInput, VirtualTableSwitch, VoteIcon, XCircleIcon, XIcon, cls, coolIconKeys, createVirtualTableSelectionStore, debounce, defaultBorderMixin, fieldBackgroundDisabledMixin, fieldBackgroundHoverMixin, fieldBackgroundInvisibleMixin, fieldBackgroundMixin, focusedDisabled, getColorSchemeForSeed, iconSize,
|
|
4
|
+
import { Alert, AlertCircleIcon, AlertTriangleIcon, AlignLeftIcon, ArrowDownIcon, ArrowLeftIcon, ArrowRightToLineIcon, ArrowUpDownIcon, ArrowUpIcon, Badge, BooleanSwitchWithLabel, Button, CHIP_COLORS, CHIP_SEED_KEYS, CalendarIcon, Card, CardView, CenteredView, CheckCircleIcon, CheckIcon, Checkbox, ChevronDownIcon, ChevronRightIcon, ChevronUpIcon, ChevronsUpDownIcon, Chip, CircleIcon, CircularProgress, CircularProgressCenter, CodeIcon, Collapse, ColumnsIcon, CopyIcon, DateTimeField, Dialog, DialogActions, DialogContent, DialogTitle, ErrorBoundary, ExpandablePanel, ExternalLinkIcon, EyeIcon, EyeOffIcon, FileTextIcon, FileUpload, FilterChip, FilterIcon, FilterXIcon, FlagIcon, FolderUpIcon, FunctionSquareIcon, GlobeIcon, HandleIcon, HashIcon, HistoryIcon, IconButton, ImageOffIcon, KanbanIcon, KanbanView, KeyRoundIcon, Label, LayoutGridIcon, Link2Icon, LinkIcon, ListIcon, ListOrderedIcon, ListView, LoadingButton, LucideIconByName, MailIcon, Markdown, Maximize2Icon, Menu, MenuItem, MinusIcon, MoreVerticalIcon, MultiSelect, MultiSelectItem, PanelLeftCloseIcon, PanelLeftOpenIcon, PencilIcon, PlusIcon, Popover, PopoverPrimitive, Portal, QuoteIcon, RadioGroup, RadioGroupItem, RefreshCwIcon, RepeatIcon, ResizablePanels, Rows3Icon, SearchBar, SearchIcon, Select, SelectItem, Separator, SettingsIcon, Sheet, Skeleton, Tab, Table, TableBody, TableCell, TableHeader, TableIcon, TableRow, Tabs, TextField, TextIcon, TextareaAutosize, ToggleButtonGroup, Tooltip, Trash2Icon, Typography, Unlink2Icon, UploadIcon, UserIcon, VirtualTable, VirtualTableDateField, VirtualTableInput, VirtualTableInput as VirtualTableInput$1, VirtualTableNumberInput, VirtualTableSwitch, VoteIcon, XCircleIcon, XIcon, cls, coolIconKeys, createVirtualTableSelectionStore, debounce, defaultBorderMixin, fieldBackgroundDisabledMixin, fieldBackgroundHoverMixin, fieldBackgroundInvisibleMixin, fieldBackgroundMixin, focusedDisabled, getColorSchemeForSeed, iconSize, paperMixin, useDebouncedCallback, useOutsideAlerter, usePortalContainer } from "@rebasepro/ui";
|
|
5
5
|
import { CollectionRegistry, DEFAULT_ONE_OF_TYPE, DEFAULT_ONE_OF_VALUE, canReadCollection, enumToObjectEntries, getArrayResolvedProperties, getDefaultValueFor, getDefaultValueFortype, getDefaultValuesFor, getEntityChildViews, getLabelOrConfigFrom, getReferenceFrom, getRelationFrom, getRelationTargetPath, getSubcollections, getTableName, isPropertyBuilder, normalizeToEntityRelation, resolveArrayProperties, resolveEnumValues, resolveProperty, resolveRelationProperty, resolveStorageFilenameString, resolveStoragePathString, resolveStorageSource } from "@rebasepro/common";
|
|
6
6
|
import { CONTAINER_FULL_WIDTH, CollectionScopeProvider, DEFAULT_PAGE_SIZE, EntityDisplayCache, ErrorTooltip, ErrorView, IconForView, PluginProviderStack, SIDE_PANEL_DEFAULT_WIDTH, StorageSourceContext, UnsavedChangesDialog, UserDisplay, addInitialSlash, apiBaseOf, buildEnumLabel, createFormexStub, deleteEntityWithCallbacks, entityDisplayKey, flattenKeys, getAdminEntityChildViews, getCollectionBySlugWithin, getCollectionPathsCombinations, getColorScheme, getColumnKeysForProperty, getDisplayPropertyKey, getDisplayResolver, getEntityFromCache, getEntityFromMemoryCache, getEntityImagePreviewPropertyKey, getEntityPreviewKeys, getFormFieldKeys, getIcon, getLastSegment as getLastSegment$1, getLeadingRelationTitleKey, getLocalChangesBackup, getNavigationEntriesFromPath, getParentReferencesFromPath, getPropertyInPath, getRedundantChildViewColumnIds, getRelationIncludeParams, getRowHeight, getTitlePropertyCandidates, getTitlePropertyKey, getTitlePropertyKeyForValues, hasDeclaredDisplay, iconsSearch, isDisabled, isFilterableRelation, isHidden, isReadOnly, looksLikeIdentifierValue, removeEntityFromCache, removeEntityFromMemoryCache, removeInitialAndTrailingSlashes, removeInitialAndTrailingSlashes as removeInitialAndTrailingSlashes$1, removeInitialSlash, removeTrailingSlash, removeTrailingSlash as removeTrailingSlash$1, resolveCollectionPathIds, resolveCollectionPathIds as resolveCollectionPathIds$1, resolveComponentRef, resolveDefaultSelectedView, resolveFilterOperators, resolveFormLayout, saveEntityToCache, saveEntityToMemoryCache, saveEntityWithCallbacks, useAnalyticsController, useApiBase, useApiConfig, useAuthController, useCollectionScope, useColumnIds, useComponentOverride, useCustomizationController, useData, useDataTableController, useFetch, useLargeLayout, useModeController, useNavigationBlocker, usePermissions, useRebaseClient, useRebaseContext, useRelationSelector, useResolvedComponent, useScrollRestoration, useSlot, useSnackbarController, useStorageSource, useStorageSources, useStorageUploadController, useTranslation, useUnsavedChangesDialog, useUserConfigurationPersistence } from "@rebasepro/app";
|
|
7
7
|
import { Field, Formex, getIn, setIn, useCreateFormex, useFormex } from "@rebasepro/forms";
|
|
8
8
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
-
import { defaultDateFormat, formatRelativeTime, getHashValue, getValueInPath, hydrateRegExp, isArrayValue, isObject, mergeDeep, randomString, readStoredJson, toArray, writeStoredJson } from "@rebasepro/utils";
|
|
9
|
+
import { defaultDateFormat, formatRelativeTime, getHashValue, getValueInPath, hydrateRegExp, isArrayValue, isObject, isPrototypePollutingKey, mergeDeep, pathTraversesPrototype, randomString, readStoredJson, toArray, writeStoredJson } from "@rebasepro/utils";
|
|
10
10
|
import { resolveAdminCollection } from "@rebasepro/admin-types";
|
|
11
11
|
import { format } from "date-fns";
|
|
12
|
-
import * as locales from "date-fns/locale";
|
|
13
12
|
import { Command } from "cmdk";
|
|
14
13
|
import { useDropzone } from "react-dropzone";
|
|
15
|
-
import { EntityReference, EntityRelation, GeoPoint, Vector, getCollectionDataPath, getDataSourceCapabilities, isRelationalCollectionConfig } from "@rebasepro/types";
|
|
14
|
+
import { EntityReference, EntityRelation, GeoPoint, MAX_LIST_LIMIT, Vector, getCollectionDataPath, getDataSourceCapabilities, isRelationalCollectionConfig } from "@rebasepro/types";
|
|
16
15
|
import { z } from "zod";
|
|
17
16
|
import useMeasure from "react-use-measure";
|
|
18
17
|
import { Link, useLocation, useNavigate, useSearchParams } from "react-router";
|
|
@@ -20,7 +19,6 @@ import { DndContext, KeyboardSensor, PointerSensor, closestCenter, useSensor, us
|
|
|
20
19
|
import { restrictToVerticalAxis } from "@dnd-kit/modifiers";
|
|
21
20
|
import { SortableContext, horizontalListSortingStrategy, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
|
|
22
21
|
import { CSS } from "@dnd-kit/utilities";
|
|
23
|
-
import ExcelJS from "exceljs";
|
|
24
22
|
import { inferTypeFromValue } from "@rebasepro/inference";
|
|
25
23
|
import { Highlight, themes } from "prism-react-renderer";
|
|
26
24
|
import { generateKeyBetween, generateNKeysBetween } from "fractional-indexing";
|
|
@@ -2244,8 +2242,7 @@ function ReferencePreviewExisting({ reference, collection, previewProperties, si
|
|
|
2244
2242
|
collection,
|
|
2245
2243
|
useCache: true
|
|
2246
2244
|
});
|
|
2247
|
-
|
|
2248
|
-
const usedEntity = entity ?? referencesCache.get(reference.pathWithId);
|
|
2245
|
+
const usedEntity = entity;
|
|
2249
2246
|
let body;
|
|
2250
2247
|
if (!reference) body = /* @__PURE__ */ jsx(ErrorView, { error: "Reference not set" });
|
|
2251
2248
|
else if (usedEntity && !usedEntity.values) body = /* @__PURE__ */ jsx(ErrorView, {
|
|
@@ -2304,7 +2301,6 @@ function ReferencePreviewExisting({ reference, collection, previewProperties, si
|
|
|
2304
2301
|
hover
|
|
2305
2302
|
});
|
|
2306
2303
|
}
|
|
2307
|
-
var referencesCache = /* @__PURE__ */ new Map();
|
|
2308
2304
|
//#endregion
|
|
2309
2305
|
//#region src/preview/components/InlineEntityListPreview.tsx
|
|
2310
2306
|
/**
|
|
@@ -2487,8 +2483,7 @@ function RelationPreviewExisting({ relation, collection, previewProperties, size
|
|
|
2487
2483
|
collection,
|
|
2488
2484
|
useCache: true
|
|
2489
2485
|
});
|
|
2490
|
-
|
|
2491
|
-
const usedEntity = passedEntity ?? entity ?? relationsCache.get(relation.pathWithId);
|
|
2486
|
+
const usedEntity = passedEntity ?? entity;
|
|
2492
2487
|
let body;
|
|
2493
2488
|
if (!relation) body = /* @__PURE__ */ jsx(ErrorView, { error: "Relation not set" });
|
|
2494
2489
|
else if (usedEntity && !usedEntity.values) body = /* @__PURE__ */ jsx(ErrorView, {
|
|
@@ -2547,7 +2542,6 @@ function RelationPreviewExisting({ relation, collection, previewProperties, size
|
|
|
2547
2542
|
hover
|
|
2548
2543
|
});
|
|
2549
2544
|
}
|
|
2550
|
-
var relationsCache = /* @__PURE__ */ new Map();
|
|
2551
2545
|
//#endregion
|
|
2552
2546
|
//#region src/preview/property_previews/ArrayOfRelationsPreview.tsx
|
|
2553
2547
|
/**
|
|
@@ -2851,13 +2845,144 @@ function KeyValuePreview({ value }) {
|
|
|
2851
2845
|
});
|
|
2852
2846
|
}
|
|
2853
2847
|
//#endregion
|
|
2848
|
+
//#region src/preview/components/date_fns_locales.ts
|
|
2849
|
+
/**
|
|
2850
|
+
* One date-fns locale, fetched when the admin is actually configured to use it.
|
|
2851
|
+
*
|
|
2852
|
+
* `DatePreview` used to open with:
|
|
2853
|
+
*
|
|
2854
|
+
* import * as locales from "date-fns/locale";
|
|
2855
|
+
*
|
|
2856
|
+
* and then index that namespace with the configured locale. A namespace import
|
|
2857
|
+
* of a barrel is a reference to every member of it, so all ~77 locales were
|
|
2858
|
+
* bundled — 640 kB, in the entry chunk's static graph, to format dates in the
|
|
2859
|
+
* one locale a given deployment has configured (and `undefined`, meaning
|
|
2860
|
+
* en-US, for nearly all of them).
|
|
2861
|
+
*
|
|
2862
|
+
* A map of thunks is written out rather than derived from the locale key,
|
|
2863
|
+
* because `import(`date-fns/locale/${name}`)` is not statically analysable:
|
|
2864
|
+
* the bundler cannot see which modules it must emit, and the import fails at
|
|
2865
|
+
* runtime in a build. Each entry below is its own async chunk, and exactly one
|
|
2866
|
+
* of them is ever fetched.
|
|
2867
|
+
*
|
|
2868
|
+
* `fil` is in the `Locale` union and has no date-fns locale to import; it
|
|
2869
|
+
* resolved to `undefined` through the namespace too, so it still does.
|
|
2870
|
+
*/
|
|
2871
|
+
var dateFnsLocaleLoaders = {
|
|
2872
|
+
af: () => import("date-fns/locale/af").then((m) => m.af),
|
|
2873
|
+
ar: () => import("date-fns/locale/ar").then((m) => m.ar),
|
|
2874
|
+
arDZ: () => import("date-fns/locale/ar-DZ").then((m) => m.arDZ),
|
|
2875
|
+
arMA: () => import("date-fns/locale/ar-MA").then((m) => m.arMA),
|
|
2876
|
+
arSA: () => import("date-fns/locale/ar-SA").then((m) => m.arSA),
|
|
2877
|
+
az: () => import("date-fns/locale/az").then((m) => m.az),
|
|
2878
|
+
be: () => import("date-fns/locale/be").then((m) => m.be),
|
|
2879
|
+
bg: () => import("date-fns/locale/bg").then((m) => m.bg),
|
|
2880
|
+
bn: () => import("date-fns/locale/bn").then((m) => m.bn),
|
|
2881
|
+
ca: () => import("date-fns/locale/ca").then((m) => m.ca),
|
|
2882
|
+
cs: () => import("date-fns/locale/cs").then((m) => m.cs),
|
|
2883
|
+
cy: () => import("date-fns/locale/cy").then((m) => m.cy),
|
|
2884
|
+
da: () => import("date-fns/locale/da").then((m) => m.da),
|
|
2885
|
+
de: () => import("date-fns/locale/de").then((m) => m.de),
|
|
2886
|
+
el: () => import("date-fns/locale/el").then((m) => m.el),
|
|
2887
|
+
enAU: () => import("date-fns/locale/en-AU").then((m) => m.enAU),
|
|
2888
|
+
enCA: () => import("date-fns/locale/en-CA").then((m) => m.enCA),
|
|
2889
|
+
enGB: () => import("date-fns/locale/en-GB").then((m) => m.enGB),
|
|
2890
|
+
enIN: () => import("date-fns/locale/en-IN").then((m) => m.enIN),
|
|
2891
|
+
enNZ: () => import("date-fns/locale/en-NZ").then((m) => m.enNZ),
|
|
2892
|
+
enUS: () => import("date-fns/locale/en-US").then((m) => m.enUS),
|
|
2893
|
+
eo: () => import("date-fns/locale/eo").then((m) => m.eo),
|
|
2894
|
+
es: () => import("date-fns/locale/es").then((m) => m.es),
|
|
2895
|
+
et: () => import("date-fns/locale/et").then((m) => m.et),
|
|
2896
|
+
eu: () => import("date-fns/locale/eu").then((m) => m.eu),
|
|
2897
|
+
faIR: () => import("date-fns/locale/fa-IR").then((m) => m.faIR),
|
|
2898
|
+
fi: () => import("date-fns/locale/fi").then((m) => m.fi),
|
|
2899
|
+
fr: () => import("date-fns/locale/fr").then((m) => m.fr),
|
|
2900
|
+
frCA: () => import("date-fns/locale/fr-CA").then((m) => m.frCA),
|
|
2901
|
+
frCH: () => import("date-fns/locale/fr-CH").then((m) => m.frCH),
|
|
2902
|
+
gd: () => import("date-fns/locale/gd").then((m) => m.gd),
|
|
2903
|
+
gl: () => import("date-fns/locale/gl").then((m) => m.gl),
|
|
2904
|
+
gu: () => import("date-fns/locale/gu").then((m) => m.gu),
|
|
2905
|
+
he: () => import("date-fns/locale/he").then((m) => m.he),
|
|
2906
|
+
hi: () => import("date-fns/locale/hi").then((m) => m.hi),
|
|
2907
|
+
hr: () => import("date-fns/locale/hr").then((m) => m.hr),
|
|
2908
|
+
hu: () => import("date-fns/locale/hu").then((m) => m.hu),
|
|
2909
|
+
hy: () => import("date-fns/locale/hy").then((m) => m.hy),
|
|
2910
|
+
id: () => import("date-fns/locale/id").then((m) => m.id),
|
|
2911
|
+
is: () => import("date-fns/locale/is").then((m) => m.is),
|
|
2912
|
+
it: () => import("date-fns/locale/it").then((m) => m.it),
|
|
2913
|
+
ja: () => import("date-fns/locale/ja").then((m) => m.ja),
|
|
2914
|
+
ka: () => import("date-fns/locale/ka").then((m) => m.ka),
|
|
2915
|
+
kk: () => import("date-fns/locale/kk").then((m) => m.kk),
|
|
2916
|
+
kn: () => import("date-fns/locale/kn").then((m) => m.kn),
|
|
2917
|
+
ko: () => import("date-fns/locale/ko").then((m) => m.ko),
|
|
2918
|
+
lb: () => import("date-fns/locale/lb").then((m) => m.lb),
|
|
2919
|
+
lt: () => import("date-fns/locale/lt").then((m) => m.lt),
|
|
2920
|
+
lv: () => import("date-fns/locale/lv").then((m) => m.lv),
|
|
2921
|
+
mk: () => import("date-fns/locale/mk").then((m) => m.mk),
|
|
2922
|
+
ms: () => import("date-fns/locale/ms").then((m) => m.ms),
|
|
2923
|
+
mt: () => import("date-fns/locale/mt").then((m) => m.mt),
|
|
2924
|
+
nb: () => import("date-fns/locale/nb").then((m) => m.nb),
|
|
2925
|
+
nl: () => import("date-fns/locale/nl").then((m) => m.nl),
|
|
2926
|
+
nlBE: () => import("date-fns/locale/nl-BE").then((m) => m.nlBE),
|
|
2927
|
+
nn: () => import("date-fns/locale/nn").then((m) => m.nn),
|
|
2928
|
+
pl: () => import("date-fns/locale/pl").then((m) => m.pl),
|
|
2929
|
+
pt: () => import("date-fns/locale/pt").then((m) => m.pt),
|
|
2930
|
+
ptBR: () => import("date-fns/locale/pt-BR").then((m) => m.ptBR),
|
|
2931
|
+
ro: () => import("date-fns/locale/ro").then((m) => m.ro),
|
|
2932
|
+
ru: () => import("date-fns/locale/ru").then((m) => m.ru),
|
|
2933
|
+
sk: () => import("date-fns/locale/sk").then((m) => m.sk),
|
|
2934
|
+
sl: () => import("date-fns/locale/sl").then((m) => m.sl),
|
|
2935
|
+
sr: () => import("date-fns/locale/sr").then((m) => m.sr),
|
|
2936
|
+
srLatn: () => import("date-fns/locale/sr-Latn").then((m) => m.srLatn),
|
|
2937
|
+
sv: () => import("date-fns/locale/sv").then((m) => m.sv),
|
|
2938
|
+
ta: () => import("date-fns/locale/ta").then((m) => m.ta),
|
|
2939
|
+
te: () => import("date-fns/locale/te").then((m) => m.te),
|
|
2940
|
+
th: () => import("date-fns/locale/th").then((m) => m.th),
|
|
2941
|
+
tr: () => import("date-fns/locale/tr").then((m) => m.tr),
|
|
2942
|
+
ug: () => import("date-fns/locale/ug").then((m) => m.ug),
|
|
2943
|
+
uk: () => import("date-fns/locale/uk").then((m) => m.uk),
|
|
2944
|
+
uz: () => import("date-fns/locale/uz").then((m) => m.uz),
|
|
2945
|
+
vi: () => import("date-fns/locale/vi").then((m) => m.vi),
|
|
2946
|
+
zhCN: () => import("date-fns/locale/zh-CN").then((m) => m.zhCN),
|
|
2947
|
+
zhTW: () => import("date-fns/locale/zh-TW").then((m) => m.zhTW)
|
|
2948
|
+
};
|
|
2949
|
+
var cache = /* @__PURE__ */ new Map();
|
|
2950
|
+
/** The locale if it has already been fetched, otherwise `undefined`. Never fetches. */
|
|
2951
|
+
function getLoadedDateFnsLocale(locale) {
|
|
2952
|
+
return locale ? cache.get(locale) : void 0;
|
|
2953
|
+
}
|
|
2954
|
+
/**
|
|
2955
|
+
* Fetch a date-fns locale. Resolves to `undefined` for a locale date-fns does
|
|
2956
|
+
* not ship, which is what indexing the namespace used to yield.
|
|
2957
|
+
*/
|
|
2958
|
+
async function loadDateFnsLocale(locale) {
|
|
2959
|
+
if (!locale) return void 0;
|
|
2960
|
+
const cached = cache.get(locale);
|
|
2961
|
+
if (cached) return cached;
|
|
2962
|
+
const loader = dateFnsLocaleLoaders[locale];
|
|
2963
|
+
if (!loader) return void 0;
|
|
2964
|
+
const loaded = await loader();
|
|
2965
|
+
cache.set(locale, loaded);
|
|
2966
|
+
return loaded;
|
|
2967
|
+
}
|
|
2968
|
+
//#endregion
|
|
2854
2969
|
//#region src/preview/components/DatePreview.tsx
|
|
2855
2970
|
/**
|
|
2856
2971
|
* @group Preview components
|
|
2857
2972
|
*/
|
|
2858
2973
|
function DatePreview({ date, mode = "date_time", timezone }) {
|
|
2859
2974
|
const customizationController = useCustomizationController();
|
|
2860
|
-
const
|
|
2975
|
+
const configuredLocale = customizationController?.locale;
|
|
2976
|
+
const [dateUtilsLocale, setDateUtilsLocale] = React.useState(() => getLoadedDateFnsLocale(configuredLocale));
|
|
2977
|
+
React.useEffect(() => {
|
|
2978
|
+
let cancelled = false;
|
|
2979
|
+
loadDateFnsLocale(configuredLocale).then((loaded) => {
|
|
2980
|
+
if (!cancelled) setDateUtilsLocale(loaded);
|
|
2981
|
+
}).catch((error) => console.error(`[rebase] could not load the "${configuredLocale}" date locale`, error));
|
|
2982
|
+
return () => {
|
|
2983
|
+
cancelled = true;
|
|
2984
|
+
};
|
|
2985
|
+
}, [configuredLocale]);
|
|
2861
2986
|
if (!date || !(date instanceof Date) || isNaN(date.getTime())) return /* @__PURE__ */ jsx(Fragment, {});
|
|
2862
2987
|
if (timezone) {
|
|
2863
2988
|
const options = {
|
|
@@ -3585,9 +3710,13 @@ function useUserSelector({ pageSize = DEFAULT_PAGE_SIZE$2 } = {}) {
|
|
|
3585
3710
|
const [currentSearch, setCurrentSearch] = useState("");
|
|
3586
3711
|
const offsetRef = useRef(0);
|
|
3587
3712
|
const userCacheRef = useRef(/* @__PURE__ */ new Map());
|
|
3713
|
+
const requestIdRef = useRef(0);
|
|
3714
|
+
const searchTimeoutRef = useRef(null);
|
|
3588
3715
|
const fetchUsers = useCallback(async (searchStr, offset, append) => {
|
|
3589
3716
|
const apiBase = apiBaseOf(client);
|
|
3590
3717
|
if (!apiBase) return;
|
|
3718
|
+
const requestId = ++requestIdRef.current;
|
|
3719
|
+
const superseded = () => requestId !== requestIdRef.current;
|
|
3591
3720
|
setIsLoading(true);
|
|
3592
3721
|
try {
|
|
3593
3722
|
const token = await getAuthToken?.();
|
|
@@ -3599,6 +3728,7 @@ function useUserSelector({ pageSize = DEFAULT_PAGE_SIZE$2 } = {}) {
|
|
|
3599
3728
|
const response = await fetch(`${apiBase}/admin/users?${params}`, { headers: token ? { Authorization: `Bearer ${token}` } : {} });
|
|
3600
3729
|
if (!response.ok) throw new Error("Failed to fetch users");
|
|
3601
3730
|
const data = await response.json();
|
|
3731
|
+
if (superseded()) return;
|
|
3602
3732
|
const newItems = (data.data ?? data.users ?? []).map((r) => {
|
|
3603
3733
|
const user = {
|
|
3604
3734
|
uid: r.uid ?? r.id ?? "",
|
|
@@ -3620,9 +3750,10 @@ function useUserSelector({ pageSize = DEFAULT_PAGE_SIZE$2 } = {}) {
|
|
|
3620
3750
|
setHasMore(newItems.length >= pageSize);
|
|
3621
3751
|
offsetRef.current = offset + newItems.length;
|
|
3622
3752
|
} catch {
|
|
3753
|
+
if (superseded()) return;
|
|
3623
3754
|
setHasMore(false);
|
|
3624
3755
|
} finally {
|
|
3625
|
-
setIsLoading(false);
|
|
3756
|
+
if (!superseded()) setIsLoading(false);
|
|
3626
3757
|
}
|
|
3627
3758
|
}, [
|
|
3628
3759
|
client?.baseUrl,
|
|
@@ -3634,15 +3765,24 @@ function useUserSelector({ pageSize = DEFAULT_PAGE_SIZE$2 } = {}) {
|
|
|
3634
3765
|
offsetRef.current = 0;
|
|
3635
3766
|
fetchUsers("", 0, false);
|
|
3636
3767
|
}, [fetchUsers]);
|
|
3768
|
+
const search = useCallback((searchStr) => {
|
|
3769
|
+
if (searchTimeoutRef.current) clearTimeout(searchTimeoutRef.current);
|
|
3770
|
+
searchTimeoutRef.current = setTimeout(() => {
|
|
3771
|
+
setCurrentSearch(searchStr);
|
|
3772
|
+
offsetRef.current = 0;
|
|
3773
|
+
fetchUsers(searchStr, 0, false);
|
|
3774
|
+
}, searchStr.trim() ? 300 : 0);
|
|
3775
|
+
}, [fetchUsers]);
|
|
3776
|
+
useEffect(() => {
|
|
3777
|
+
return () => {
|
|
3778
|
+
if (searchTimeoutRef.current) clearTimeout(searchTimeoutRef.current);
|
|
3779
|
+
};
|
|
3780
|
+
}, []);
|
|
3637
3781
|
return {
|
|
3638
3782
|
items,
|
|
3639
3783
|
isLoading,
|
|
3640
3784
|
hasMore,
|
|
3641
|
-
search
|
|
3642
|
-
setCurrentSearch(searchStr);
|
|
3643
|
-
offsetRef.current = 0;
|
|
3644
|
-
fetchUsers(searchStr, 0, false);
|
|
3645
|
-
}, [fetchUsers]),
|
|
3785
|
+
search,
|
|
3646
3786
|
loadMore: useCallback(() => {
|
|
3647
3787
|
fetchUsers(currentSearch, offsetRef.current, true);
|
|
3648
3788
|
}, [fetchUsers, currentSearch]),
|
|
@@ -3667,7 +3807,9 @@ var UserSelector = React$1.forwardRef(({ value, size = "medium", onValueChange,
|
|
|
3667
3807
|
}, [ref]);
|
|
3668
3808
|
const contentRef = useRef(null);
|
|
3669
3809
|
const searchInputRef = useRef(null);
|
|
3670
|
-
const
|
|
3810
|
+
const listedUser = (value ? getUser(value) ?? null : null) ?? (value ? availableItems.find((i) => i.uid === value)?.user ?? null : null);
|
|
3811
|
+
const fetchedUser = useResolvedUser(value && !listedUser ? value : void 0);
|
|
3812
|
+
const resolvedUser = listedUser ?? fetchedUser;
|
|
3671
3813
|
const sentinelCallbackRef = useCallback((node) => {
|
|
3672
3814
|
if (observerRef.current) {
|
|
3673
3815
|
observerRef.current.disconnect();
|
|
@@ -3776,6 +3918,9 @@ var UserSelector = React$1.forwardRef(({ value, size = "medium", onValueChange,
|
|
|
3776
3918
|
children: [resolvedUser ? /* @__PURE__ */ jsx("div", {
|
|
3777
3919
|
className: "flex flex-row items-center gap-1 truncate flex-1 min-w-0 mr-2",
|
|
3778
3920
|
children: /* @__PURE__ */ jsx(UserDisplay, { user: resolvedUser })
|
|
3921
|
+
}) : value ? /* @__PURE__ */ jsx("span", {
|
|
3922
|
+
className: "text-sm truncate text-text-primary dark:text-text-primary-dark",
|
|
3923
|
+
children: value
|
|
3779
3924
|
}) : /* @__PURE__ */ jsx("span", {
|
|
3780
3925
|
className: "text-sm text-text-secondary dark:text-text-secondary-dark",
|
|
3781
3926
|
children: resolvedPlaceholder
|
|
@@ -4453,7 +4598,7 @@ function relationCardinality$2(relation) {
|
|
|
4453
4598
|
if (relation.kind === "belongsTo" || relation.kind === "hasOne") return "one";
|
|
4454
4599
|
return relation.cardinality;
|
|
4455
4600
|
}
|
|
4456
|
-
var RelationSelector = React$1.forwardRef(({ value, size = "medium", onValueChange, invisible, disabled, placeholder, useChips = true, className, relation, multiple: multipleOverride, fixedFilter, pageSize, emptyPlaceholder, searchPlaceholder = "Search...", noResultsText = "No
|
|
4601
|
+
var RelationSelector = React$1.forwardRef(({ value, size = "medium", onValueChange, invisible, disabled, placeholder, useChips = true, className, relation, multiple: multipleOverride, fixedFilter, pageSize, emptyPlaceholder, searchPlaceholder = "Search...", noResultsText = "No matches.", emptyText = "Select…", emptyCollectionText, loadingText = "Loading..." }, ref) => {
|
|
4457
4602
|
const collection = relation.target();
|
|
4458
4603
|
const dataClient = useData();
|
|
4459
4604
|
const sidePanelController = useSidePanel();
|
|
@@ -4461,6 +4606,7 @@ var RelationSelector = React$1.forwardRef(({ value, size = "medium", onValueChan
|
|
|
4461
4606
|
const multiple = multipleOverride ?? relationCardinality$2(relation) === "many";
|
|
4462
4607
|
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
|
|
4463
4608
|
const isPopoverOpenRef = useRef(false);
|
|
4609
|
+
const [listRequested, setListRequested] = useState(false);
|
|
4464
4610
|
const [selectedItems, setSelectedItems] = useState([]);
|
|
4465
4611
|
const [isLoadingSelectedItems, setIsLoadingSelectedItems] = useState(false);
|
|
4466
4612
|
const [searchString, setSearchString] = useState("");
|
|
@@ -4470,7 +4616,8 @@ var RelationSelector = React$1.forwardRef(({ value, size = "medium", onValueChan
|
|
|
4470
4616
|
path: getCollectionDataPath(collection),
|
|
4471
4617
|
collection,
|
|
4472
4618
|
fixedFilter,
|
|
4473
|
-
pageSize
|
|
4619
|
+
pageSize,
|
|
4620
|
+
enabled: listRequested
|
|
4474
4621
|
});
|
|
4475
4622
|
const scrollContainerRef = useRef(null);
|
|
4476
4623
|
const sentinelRef = useRef(null);
|
|
@@ -4663,6 +4810,7 @@ var RelationSelector = React$1.forwardRef(({ value, size = "medium", onValueChan
|
|
|
4663
4810
|
if (disabled) return;
|
|
4664
4811
|
if (next) {
|
|
4665
4812
|
pinnedIdsRef.current = new Set(selectedItems.map((i) => String(i.id)));
|
|
4813
|
+
setListRequested(true);
|
|
4666
4814
|
setIsPopoverOpen(true);
|
|
4667
4815
|
isPopoverOpenRef.current = true;
|
|
4668
4816
|
}
|
|
@@ -4698,7 +4846,7 @@ var RelationSelector = React$1.forwardRef(({ value, size = "medium", onValueChan
|
|
|
4698
4846
|
isPopoverOpenRef.current = false;
|
|
4699
4847
|
pinnedIdsRef.current = null;
|
|
4700
4848
|
}, []);
|
|
4701
|
-
const resolvedPlaceholder = placeholder || emptyPlaceholder ||
|
|
4849
|
+
const resolvedPlaceholder = placeholder || emptyPlaceholder || emptyText;
|
|
4702
4850
|
const portalContainer = contextPortalContainer ?? (typeof document !== "undefined" ? document.body : void 0) ?? void 0;
|
|
4703
4851
|
return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(PopoverPrimitive.Root, {
|
|
4704
4852
|
open: isPopoverOpen,
|
|
@@ -4715,6 +4863,7 @@ var RelationSelector = React$1.forwardRef(({ value, size = "medium", onValueChan
|
|
|
4715
4863
|
disabled,
|
|
4716
4864
|
onClick: () => {
|
|
4717
4865
|
if (disabled) return;
|
|
4866
|
+
setListRequested(true);
|
|
4718
4867
|
setIsPopoverOpen((o) => {
|
|
4719
4868
|
const next = !o;
|
|
4720
4869
|
isPopoverOpenRef.current = next;
|
|
@@ -4871,7 +5020,7 @@ var RelationSelector = React$1.forwardRef(({ value, size = "medium", onValueChan
|
|
|
4871
5020
|
}),
|
|
4872
5021
|
!isLoading && availableItems.length === 0 && /* @__PURE__ */ jsx(Command.Empty, {
|
|
4873
5022
|
className: "px-4 py-6 text-center text-text-secondary dark:text-text-secondary-dark",
|
|
4874
|
-
children: noResultsText
|
|
5023
|
+
children: searchString ? noResultsText : emptyCollectionText ?? `No ${collection.name ?? "entries"} yet.`
|
|
4875
5024
|
}),
|
|
4876
5025
|
/* @__PURE__ */ jsxs(Command.Group, { children: [
|
|
4877
5026
|
(() => {
|
|
@@ -5102,14 +5251,16 @@ function getTableBindingForProperty(property, selected) {
|
|
|
5102
5251
|
allowScroll: false
|
|
5103
5252
|
};
|
|
5104
5253
|
} else if (property.type === "relation") {
|
|
5105
|
-
if (property.relation)
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5254
|
+
if (property.relation) {
|
|
5255
|
+
if (property.admin?.widget === "dialog") return {
|
|
5256
|
+
Component: RelationDialogBindingComponent,
|
|
5257
|
+
allowScroll: false
|
|
5258
|
+
};
|
|
5259
|
+
else if (selected) return {
|
|
5260
|
+
Component: RelationSelectorBindingComponent,
|
|
5261
|
+
allowScroll: false
|
|
5262
|
+
};
|
|
5263
|
+
}
|
|
5113
5264
|
} else if (property.type === "array") {
|
|
5114
5265
|
const arrayProperty = property;
|
|
5115
5266
|
if (!arrayProperty.of && !arrayProperty.oneOf) throw Error("You need to specify an 'of' or 'oneOf' prop (or specify a custom field) in your array property");
|
|
@@ -6268,6 +6419,7 @@ function StringNumberFilterField({ name, value, setValue, type, isArray, enumVal
|
|
|
6268
6419
|
children: [
|
|
6269
6420
|
!enumValues && /* @__PURE__ */ jsx(TextField, {
|
|
6270
6421
|
size: "medium",
|
|
6422
|
+
"aria-label": `${title ?? name} filter value`,
|
|
6271
6423
|
type: type === "number" ? "number" : void 0,
|
|
6272
6424
|
value: internalValue !== void 0 && internalValue != null ? String(internalValue) : "",
|
|
6273
6425
|
disabled: isNullOperation,
|
|
@@ -7630,15 +7782,6 @@ function FieldCaption({ error, children }) {
|
|
|
7630
7782
|
//#endregion
|
|
7631
7783
|
//#region src/components/SearchIconsView.tsx
|
|
7632
7784
|
var UPDATE_SEARCH_INDEX_WAIT_MS = 220;
|
|
7633
|
-
function toPascalCase(str) {
|
|
7634
|
-
return str.split(/[-_]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
7635
|
-
}
|
|
7636
|
-
function resolveIcon(iconKey) {
|
|
7637
|
-
const allIcons = lucideIcons;
|
|
7638
|
-
let icon = allIcons[iconKey];
|
|
7639
|
-
if (!icon) icon = allIcons[toPascalCase(iconKey)];
|
|
7640
|
-
return icon ?? null;
|
|
7641
|
-
}
|
|
7642
7785
|
function SearchIconsView({ selectedIcon = "", onIconSelected }) {
|
|
7643
7786
|
const { t } = useTranslation();
|
|
7644
7787
|
const [keys, setKeys] = React.useState(null);
|
|
@@ -7664,21 +7807,20 @@ function SearchIconsView({ selectedIcon = "", onIconSelected }) {
|
|
|
7664
7807
|
placeholder: t("search_for_more_icons")
|
|
7665
7808
|
}), /* @__PURE__ */ jsx("div", {
|
|
7666
7809
|
className: "flex max-w-full flex-wrap mt-4",
|
|
7667
|
-
children: icons.map((icon) => {
|
|
7668
|
-
|
|
7669
|
-
|
|
7670
|
-
|
|
7671
|
-
|
|
7672
|
-
|
|
7673
|
-
|
|
7674
|
-
|
|
7675
|
-
|
|
7676
|
-
|
|
7677
|
-
|
|
7678
|
-
children: /* @__PURE__ */ jsx(LucideIcon, { size: iconSize.medium })
|
|
7810
|
+
children: icons.map((icon) => /* @__PURE__ */ jsx(Tooltip, {
|
|
7811
|
+
title: icon,
|
|
7812
|
+
asChild: true,
|
|
7813
|
+
children: /* @__PURE__ */ jsx(IconButton, {
|
|
7814
|
+
shape: "square",
|
|
7815
|
+
toggled: selectedIcon === icon,
|
|
7816
|
+
onClick: onIconSelected ? () => onIconSelected(icon) : void 0,
|
|
7817
|
+
className: "box-content m-1",
|
|
7818
|
+
children: /* @__PURE__ */ jsx(LucideIconByName, {
|
|
7819
|
+
name: icon,
|
|
7820
|
+
size: iconSize.medium
|
|
7679
7821
|
})
|
|
7680
|
-
}
|
|
7681
|
-
})
|
|
7822
|
+
})
|
|
7823
|
+
}, icon))
|
|
7682
7824
|
})] });
|
|
7683
7825
|
}
|
|
7684
7826
|
//#endregion
|
|
@@ -8022,10 +8164,23 @@ function getWorksheetHeaders(worksheet) {
|
|
|
8022
8164
|
/**
|
|
8023
8165
|
* Take an object with keys of type `address.street`, `address.city` and
|
|
8024
8166
|
* convert it to an object with nested objects like `{ address: { street: ..., city: ... } }`
|
|
8167
|
+
*
|
|
8168
|
+
* Keys here are the **header row of an uploaded file**, so they are attacker
|
|
8169
|
+
* data. `res["__proto__"] = …` is the prototype setter rather than an own
|
|
8170
|
+
* property, so a column named `__proto__.polluted` walked out of the accumulator
|
|
8171
|
+
* and wrote onto `Object.prototype` for the life of the tab — and
|
|
8172
|
+
* `constructor.prototype.x` threw a `TypeError` that surfaced as an unreadable
|
|
8173
|
+
* file. Such a column is refused, not sanitised, the way `setIn` settled it
|
|
8174
|
+
* (`docs/bug-classes.md` class 22).
|
|
8175
|
+
*
|
|
8025
8176
|
* @param flatObj
|
|
8026
8177
|
*/
|
|
8027
8178
|
function unflattenObject(flatObj) {
|
|
8028
8179
|
return Object.keys(flatObj).reduce((nestedObj, key) => {
|
|
8180
|
+
if (pathTraversesPrototype(key)) {
|
|
8181
|
+
console.warn(`Skipping column "${key}": a header may not reach the prototype chain`);
|
|
8182
|
+
return nestedObj;
|
|
8183
|
+
}
|
|
8029
8184
|
let currentObj = nestedObj;
|
|
8030
8185
|
const keyParts = key.split(".");
|
|
8031
8186
|
keyParts.forEach((keyPart, i) => {
|
|
@@ -8047,6 +8202,10 @@ function unflattenObject(flatObj) {
|
|
|
8047
8202
|
}
|
|
8048
8203
|
function mapJsonParse(obj) {
|
|
8049
8204
|
return Object.keys(obj).reduce((acc, key) => {
|
|
8205
|
+
if (isPrototypePollutingKey(key)) {
|
|
8206
|
+
console.warn(`Skipping column "${key}": a header may not reach the prototype chain`);
|
|
8207
|
+
return acc;
|
|
8208
|
+
}
|
|
8050
8209
|
try {
|
|
8051
8210
|
acc[key] = JSON.parse(obj[key]);
|
|
8052
8211
|
} catch (e) {
|
|
@@ -8056,10 +8215,183 @@ function mapJsonParse(obj) {
|
|
|
8056
8215
|
}, {});
|
|
8057
8216
|
}
|
|
8058
8217
|
//#endregion
|
|
8218
|
+
//#region src/data_import/utils/csv.ts
|
|
8219
|
+
/**
|
|
8220
|
+
* Delimiters sniffed from the header row, most common first.
|
|
8221
|
+
*
|
|
8222
|
+
* A spreadsheet saved as "CSV" in a locale that uses the comma as a decimal
|
|
8223
|
+
* separator is semicolon-delimited, and a tab-separated file saved as `.csv`
|
|
8224
|
+
* happens often enough to be worth reading.
|
|
8225
|
+
*/
|
|
8226
|
+
var CANDIDATE_DELIMITERS = [
|
|
8227
|
+
",",
|
|
8228
|
+
";",
|
|
8229
|
+
" "
|
|
8230
|
+
];
|
|
8231
|
+
/**
|
|
8232
|
+
* Split CSV text into rows of raw cells, RFC 4180 style.
|
|
8233
|
+
*
|
|
8234
|
+
* Quoted fields may contain the delimiter, newlines and doubled quotes; this is
|
|
8235
|
+
* the exact inverse of what the export's `entryToCSVRow` writes, so a file this
|
|
8236
|
+
* admin produced reads back into the cells it was built from.
|
|
8237
|
+
*/
|
|
8238
|
+
function parseCsvRows(text, delimiter = ",") {
|
|
8239
|
+
const rows = [];
|
|
8240
|
+
let row = [];
|
|
8241
|
+
let field = "";
|
|
8242
|
+
let quoted = false;
|
|
8243
|
+
let inQuotes = false;
|
|
8244
|
+
let i = 0;
|
|
8245
|
+
const endField = () => {
|
|
8246
|
+
row.push(field);
|
|
8247
|
+
field = "";
|
|
8248
|
+
quoted = false;
|
|
8249
|
+
};
|
|
8250
|
+
while (i < text.length) {
|
|
8251
|
+
const char = text[i];
|
|
8252
|
+
if (inQuotes) {
|
|
8253
|
+
if (char === "\"") {
|
|
8254
|
+
if (text[i + 1] === "\"") {
|
|
8255
|
+
field += "\"";
|
|
8256
|
+
i += 2;
|
|
8257
|
+
continue;
|
|
8258
|
+
}
|
|
8259
|
+
inQuotes = false;
|
|
8260
|
+
i++;
|
|
8261
|
+
continue;
|
|
8262
|
+
}
|
|
8263
|
+
field += char;
|
|
8264
|
+
i++;
|
|
8265
|
+
continue;
|
|
8266
|
+
}
|
|
8267
|
+
if (char === "\"" && field === "") {
|
|
8268
|
+
inQuotes = true;
|
|
8269
|
+
quoted = true;
|
|
8270
|
+
i++;
|
|
8271
|
+
continue;
|
|
8272
|
+
}
|
|
8273
|
+
if (char === delimiter) {
|
|
8274
|
+
endField();
|
|
8275
|
+
i++;
|
|
8276
|
+
continue;
|
|
8277
|
+
}
|
|
8278
|
+
if (char === "\r") {
|
|
8279
|
+
i++;
|
|
8280
|
+
continue;
|
|
8281
|
+
}
|
|
8282
|
+
if (char === "\n") {
|
|
8283
|
+
endField();
|
|
8284
|
+
rows.push(row);
|
|
8285
|
+
row = [];
|
|
8286
|
+
i++;
|
|
8287
|
+
continue;
|
|
8288
|
+
}
|
|
8289
|
+
field += char;
|
|
8290
|
+
i++;
|
|
8291
|
+
}
|
|
8292
|
+
if (field !== "" || quoted || row.length > 0) {
|
|
8293
|
+
endField();
|
|
8294
|
+
rows.push(row);
|
|
8295
|
+
}
|
|
8296
|
+
return rows;
|
|
8297
|
+
}
|
|
8298
|
+
/**
|
|
8299
|
+
* Pick the delimiter that splits the header row into the most cells. A
|
|
8300
|
+
* single-column file matches nothing and stays on the comma.
|
|
8301
|
+
*/
|
|
8302
|
+
function detectCsvDelimiter(text) {
|
|
8303
|
+
const sample = text.slice(0, 64 * 1024);
|
|
8304
|
+
let best = CANDIDATE_DELIMITERS[0];
|
|
8305
|
+
let bestCount = 1;
|
|
8306
|
+
for (const candidate of CANDIDATE_DELIMITERS) {
|
|
8307
|
+
const count = parseCsvRows(sample, candidate)[0]?.length ?? 0;
|
|
8308
|
+
if (count > bestCount) {
|
|
8309
|
+
best = candidate;
|
|
8310
|
+
bestCount = count;
|
|
8311
|
+
}
|
|
8312
|
+
}
|
|
8313
|
+
return best;
|
|
8314
|
+
}
|
|
8315
|
+
/**
|
|
8316
|
+
* Parse CSV text into one object per row, keyed by the header row.
|
|
8317
|
+
*
|
|
8318
|
+
* The picker accepts `.csv` and the dialog promises "Upload a CSV, Excel or JSON
|
|
8319
|
+
* file", but every non-JSON file used to be handed to ExcelJS'
|
|
8320
|
+
* `workbook.xlsx.load`, which is a zip reader — so every CSV failed with
|
|
8321
|
+
* "Can't find end of central directory : is this a zip file ?". The export
|
|
8322
|
+
* writes CSV by default, so the round trip could not close.
|
|
8323
|
+
*
|
|
8324
|
+
* A blank header keeps its column rather than collapsing it, so the cells to its
|
|
8325
|
+
* right stay under their own names.
|
|
8326
|
+
*/
|
|
8327
|
+
function parseCsvToObjects(text) {
|
|
8328
|
+
const cleaned = text.charCodeAt(0) === 65279 ? text.slice(1) : text;
|
|
8329
|
+
const rows = parseCsvRows(cleaned, detectCsvDelimiter(cleaned)).filter((row) => row.some((cell) => cell.trim() !== ""));
|
|
8330
|
+
if (rows.length === 0) return {
|
|
8331
|
+
headers: [],
|
|
8332
|
+
data: []
|
|
8333
|
+
};
|
|
8334
|
+
const headers = rows[0].map((header, index) => header.trim() || `Column${index + 1}`);
|
|
8335
|
+
return {
|
|
8336
|
+
headers,
|
|
8337
|
+
data: rows.slice(1).map((cells) => {
|
|
8338
|
+
const obj = {};
|
|
8339
|
+
headers.forEach((header, index) => {
|
|
8340
|
+
if (isPrototypePollutingKey(header)) return;
|
|
8341
|
+
const cell = cells[index];
|
|
8342
|
+
if (cell === void 0) return;
|
|
8343
|
+
obj[header] = cell;
|
|
8344
|
+
});
|
|
8345
|
+
return obj;
|
|
8346
|
+
})
|
|
8347
|
+
};
|
|
8348
|
+
}
|
|
8349
|
+
//#endregion
|
|
8059
8350
|
//#region src/data_import/utils/file_to_json.ts
|
|
8351
|
+
var excelJsModule;
|
|
8352
|
+
function loadExcelJs() {
|
|
8353
|
+
excelJsModule ??= import("exceljs").then((mod) => mod.default ?? mod);
|
|
8354
|
+
return excelJsModule;
|
|
8355
|
+
}
|
|
8356
|
+
/**
|
|
8357
|
+
* Whether this file is delimited text rather than a workbook.
|
|
8358
|
+
*
|
|
8359
|
+
* Browsers report `text/csv`, `application/csv` or nothing at all for the same
|
|
8360
|
+
* `.csv`, so the extension is the reliable half of the test.
|
|
8361
|
+
*/
|
|
8362
|
+
function isCsvFile(file) {
|
|
8363
|
+
const name = (file.name ?? "").toLowerCase();
|
|
8364
|
+
if (name.endsWith(".csv") || name.endsWith(".tsv")) return true;
|
|
8365
|
+
return file.type === "text/csv" || file.type === "application/csv";
|
|
8366
|
+
}
|
|
8367
|
+
/** Turn the parsed cells of one sheet/file into the entity-shaped rows the import maps. */
|
|
8368
|
+
function toImportRows(rows) {
|
|
8369
|
+
return rows.map(mapJsonParse).map(unflattenObject);
|
|
8370
|
+
}
|
|
8060
8371
|
function convertFileToJson(file) {
|
|
8061
8372
|
return new Promise((resolve, reject) => {
|
|
8062
|
-
if (file
|
|
8373
|
+
if (isCsvFile(file)) {
|
|
8374
|
+
console.debug("Converting CSV file to JSON", file.name);
|
|
8375
|
+
const reader = new FileReader();
|
|
8376
|
+
reader.onload = function(e) {
|
|
8377
|
+
try {
|
|
8378
|
+
const { headers, data } = parseCsvToObjects(e.target?.result);
|
|
8379
|
+
if (headers.length === 0) {
|
|
8380
|
+
reject(/* @__PURE__ */ new Error("The CSV file is empty"));
|
|
8381
|
+
return;
|
|
8382
|
+
}
|
|
8383
|
+
resolve({
|
|
8384
|
+
data: toImportRows(data),
|
|
8385
|
+
propertiesOrder: headers
|
|
8386
|
+
});
|
|
8387
|
+
} catch (err) {
|
|
8388
|
+
console.error("Error parsing CSV file", err);
|
|
8389
|
+
reject(err);
|
|
8390
|
+
}
|
|
8391
|
+
};
|
|
8392
|
+
reader.onerror = () => reject(reader.error ?? /* @__PURE__ */ new Error("Could not read the file"));
|
|
8393
|
+
reader.readAsText(file, "utf-8");
|
|
8394
|
+
} else if (file.type === "application/json") {
|
|
8063
8395
|
console.debug("Converting JSON file to JSON", file.name);
|
|
8064
8396
|
const reader = new FileReader();
|
|
8065
8397
|
reader.onload = function(e) {
|
|
@@ -8083,7 +8415,7 @@ function convertFileToJson(file) {
|
|
|
8083
8415
|
reader.onload = async function(e) {
|
|
8084
8416
|
try {
|
|
8085
8417
|
const buffer = e.target?.result;
|
|
8086
|
-
const workbook = new
|
|
8418
|
+
const workbook = new (await (loadExcelJs())).Workbook();
|
|
8087
8419
|
await workbook.xlsx.load(buffer);
|
|
8088
8420
|
const worksheet = workbook.worksheets[0];
|
|
8089
8421
|
if (!worksheet) {
|
|
@@ -8097,12 +8429,12 @@ function convertFileToJson(file) {
|
|
|
8097
8429
|
const obj = {};
|
|
8098
8430
|
row.eachCell({ includeEmpty: false }, (cell, colNumber) => {
|
|
8099
8431
|
const header = headers[colNumber - 1];
|
|
8100
|
-
if (header) obj[header] = cell.value;
|
|
8432
|
+
if (header && !isPrototypePollutingKey(header)) obj[header] = cell.value;
|
|
8101
8433
|
});
|
|
8102
8434
|
parsedData.push(obj);
|
|
8103
8435
|
});
|
|
8104
8436
|
resolve({
|
|
8105
|
-
data: parsedData
|
|
8437
|
+
data: toImportRows(parsedData),
|
|
8106
8438
|
propertiesOrder: headers
|
|
8107
8439
|
});
|
|
8108
8440
|
} catch (err) {
|
|
@@ -8151,44 +8483,153 @@ function ImportFileUpload({ onDataAdded }) {
|
|
|
8151
8483
|
});
|
|
8152
8484
|
}
|
|
8153
8485
|
//#endregion
|
|
8486
|
+
//#region src/data_import/utils/save_entities.ts
|
|
8487
|
+
/** Rows written per request, and per transaction where the data source has one. */
|
|
8488
|
+
var IMPORT_BATCH_SIZE = 25;
|
|
8489
|
+
/** The row a bulk write takes: the values, with the chosen id as a column. */
|
|
8490
|
+
function toBulkRow(entity) {
|
|
8491
|
+
const values = { ...entity.values ?? {} };
|
|
8492
|
+
if (entity.id !== void 0 && entity.id !== null && entity.id !== "") values.id = entity.id;
|
|
8493
|
+
return values;
|
|
8494
|
+
}
|
|
8495
|
+
function isBulkUnsupported(error) {
|
|
8496
|
+
if (error?.code === "BULK_UNSUPPORTED") return true;
|
|
8497
|
+
const message = error?.message ?? "";
|
|
8498
|
+
return /bulk writes are not supported|does not support bulk/i.test(message);
|
|
8499
|
+
}
|
|
8500
|
+
function isConflict(error) {
|
|
8501
|
+
if ((error?.status ?? error?.statusCode) === 409) return true;
|
|
8502
|
+
const code = error?.code;
|
|
8503
|
+
return code === "CONFLICT" || code === "23505";
|
|
8504
|
+
}
|
|
8505
|
+
/**
|
|
8506
|
+
* Write one batch, all-or-nothing wherever the data source can.
|
|
8507
|
+
*
|
|
8508
|
+
* `POST /<collection>/bulk` validates every row before the transaction opens,
|
|
8509
|
+
* names a failure by row index, and takes the `upsert` flag the preview screen
|
|
8510
|
+
* promises — none of which one `create` per row inside a `Promise.all` could
|
|
8511
|
+
* offer, since `Promise.all` does not cancel its siblings and every request was
|
|
8512
|
+
* its own transaction.
|
|
8513
|
+
*/
|
|
8514
|
+
async function writeBatch(accessor, batch, bulkUnsupported, written) {
|
|
8515
|
+
if (accessor.createMany && !bulkUnsupported.current) try {
|
|
8516
|
+
await accessor.createMany(batch.map(toBulkRow), { upsert: true });
|
|
8517
|
+
written.count = batch.length;
|
|
8518
|
+
return;
|
|
8519
|
+
} catch (e) {
|
|
8520
|
+
if (!isBulkUnsupported(e)) throw e;
|
|
8521
|
+
bulkUnsupported.current = true;
|
|
8522
|
+
}
|
|
8523
|
+
for (const entity of batch) {
|
|
8524
|
+
try {
|
|
8525
|
+
await accessor.create(entity.values ?? {}, entity.id);
|
|
8526
|
+
} catch (e) {
|
|
8527
|
+
if (entity.id === void 0 || !isConflict(e)) throw e;
|
|
8528
|
+
await accessor.update(entity.id, entity.values ?? {});
|
|
8529
|
+
}
|
|
8530
|
+
written.count++;
|
|
8531
|
+
}
|
|
8532
|
+
}
|
|
8533
|
+
/**
|
|
8534
|
+
* Write imported entities in batches, reporting what was committed.
|
|
8535
|
+
*
|
|
8536
|
+
* Resumable on purpose: a failure at row 4 000 leaves rows 1–3 999 in the table
|
|
8537
|
+
* whatever this does, so the only useful thing to report is where to carry on
|
|
8538
|
+
* from. `offset` is that point — the Retry button passes back the `committed`
|
|
8539
|
+
* count of the error it is retrying, instead of starting from zero and
|
|
8540
|
+
* conflicting on every row that already exists.
|
|
8541
|
+
*
|
|
8542
|
+
* @throws {ImportSaveError} naming the rows that were not written.
|
|
8543
|
+
*/
|
|
8544
|
+
async function saveImportedEntities(dataClient, path, data, { offset = 0, batchSize = 25, onBatchCommitted = () => void 0, bulkUnsupported = { current: false } } = {}) {
|
|
8545
|
+
console.debug("Saving imported data", offset, batchSize);
|
|
8546
|
+
const accessor = dataClient.collection(path);
|
|
8547
|
+
for (let start = offset; start < data.length; start += batchSize) {
|
|
8548
|
+
const batch = data.slice(start, start + batchSize);
|
|
8549
|
+
const written = { count: 0 };
|
|
8550
|
+
try {
|
|
8551
|
+
await writeBatch(accessor, batch, bulkUnsupported, written);
|
|
8552
|
+
} catch (e) {
|
|
8553
|
+
throw Object.assign(e, {
|
|
8554
|
+
committed: start + written.count,
|
|
8555
|
+
failedFrom: start + written.count,
|
|
8556
|
+
failedTo: start + batch.length
|
|
8557
|
+
});
|
|
8558
|
+
}
|
|
8559
|
+
onBatchCommitted(start + batch.length);
|
|
8560
|
+
}
|
|
8561
|
+
onBatchCommitted(data.length);
|
|
8562
|
+
}
|
|
8563
|
+
//#endregion
|
|
8154
8564
|
//#region src/data_import/components/ImportSaveInProgress.tsx
|
|
8155
8565
|
function ImportSaveInProgress({ path, importConfig, collection, onImportSuccess }) {
|
|
8156
8566
|
const [errorSaving, setErrorSaving] = useState(void 0);
|
|
8157
8567
|
const dataClient = useData();
|
|
8158
8568
|
const savingRef = useRef(false);
|
|
8569
|
+
const committedRef = useRef(0);
|
|
8570
|
+
const bulkUnsupportedRef = useRef({ current: false });
|
|
8159
8571
|
const [processedEntities, setProcessedEntities] = useState(0);
|
|
8160
8572
|
function save() {
|
|
8161
8573
|
if (savingRef.current) return;
|
|
8162
8574
|
savingRef.current = true;
|
|
8163
|
-
|
|
8575
|
+
setErrorSaving(void 0);
|
|
8576
|
+
saveImportedEntities(dataClient, path, importConfig.entities, {
|
|
8577
|
+
offset: committedRef.current,
|
|
8578
|
+
batchSize: 25,
|
|
8579
|
+
onBatchCommitted: (written) => {
|
|
8580
|
+
committedRef.current = written;
|
|
8581
|
+
setProcessedEntities(written);
|
|
8582
|
+
},
|
|
8583
|
+
bulkUnsupported: bulkUnsupportedRef.current
|
|
8584
|
+
}).then(() => {
|
|
8164
8585
|
onImportSuccess(collection);
|
|
8165
8586
|
savingRef.current = false;
|
|
8166
8587
|
}).catch((e) => {
|
|
8167
|
-
|
|
8588
|
+
const error = e;
|
|
8589
|
+
committedRef.current = error.committed ?? committedRef.current;
|
|
8590
|
+
setProcessedEntities(committedRef.current);
|
|
8591
|
+
setErrorSaving(error);
|
|
8168
8592
|
savingRef.current = false;
|
|
8169
8593
|
});
|
|
8170
8594
|
}
|
|
8171
8595
|
useEffect(() => {
|
|
8172
8596
|
save();
|
|
8173
8597
|
}, []);
|
|
8174
|
-
if (errorSaving)
|
|
8175
|
-
|
|
8176
|
-
|
|
8177
|
-
|
|
8178
|
-
|
|
8179
|
-
|
|
8180
|
-
|
|
8181
|
-
|
|
8182
|
-
|
|
8183
|
-
|
|
8184
|
-
|
|
8185
|
-
|
|
8186
|
-
|
|
8187
|
-
|
|
8188
|
-
|
|
8189
|
-
|
|
8190
|
-
|
|
8191
|
-
|
|
8598
|
+
if (errorSaving) {
|
|
8599
|
+
const total = importConfig.entities.length;
|
|
8600
|
+
const failedRows = errorSaving.failedTo - errorSaving.failedFrom > 1 ? `rows ${errorSaving.failedFrom + 1}–${errorSaving.failedTo}` : `row ${errorSaving.failedFrom + 1}`;
|
|
8601
|
+
return /* @__PURE__ */ jsxs(CenteredView, {
|
|
8602
|
+
className: "flex flex-col gap-4 items-center",
|
|
8603
|
+
children: [
|
|
8604
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
8605
|
+
variant: "h6",
|
|
8606
|
+
children: "Error saving data"
|
|
8607
|
+
}),
|
|
8608
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
8609
|
+
variant: "body2",
|
|
8610
|
+
color: "error",
|
|
8611
|
+
children: errorSaving.message
|
|
8612
|
+
}),
|
|
8613
|
+
/* @__PURE__ */ jsxs(Typography, {
|
|
8614
|
+
variant: "body2",
|
|
8615
|
+
children: [
|
|
8616
|
+
errorSaving.committed,
|
|
8617
|
+
" of ",
|
|
8618
|
+
total,
|
|
8619
|
+
" rows were imported. The import stopped on ",
|
|
8620
|
+
failedRows,
|
|
8621
|
+
", which ",
|
|
8622
|
+
errorSaving.failedTo - errorSaving.failedFrom > 1 ? "were" : "was",
|
|
8623
|
+
" not written."
|
|
8624
|
+
]
|
|
8625
|
+
}),
|
|
8626
|
+
/* @__PURE__ */ jsx(Button, {
|
|
8627
|
+
onClick: save,
|
|
8628
|
+
children: errorSaving.committed > 0 ? `Retry from row ${errorSaving.committed + 1}` : "Retry"
|
|
8629
|
+
})
|
|
8630
|
+
]
|
|
8631
|
+
});
|
|
8632
|
+
}
|
|
8192
8633
|
return /* @__PURE__ */ jsxs("div", {
|
|
8193
8634
|
className: "flex flex-col gap-4 items-center",
|
|
8194
8635
|
children: [
|
|
@@ -8213,18 +8654,6 @@ function ImportSaveInProgress({ path, importConfig, collection, onImportSuccess
|
|
|
8213
8654
|
]
|
|
8214
8655
|
});
|
|
8215
8656
|
}
|
|
8216
|
-
function saveDataBatch(dataClient, collection, path, data, offset = 0, batchSize = 25, onProgressUpdate) {
|
|
8217
|
-
console.debug("Saving imported data", offset, batchSize);
|
|
8218
|
-
const batch = data.slice(offset, offset + batchSize);
|
|
8219
|
-
return Promise.all(batch.map((d) => dataClient.collection(path).create(d.values, d.id))).then(() => {
|
|
8220
|
-
if (offset + batchSize < data.length) {
|
|
8221
|
-
onProgressUpdate(offset + batchSize);
|
|
8222
|
-
return saveDataBatch(dataClient, collection, path, data, offset + batchSize, batchSize, onProgressUpdate);
|
|
8223
|
-
}
|
|
8224
|
-
onProgressUpdate(data.length);
|
|
8225
|
-
return Promise.resolve();
|
|
8226
|
-
});
|
|
8227
|
-
}
|
|
8228
8657
|
//#endregion
|
|
8229
8658
|
//#region src/data_import/hooks/useImportConfig.tsx
|
|
8230
8659
|
var useImportConfig = () => {
|
|
@@ -8296,6 +8725,10 @@ function convertDataToEntity(authController, navigation, data, idColumn, headers
|
|
|
8296
8725
|
}
|
|
8297
8726
|
function flattenEntry(obj, parent = "") {
|
|
8298
8727
|
return Object.keys(obj).reduce((acc, key) => {
|
|
8728
|
+
if (isPrototypePollutingKey(key)) {
|
|
8729
|
+
console.warn(`Skipping column "${key}": a header may not reach the prototype chain`);
|
|
8730
|
+
return acc;
|
|
8731
|
+
}
|
|
8299
8732
|
const prefixedKey = parent ? `${parent}.${key}` : key;
|
|
8300
8733
|
if (typeof obj[key] === "object" && !(obj[key] instanceof Date) && obj[key] !== null && !Array.isArray(obj[key])) Object.assign(acc, flattenEntry(obj[key], prefixedKey));
|
|
8301
8734
|
else acc[prefixedKey] = obj[key];
|
|
@@ -8370,6 +8803,7 @@ function processValueMapping(authController, value, navigation, property) {
|
|
|
8370
8803
|
//#endregion
|
|
8371
8804
|
//#region src/data_import/components/DataNewPropertiesMapping.tsx
|
|
8372
8805
|
function DataNewPropertiesMapping({ importConfig, destinationProperties, buildPropertyView }) {
|
|
8806
|
+
const { t } = useTranslation();
|
|
8373
8807
|
const headersMapping = importConfig.headersMapping;
|
|
8374
8808
|
const headingsOrder = importConfig.headingsOrder;
|
|
8375
8809
|
const idColumn = importConfig.idColumn;
|
|
@@ -8449,7 +8883,7 @@ function DataNewPropertiesMapping({ importConfig, destinationProperties, buildPr
|
|
|
8449
8883
|
/* @__PURE__ */ jsx(TableCell, {
|
|
8450
8884
|
header: true,
|
|
8451
8885
|
style: { width: "65%" },
|
|
8452
|
-
children: "
|
|
8886
|
+
children: t("default_value")
|
|
8453
8887
|
})
|
|
8454
8888
|
] }), /* @__PURE__ */ jsx(TableBody, { children: destinationProperties && getAllPropertyKeys(destinationProperties).map((key) => {
|
|
8455
8889
|
const property = getPropertyInPath$1(destinationProperties, key);
|
|
@@ -8529,17 +8963,22 @@ function IdSelectField({ idColumn, headersMapping, onChange }) {
|
|
|
8529
8963
|
}) });
|
|
8530
8964
|
}
|
|
8531
8965
|
function DefaultValuesField({ property, onValueChange, defaultValue }) {
|
|
8966
|
+
const { t } = useTranslation();
|
|
8967
|
+
const defaultValueLabel = t("default_value");
|
|
8968
|
+
const ariaLabel = property.name ? `${defaultValueLabel}: ${property.name}` : defaultValueLabel;
|
|
8532
8969
|
if (property.type === "string") return /* @__PURE__ */ jsx(TextField, {
|
|
8533
8970
|
size: "medium",
|
|
8534
|
-
|
|
8971
|
+
"aria-label": ariaLabel,
|
|
8972
|
+
placeholder: defaultValueLabel,
|
|
8535
8973
|
value: defaultValue ?? "",
|
|
8536
8974
|
onChange: (event) => onValueChange(event.target.value)
|
|
8537
8975
|
});
|
|
8538
8976
|
else if (property.type === "number") return /* @__PURE__ */ jsx(TextField, {
|
|
8539
8977
|
size: "medium",
|
|
8540
8978
|
type: "number",
|
|
8979
|
+
"aria-label": ariaLabel,
|
|
8541
8980
|
value: defaultValue ?? "",
|
|
8542
|
-
placeholder:
|
|
8981
|
+
placeholder: defaultValueLabel,
|
|
8543
8982
|
onChange: (event) => onValueChange(event.target.value)
|
|
8544
8983
|
});
|
|
8545
8984
|
else if (property.type === "boolean") return /* @__PURE__ */ jsx(BooleanSwitchWithLabel, {
|
|
@@ -8576,6 +9015,7 @@ function ImportNewPropertyFieldPreview({ propertyKey, property, onEditClick, inc
|
|
|
8576
9015
|
className: "flex flex-row items-center gap-2",
|
|
8577
9016
|
children: [includeName && /* @__PURE__ */ jsx(TextField, {
|
|
8578
9017
|
size: "medium",
|
|
9018
|
+
"aria-label": propertyKey ? `Name of property ${propertyKey}` : "Property name",
|
|
8579
9019
|
className: "text-base grow",
|
|
8580
9020
|
value: property?.name ?? "",
|
|
8581
9021
|
onChange: (e) => {
|
|
@@ -8878,8 +9318,8 @@ function EditorCollectionAction({ path, parentCollectionSlugs, parentEntityIds,
|
|
|
8878
9318
|
}
|
|
8879
9319
|
//#endregion
|
|
8880
9320
|
//#region src/components/CollectionViewBinding/CollectionViewActions.tsx
|
|
8881
|
-
var ImportCollectionAction = lazy(() => import("./import-
|
|
8882
|
-
var ExportCollectionAction = lazy(() => import("./export-
|
|
9321
|
+
var ImportCollectionAction = lazy(() => import("./import-C_4edkoD.js").then((n) => n.t).then((m) => ({ default: m.ImportCollectionAction })));
|
|
9322
|
+
var ExportCollectionAction = lazy(() => import("./export-L53WektO.js").then((n) => n.t).then((m) => ({ default: m.ExportCollectionAction })));
|
|
8883
9323
|
function CollectionViewActions({ collection, relativePath, parentCollectionSlugs, parentEntityIds, onNewClick, onAddExistingClick, onMultipleDeleteClick, selectionEnabled, path, selectionController, tableController, collectionEntitiesCount, compact, children, openNewDocument }) {
|
|
8884
9324
|
const context = useAdminContext();
|
|
8885
9325
|
const { canCreate, canDelete } = usePermissions();
|
|
@@ -11204,7 +11644,7 @@ function JsonPreviewBinding({ values }) {
|
|
|
11204
11644
|
}
|
|
11205
11645
|
//#endregion
|
|
11206
11646
|
//#region src/components/EntityInspector.tsx
|
|
11207
|
-
var EntityHistoryView = lazy(() => import("./history-
|
|
11647
|
+
var EntityHistoryView = lazy(() => import("./history-D5SKygpJ.js").then((m) => ({ default: m.EntityHistoryView })));
|
|
11208
11648
|
/**
|
|
11209
11649
|
* Raw values and revision history, as an inspector rather than as tabs.
|
|
11210
11650
|
*
|
|
@@ -12326,6 +12766,7 @@ function useBoardDataController({ fullPath, collection, columnProperty, columns,
|
|
|
12326
12766
|
}, []);
|
|
12327
12767
|
const subscribeToColumn = useCallback((column, itemCount) => {
|
|
12328
12768
|
if (isCleaningUpRef.current) return;
|
|
12769
|
+
const requestedCount = Math.min(itemCount, MAX_LIST_LIMIT);
|
|
12329
12770
|
const currentDataClient = dataClientRef.current;
|
|
12330
12771
|
const currentCollection = collectionRef.current;
|
|
12331
12772
|
const currentContext = contextRef.current;
|
|
@@ -12455,7 +12896,7 @@ function useBoardDataController({ fullPath, collection, columnProperty, columns,
|
|
|
12455
12896
|
const includeParams = getRelationIncludeParams(currentCollection);
|
|
12456
12897
|
const fetchOnce = () => accessor.find({
|
|
12457
12898
|
where: whereFilter,
|
|
12458
|
-
limit:
|
|
12899
|
+
limit: requestedCount,
|
|
12459
12900
|
orderBy: orderByParam,
|
|
12460
12901
|
include: includeParams,
|
|
12461
12902
|
searchString: currentSearchString
|
|
@@ -12481,7 +12922,7 @@ function useBoardDataController({ fullPath, collection, columnProperty, columns,
|
|
|
12481
12922
|
let liveDataReceived = false;
|
|
12482
12923
|
const unsubscribe = accessor.listen({
|
|
12483
12924
|
where: whereFilter,
|
|
12484
|
-
limit:
|
|
12925
|
+
limit: requestedCount,
|
|
12485
12926
|
orderBy: orderByParam,
|
|
12486
12927
|
include: includeParams,
|
|
12487
12928
|
searchString: currentSearchString
|
|
@@ -13064,7 +13505,7 @@ function CollectionBoardViewBinding({ collection, tableController, fullPath, par
|
|
|
13064
13505
|
analyticsController.onAnalyticsEvent?.("kanban_backfill_order", { path: fullPath });
|
|
13065
13506
|
setBackfillLoading(true);
|
|
13066
13507
|
try {
|
|
13067
|
-
const entitiesToUpdate = (await dataClient.collection(fullPath).find({ limit:
|
|
13508
|
+
const entitiesToUpdate = (await dataClient.collection(fullPath).find({ limit: MAX_LIST_LIMIT })).data.filter((entity) => !isValidOrderKey(entity.values?.[orderProperty]));
|
|
13068
13509
|
const keys = generateNKeysBetween(null, null, entitiesToUpdate.length, ORDER_KEY_DIGITS);
|
|
13069
13510
|
const updates = [];
|
|
13070
13511
|
entitiesToUpdate.forEach((entity, index) => {
|
|
@@ -13304,10 +13745,10 @@ var ALL_VIEW_MODES = [
|
|
|
13304
13745
|
function ViewModeToggle({ viewMode = "table", onViewModeChange, enabledViews = ALL_VIEW_MODES, size, onSizeChanged, open, onOpenChange, kanbanPropertyOptions, selectedKanbanProperty, onKanbanPropertyChange }) {
|
|
13305
13746
|
const { t } = useTranslation();
|
|
13306
13747
|
const getViewModeIcon = () => {
|
|
13307
|
-
if (viewMode === "kanban") return /* @__PURE__ */ jsx(KanbanIcon, { size: iconSize.
|
|
13308
|
-
if (viewMode === "cards") return /* @__PURE__ */ jsx(LayoutGridIcon, { size: iconSize.
|
|
13309
|
-
if (viewMode === "table") return /* @__PURE__ */ jsx(TableIcon, { size: iconSize.
|
|
13310
|
-
return /* @__PURE__ */ jsx(ListIcon, { size: iconSize.
|
|
13748
|
+
if (viewMode === "kanban") return /* @__PURE__ */ jsx(KanbanIcon, { size: iconSize.small });
|
|
13749
|
+
if (viewMode === "cards") return /* @__PURE__ */ jsx(LayoutGridIcon, { size: iconSize.small });
|
|
13750
|
+
if (viewMode === "table") return /* @__PURE__ */ jsx(TableIcon, { size: iconSize.small });
|
|
13751
|
+
return /* @__PURE__ */ jsx(ListIcon, { size: iconSize.small });
|
|
13311
13752
|
};
|
|
13312
13753
|
const getViewModeName = () => {
|
|
13313
13754
|
if (viewMode === "kanban") return t("board");
|
|
@@ -14346,7 +14787,7 @@ function PopupFormFieldInternal({ tableKey, entityId, customFieldValidator, prop
|
|
|
14346
14787
|
}
|
|
14347
14788
|
//#endregion
|
|
14348
14789
|
//#region src/components/ClearFilterSortButton.tsx
|
|
14349
|
-
function ClearFilterSortButton({ tableController, enabled }) {
|
|
14790
|
+
function ClearFilterSortButton({ tableController, enabled, compact }) {
|
|
14350
14791
|
const { t } = useTranslation();
|
|
14351
14792
|
if (!enabled) return null;
|
|
14352
14793
|
const filterIsSet = !!tableController.filterValues && Object.keys(tableController.filterValues).length > 0;
|
|
@@ -14358,15 +14799,14 @@ function ClearFilterSortButton({ tableController, enabled }) {
|
|
|
14358
14799
|
else label = t("clear_sort");
|
|
14359
14800
|
return /* @__PURE__ */ jsx(Tooltip, {
|
|
14360
14801
|
title: label,
|
|
14361
|
-
children: /* @__PURE__ */ jsx(
|
|
14802
|
+
children: /* @__PURE__ */ jsx(IconButton, {
|
|
14362
14803
|
size: "small",
|
|
14363
|
-
variant: "text",
|
|
14364
14804
|
"aria-label": label,
|
|
14365
14805
|
onClick: () => {
|
|
14366
14806
|
tableController.clearFilter?.();
|
|
14367
14807
|
tableController.setSortBy?.(void 0);
|
|
14368
14808
|
},
|
|
14369
|
-
children: /* @__PURE__ */ jsx(FilterXIcon, { size: iconSize.small })
|
|
14809
|
+
children: /* @__PURE__ */ jsx(FilterXIcon, { size: compact ? iconSize.smallest : iconSize.small })
|
|
14370
14810
|
})
|
|
14371
14811
|
});
|
|
14372
14812
|
}
|
|
@@ -14724,7 +15164,7 @@ function FilterPresetsButton({ filterPresets, tableController, compact }) {
|
|
|
14724
15164
|
* is one click rather than a trip through a direction selector; clicking a
|
|
14725
15165
|
* different one starts it ascending. The popover stays open through both.
|
|
14726
15166
|
*/
|
|
14727
|
-
function SortButton({ tableController, properties }) {
|
|
15167
|
+
function SortButton({ tableController, properties, compact }) {
|
|
14728
15168
|
const { t } = useTranslation();
|
|
14729
15169
|
const [open, setOpen] = useState(false);
|
|
14730
15170
|
const options = useMemo(() => getSortablePropertyOptions(properties), [properties]);
|
|
@@ -14750,6 +15190,7 @@ function SortButton({ tableController, properties }) {
|
|
|
14750
15190
|
if (!setSortBy || options.length === 0) return null;
|
|
14751
15191
|
const activeOption = options.find((option) => option.key === activeKey);
|
|
14752
15192
|
const label = activeOption ? `${t("sort_by")}: ${activeOption.title}` : t("sort");
|
|
15193
|
+
const arrowSize = compact ? iconSize.smallest : iconSize.small;
|
|
14753
15194
|
return /* @__PURE__ */ jsx(Popover, {
|
|
14754
15195
|
open,
|
|
14755
15196
|
onOpenChange: setOpen,
|
|
@@ -14761,7 +15202,7 @@ function SortButton({ tableController, properties }) {
|
|
|
14761
15202
|
"aria-label": label,
|
|
14762
15203
|
title: label,
|
|
14763
15204
|
className: cls(activeOption && "text-primary"),
|
|
14764
|
-
children: activeDirection === "desc" ? /* @__PURE__ */ jsx(ArrowDownIcon, { size:
|
|
15205
|
+
children: activeDirection === "desc" ? /* @__PURE__ */ jsx(ArrowDownIcon, { size: arrowSize }) : activeDirection === "asc" ? /* @__PURE__ */ jsx(ArrowUpIcon, { size: arrowSize }) : /* @__PURE__ */ jsx(ArrowUpDownIcon, { size: arrowSize })
|
|
14765
15206
|
}),
|
|
14766
15207
|
children: /* @__PURE__ */ jsxs("div", {
|
|
14767
15208
|
className: "py-2 min-w-[240px] flex flex-col",
|
|
@@ -14858,6 +15299,7 @@ function CollectionViewStartActions({ collection, relativePath, parentCollection
|
|
|
14858
15299
|
children: /* @__PURE__ */ jsx(XIcon, { size: iconSize.smallest })
|
|
14859
15300
|
})
|
|
14860
15301
|
}, "close_tooltip");
|
|
15302
|
+
const iconOnlyToolbar = !largeLayout || Boolean(compact);
|
|
14861
15303
|
const filtersButton = resolvedProperties && tableController.setFilterValues && /* @__PURE__ */ jsx(Tooltip, {
|
|
14862
15304
|
title: t("filters"),
|
|
14863
15305
|
children: /* @__PURE__ */ jsx(Badge, {
|
|
@@ -14880,7 +15322,8 @@ function CollectionViewStartActions({ collection, relativePath, parentCollection
|
|
|
14880
15322
|
}, "filters_tooltip");
|
|
14881
15323
|
const sortButton = resolvedProperties && (viewMode === "list" || viewMode === "cards") && /* @__PURE__ */ jsx(SortButton, {
|
|
14882
15324
|
tableController,
|
|
14883
|
-
properties: resolvedProperties
|
|
15325
|
+
properties: resolvedProperties,
|
|
15326
|
+
compact: iconOnlyToolbar
|
|
14884
15327
|
}, "sort_button");
|
|
14885
15328
|
const filterPresetsButton = !compact && collection.filterPresets?.length ? /* @__PURE__ */ jsx(FilterPresetsButton, {
|
|
14886
15329
|
filterPresets: collection.filterPresets,
|
|
@@ -14894,6 +15337,7 @@ function CollectionViewStartActions({ collection, relativePath, parentCollection
|
|
|
14894
15337
|
sortButton,
|
|
14895
15338
|
/* @__PURE__ */ jsx(ClearFilterSortButton, {
|
|
14896
15339
|
tableController,
|
|
15340
|
+
compact: iconOnlyToolbar,
|
|
14897
15341
|
enabled: !collection.fixedFilter
|
|
14898
15342
|
}, "clear_filter"),
|
|
14899
15343
|
filterPresetsButton
|
|
@@ -15909,6 +16353,7 @@ function EntityIdHeaderWidget({ collection, path, idPath, openEntityMode }) {
|
|
|
15909
16353
|
className: "flex p-2 w-full gap-2",
|
|
15910
16354
|
children: [/* @__PURE__ */ jsx(TextField, {
|
|
15911
16355
|
autoFocus: openPopup,
|
|
16356
|
+
"aria-label": t("find_entity_by_id"),
|
|
15912
16357
|
placeholder: t("find_entity_by_id"),
|
|
15913
16358
|
size: "small",
|
|
15914
16359
|
onChange: (e) => {
|
|
@@ -19199,7 +19644,8 @@ function DateTimeFieldBinding({ propertyKey, value, setValue, autoFocus, error,
|
|
|
19199
19644
|
required: property.validation?.required,
|
|
19200
19645
|
className: showError ? "text-red-500 dark:text-red-500" : "text-text-secondary dark:text-text-secondary-dark",
|
|
19201
19646
|
title: property.name ?? propertyKey
|
|
19202
|
-
})
|
|
19647
|
+
}),
|
|
19648
|
+
"aria-label": hideLabel ? property.name ?? propertyKey : void 0
|
|
19203
19649
|
})
|
|
19204
19650
|
}), /* @__PURE__ */ jsx(FieldHelperText, {
|
|
19205
19651
|
includeDescription,
|
|
@@ -19356,6 +19802,7 @@ function MapKeyValueRow({ id, fieldKey, value, onFieldKeyChange, onDeleteClick,
|
|
|
19356
19802
|
const { t } = useTranslation();
|
|
19357
19803
|
function buildInput(entryValue, fieldKey, type) {
|
|
19358
19804
|
if (type === "string" || type === "number") return /* @__PURE__ */ jsx(TextField, {
|
|
19805
|
+
"aria-label": fieldKey ? `${t("value")}: ${fieldKey}` : t("value"),
|
|
19359
19806
|
placeholder: "value",
|
|
19360
19807
|
value: entryValue,
|
|
19361
19808
|
type: type === "number" ? "number" : "text",
|
|
@@ -19471,6 +19918,7 @@ function MapKeyValueRow({ id, fieldKey, value, onFieldKeyChange, onDeleteClick,
|
|
|
19471
19918
|
className: "w-[300px] max-w-[30%]",
|
|
19472
19919
|
children: /* @__PURE__ */ jsx(TextField, {
|
|
19473
19920
|
value: fieldKey,
|
|
19921
|
+
"aria-label": t("key"),
|
|
19474
19922
|
placeholder: "key",
|
|
19475
19923
|
disabled: disabled || entryValue !== void 0 && entryValue !== null && entryValue !== "",
|
|
19476
19924
|
size: "medium",
|
|
@@ -19541,6 +19989,7 @@ function ArrayKeyValueRow({ id, index, value, setValue }) {
|
|
|
19541
19989
|
function buildInput(entryValue, type) {
|
|
19542
19990
|
if (type === "string" || type === "number") return /* @__PURE__ */ jsx(TextField, {
|
|
19543
19991
|
value: entryValue,
|
|
19992
|
+
"aria-label": `Value ${index + 1}`,
|
|
19544
19993
|
type: type === "number" ? "number" : "text",
|
|
19545
19994
|
size: "medium",
|
|
19546
19995
|
onChange: (event) => {
|
|
@@ -20590,6 +21039,7 @@ function TextFieldBinding({ propertyKey, value, setValue, error, showError, disa
|
|
|
20590
21039
|
required: property.validation?.required || property.isId === true,
|
|
20591
21040
|
title: property.name ?? propertyKey
|
|
20592
21041
|
});
|
|
21042
|
+
const accessibleName = property.name ?? propertyKey;
|
|
20593
21043
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
20594
21044
|
/* @__PURE__ */ jsx(PropertyIdCopyTooltip, {
|
|
20595
21045
|
propertyKey,
|
|
@@ -20605,6 +21055,7 @@ function TextFieldBinding({ propertyKey, value, setValue, error, showError, disa
|
|
|
20605
21055
|
onChange,
|
|
20606
21056
|
autoFocus,
|
|
20607
21057
|
disabled,
|
|
21058
|
+
"aria-label": accessibleName,
|
|
20608
21059
|
className: cls("rounded-md resize-none w-full outline-none text-base bg-transparent min-h-[64px] px-3", label ? "pt-8 pb-2" : "py-2", disabled && "outline-none opacity-50 text-surface-accent-600 dark:text-surface-accent-500", showError && error ? "text-red-500 dark:text-red-600" : "")
|
|
20609
21060
|
}),
|
|
20610
21061
|
property.admin?.clearable && /* @__PURE__ */ jsx("div", {
|
|
@@ -20621,6 +21072,7 @@ function TextFieldBinding({ propertyKey, value, setValue, error, showError, disa
|
|
|
20621
21072
|
onChange,
|
|
20622
21073
|
autoFocus,
|
|
20623
21074
|
label,
|
|
21075
|
+
"aria-label": label ? void 0 : accessibleName,
|
|
20624
21076
|
type: inputType,
|
|
20625
21077
|
disabled,
|
|
20626
21078
|
endAdornment: property.admin?.clearable && /* @__PURE__ */ jsx(IconButton, {
|
|
@@ -20756,6 +21208,7 @@ function VectorFieldBinding({ propertyKey, value, setValue, error, showError, di
|
|
|
20756
21208
|
className: cls("flex flex-col gap-2 p-4 rounded-xl border bg-surface-50/20 dark:bg-surface-800/10", defaultBorderMixin),
|
|
20757
21209
|
children: [/* @__PURE__ */ jsx(TextField, {
|
|
20758
21210
|
size,
|
|
21211
|
+
"aria-label": `${property.name ?? propertyKey} vector values`,
|
|
20759
21212
|
value: textValue,
|
|
20760
21213
|
onChange,
|
|
20761
21214
|
autoFocus: true,
|
|
@@ -21039,6 +21492,7 @@ function BinaryFieldBinding({ propertyKey, value, setValue, error, showError, di
|
|
|
21039
21492
|
className: cls("flex flex-col gap-2 p-4 rounded-xl border", defaultBorderMixin),
|
|
21040
21493
|
children: [/* @__PURE__ */ jsx(TextField, {
|
|
21041
21494
|
size,
|
|
21495
|
+
"aria-label": `${property.name ?? propertyKey} base64 value`,
|
|
21042
21496
|
value: text,
|
|
21043
21497
|
onChange,
|
|
21044
21498
|
autoFocus: autoFocus ?? true,
|
|
@@ -23985,6 +24439,6 @@ function getFullIdPath(propertyKey, propertyNamespace) {
|
|
|
23985
24439
|
return idToPropertiesPath(propertyNamespace ? `${propertyNamespace}.${propertyKey}` : propertyKey);
|
|
23986
24440
|
}
|
|
23987
24441
|
//#endregion
|
|
23988
|
-
export { getEntityViewWidth as $,
|
|
24442
|
+
export { getEntityViewWidth as $, SkeletonPropertyComponent as $n, resolveCollectionPathIds$1 as $t, SelectFieldBinding as A, ArrayOfStringsPreview as An, saveImportedEntities as At, useSelectionDialog as B, useSidePanel as Bn, FieldCaption as Bt, getDefaultFieldId as C, UserPreview as Cn, isReferenceProperty as Cr, convertDataToEntity as Ct, TextFieldBinding as D, KeyValuePreview as Dn, useImportConfig as Dt, VectorFieldBinding as E, DatePreview as En, getInferenceType as Et, MapFieldBinding as F, ArrayOfReferencesPreview as Fn, parseCsvToObjects as Ft, useTopLevelNavigation as G, getEntityPreviewKeys as Gn, mergeEntityActions as Gt, SideDialogs as H, useCollectionRegistryController as Hn, BreadcrumbsProvider as Ht, KeyValueFieldBinding as I, InlineEntityListPreview as In, unflattenObject as It, useBuildCollectionRegistryController as J, ArrayPropertyPreview as Jn, getCollectionPathsCombinations as Jt, useResolvedViews as K, getEntityTitlePropertyKey as Kn, addInitialSlash as Kt, DateTimeFieldBinding as L, ReferencePreview as Ln, ArrayContainer as Lt, ReferenceFieldBinding as M, ArrayEnumPreview as Mn, convertFileToJson as Mt, MultiSelectFieldBinding as N, ArrayOfStorageComponentsPreview as Nn, detectCsvDelimiter as Nt, SwitchFieldBinding as O, MapPropertyPreview as On, ImportSaveInProgress as Ot, MarkdownEditorFieldBinding as P, RelationPreview as Pn, parseCsvRows as Pt, buildSidePanelsFromUrl as Q, StorageThumbnailInternal as Qn, removeTrailingSlash$1 as Qt, BlockFieldBinding as R, EntityPreviewBinding as Rn, PropertyConfigBadge as Rt, getDefaultFieldConfig as S, PropertyPreview as Sn, getResolvedPropertyInPath as Sr, DataNewPropertiesMapping as St, getFieldId as T, BooleanPreview as Tn, processValueMapping as Tt, useBuildUrlController as U, getUserLabel as Un, resolveEntityAction as Ut, SelectionTableBinding as V, CollectionRegistryContext as Vn, useBreadcrumbsController as Vt, useBuildNavigationStateController as W, useResolvedUser as Wn, resolveEntityView as Wt, resolveNavigationFrom as X, EnumValuesChip as Xn, removeInitialAndTrailingSlashes$1 as Xt, useHistory as Y, StringPropertyPreview as Yn, getLastSegment$1 as Yt, useResolvedNavigationFrom as Z, StorageThumbnail as Zn, removeInitialSlash as Zt, useCollectionsConfigController as _, ReadOnlyFieldBinding as _n, getDefaultPropertiesOrder as _r, CollectionViewActions as _t, namespaceToPropertiesPath as a, SelectableTable as an, ImagePreview as ar, removeEmptyContainers as at, PropertyFieldBinding as b, FieldHelperText as bn, getPropertiesWithPropertiesOrder as br, useCollectionEditorDialogsState as bt, buildCollectionGenerationCallback as c, UrlContext as cn, FormSections as cr, copyEntityAction as ct, fromSerializableCollectionConfigs as d, useNavigationStateController as dn, FieldBlock as dr, resetPasswordAction as dt, resolveOpenEntityMode as en, renderSkeletonCaptionText as er, useBuildSidePanel as et, fromSerializableProperties as f, useSideDialogsController as fn, LABEL_ICON_SIZE as fr, CreationResultDialog as ft, toSerializableProperty as g, useClearRestoreValue as gn, getBracketNotation as gr, EntityCardBinding as gt, toSerializableProperties as h, ArrayCustomShapedFieldBinding as hn, PropertyIdCopyTooltip as hr, CollectionCardViewBinding as ht, namespaceToPropertiesOrderPath as i, CollectionTableBinding as in, UrlComponentPreview as ir, getInitialEntityValues as it, RepeatFieldBinding as j, ArrayPropertyEnumPreview as jn, ImportFileUpload as jt, StorageUploadFieldBinding as k, ArrayOneOfPreview as kn, IMPORT_BATCH_SIZE as kt, validateCollectionJson as l, useUrlController as ln, FormRail as lr, deleteEntityAction as lt, toSerializableCollectionConfig as m, SelectableTableContext as mn, spanClass as mr, EntityViewBinding as mt, getFullIdPath as n, useSelectionController as nn, renderSkeletonImageThumbnail as nr, extractTouchedValues as nt, CollectionGenerationApiError as o, CollectionRowActions as on, sanitizeUrl as or, zodToFormErrors as ot, fromSerializableProperty as p, SideDialogsControllerContext as pn, isSelfLabellingProperty as pr, DetailViewBinding as pt, useResolvedCollections as q, getEntityTitlePropertyKeyForEntity as qn, getCollectionBySlugWithin as qt, idToPropertiesPath as r, VirtualTableInput$1 as rn, renderSkeletonText as rr, getChanges as rt, DEFAULT_COLLECTION_GENERATION_ENDPOINT as s, useAdminContext as sn, EmptyValue as sr, CollectionViewBinding as st, getFullId as t, resolveViewMode as tn, renderSkeletonIcon as tr, EditViewBinding as tt, fromSerializableCollectionConfig as u, NavigationStateContext as un, RecordMeta as ur, editEntityAction as ut, EntityFormBinding as v, LabelWithIconAndTooltip as vn, getIconForProperty as vr, useCollectionEditorController as vt, getFieldConfig as w, NumberPropertyPreview as wn, isRelationProperty as wr, flattenEntry as wt, DEFAULT_FIELD_CONFIGS as x, ArrayOfMapsPreview as xn, getPropertyInPath$1 as xr, ImportNewPropertyFieldPreview as xt, EntityForm as y, LabelWithIcon as yn, getIconForWidget as yr, ConfigControllerProvider as yt, ArrayOfReferencesFieldBinding as z, SidePanelControllerContext as zn, SearchIconsView as zt };
|
|
23989
24443
|
|
|
23990
|
-
//# sourceMappingURL=util-
|
|
24444
|
+
//# sourceMappingURL=util-C-D5yD2n.js.map
|