@rebasepro/admin 0.10.0 → 0.10.1-canary.14e53ae
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-BOlKGGg7.js → CollectionEditorDialog-BqVYCTmU.js} +3 -3
- package/dist/{CollectionEditorDialog-BOlKGGg7.js.map → CollectionEditorDialog-BqVYCTmU.js.map} +1 -1
- package/dist/{PropertyEditView-i51tFOfM.js → PropertyEditView-CIX85COE.js} +2 -2
- package/dist/{PropertyEditView-i51tFOfM.js.map → PropertyEditView-CIX85COE.js.map} +1 -1
- package/dist/{RouterCollectionsStudioView-Dh4W53PU.js → RouterCollectionsStudioView-_1RsFWS0.js} +4 -4
- package/dist/{RouterCollectionsStudioView-Dh4W53PU.js.map → RouterCollectionsStudioView-_1RsFWS0.js.map} +1 -1
- package/dist/collection_editor_ui.js +4 -4
- package/dist/components/CollectionViewBinding/usePreviewSlots.d.ts +2 -0
- package/dist/{export-BM6HPujv.js → export-CIi-Zrsn.js} +2 -2
- package/dist/{export-BM6HPujv.js.map → export-CIi-Zrsn.js.map} +1 -1
- package/dist/{history-CtIPJxvQ.js → history-BojXI3uS.js} +2 -2
- package/dist/{history-CtIPJxvQ.js.map → history-BojXI3uS.js.map} +1 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/useResolvedUsers.d.ts +8 -0
- package/dist/{import-DzQnT03v.js → import-SVoKfekq.js} +2 -2
- package/dist/{import-DzQnT03v.js.map → import-SVoKfekq.js.map} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +7 -7
- package/dist/preview/components/UserPreview.d.ts +3 -2
- package/dist/preview/property_previews/StringPropertyPreview.d.ts +1 -1
- package/dist/util/previews.d.ts +18 -0
- package/dist/{util-XFwQ9FEc.js → util-aJuPuy_1.js} +198 -65
- package/dist/util-aJuPuy_1.js.map +1 -0
- package/package.json +8 -8
- package/src/components/CollectionViewBinding/usePreviewSlots.ts +41 -10
- package/src/components/DetailViewBinding.tsx +12 -5
- package/src/components/EntityPreviewBinding.tsx +2 -2
- package/src/components/SideDialogs.tsx +1 -1
- package/src/form/EntityForm.tsx +12 -5
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useResolvedUsers.ts +143 -0
- package/src/index.ts +2 -1
- package/src/preview/components/ReferencePreview.tsx +2 -2
- package/src/preview/components/RelationPreview.tsx +2 -2
- package/src/preview/components/UserPreview.tsx +13 -4
- package/src/preview/property_previews/StringPropertyPreview.tsx +15 -9
- package/src/util/previews.ts +35 -46
- package/dist/util-XFwQ9FEc.js.map +0 -1
|
@@ -2,7 +2,7 @@ import * as React$1 from "react";
|
|
|
2
2
|
import React, { Suspense, createContext, createElement, forwardRef, lazy, memo, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
3
3
|
import { deepEqual } from "fast-equals";
|
|
4
4
|
import { Alert, AlertCircleIcon, AlertTriangleIcon, AlignLeftIcon, ArrowLeftIcon, ArrowRightToLineIcon, Badge, BooleanSwitchWithLabel, Button, CHIP_COLORS, 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, LinkIcon, ListIcon, ListOrderedIcon, ListView, LoaderIcon, LoadingButton, MailIcon, Markdown, Maximize2Icon, Menu, MenuItem, MinusIcon, MoreVerticalIcon, MultiSelect, MultiSelectItem, 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, TableView, Tabs, TextField, TextIcon, TextareaAutosize, ToggleButtonGroup, Tooltip, Trash2Icon, Typography, UploadIcon, UserIcon, VirtualTableDateField, VirtualTableInput, VirtualTableInput as VirtualTableInput$1, VirtualTableNumberInput, VirtualTableSwitch, VoteIcon, XCircleIcon, XIcon, cls, coolIconKeys, createVirtualTableSelectionStore, debounce, defaultBorderMixin, fieldBackgroundDisabledMixin, fieldBackgroundHoverMixin, fieldBackgroundInvisibleMixin, fieldBackgroundMixin, focusedDisabled, getColorSchemeForSeed, iconSize, lucideIcons, paperMixin, useDebouncedCallback, useInjectStyles, useOutsideAlerter } from "@rebasepro/ui";
|
|
5
|
-
import { CollectionRegistry, DEFAULT_ONE_OF_TYPE, DEFAULT_ONE_OF_VALUE, addInitialSlash, canReadCollection, enumToObjectEntries, getArrayResolvedProperties, getCollectionBySlugWithin, getCollectionPathsCombinations, getDefaultValueFor, getDefaultValueFortype, getDefaultValuesFor, getEntityImagePreviewPropertyKey, getLabelOrConfigFrom, getLastSegment as getLastSegment$1, getLocalChangesBackup, getNavigationEntriesFromPath, getParentReferencesFromPath, getReferenceFrom, getRelationFrom, getSubcollections, getTableName, isHidden, isPropertyBuilder, isReadOnly, normalizeToEntityRelation, removeInitialAndTrailingSlashes, removeInitialAndTrailingSlashes as removeInitialAndTrailingSlashes$1, removeInitialSlash, removeTrailingSlash, removeTrailingSlash as removeTrailingSlash$1, resolveArrayProperties, resolveCollectionPathIds, resolveCollectionPathIds as resolveCollectionPathIds$1, resolveDefaultSelectedView, resolveEnumValues, resolveFilterOperators, resolveProperty, resolveRelationProperty, resolveStorageFilenameString, resolveStoragePathString, resolveStorageSource } from "@rebasepro/common";
|
|
5
|
+
import { CollectionRegistry, DEFAULT_ONE_OF_TYPE, DEFAULT_ONE_OF_VALUE, addInitialSlash, canReadCollection, enumToObjectEntries, getArrayResolvedProperties, getCollectionBySlugWithin, getCollectionPathsCombinations, getDefaultValueFor, getDefaultValueFortype, getDefaultValuesFor, getEntityImagePreviewPropertyKey, getLabelOrConfigFrom, getLastSegment as getLastSegment$1, getLocalChangesBackup, getNavigationEntriesFromPath, getParentReferencesFromPath, getReferenceFrom, getRelationFrom, getSubcollections, getTableName, getTitlePropertyCandidates, getTitlePropertyKey, getTitlePropertyKeyForValues, isHidden, isPropertyBuilder, isReadOnly, looksLikeIdentifierValue, normalizeToEntityRelation, removeInitialAndTrailingSlashes, removeInitialAndTrailingSlashes as removeInitialAndTrailingSlashes$1, removeInitialSlash, removeTrailingSlash, removeTrailingSlash as removeTrailingSlash$1, resolveArrayProperties, resolveCollectionPathIds, resolveCollectionPathIds as resolveCollectionPathIds$1, resolveDefaultSelectedView, resolveEnumValues, resolveFilterOperators, resolveProperty, resolveRelationProperty, resolveStorageFilenameString, resolveStoragePathString, resolveStorageSource } from "@rebasepro/common";
|
|
6
6
|
import { ADDITIONAL_TAB_WIDTH, CONTAINER_FULL_WIDTH, CollectionScopeProvider, DEFAULT_PAGE_SIZE, ErrorTooltip, ErrorView, FORM_CONTAINER_WIDTH, IconForView, PluginProviderStack, StorageSourceContext, UnsavedChangesDialog, UserDisplay, buildEnumLabel, createFormexStub, deleteEntityWithCallbacks, flattenKeys, getColorScheme, getColumnKeysForProperty, getEntityFromCache, getEntityFromMemoryCache, getFormFieldKeys, getIcon, getRelationIncludeParams, getRowHeight, iconsSearch, removeEntityFromCache, removeEntityFromMemoryCache, resolveComponentRef, saveEntityToCache, saveEntityToMemoryCache, saveEntityWithCallbacks, useAnalyticsController, 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";
|
|
@@ -540,7 +540,7 @@ function EnumValuesChip({ enumValues, enumKey, size, className, children }) {
|
|
|
540
540
|
/**
|
|
541
541
|
* @group Preview components
|
|
542
542
|
*/
|
|
543
|
-
function StringPropertyPreview({ propertyKey, value, property, size }) {
|
|
543
|
+
function StringPropertyPreview({ propertyKey, value, property, size, textOnly }) {
|
|
544
544
|
const strValue = value;
|
|
545
545
|
if (property.enum) return /* @__PURE__ */ jsx(EnumValuesChip, {
|
|
546
546
|
enumKey: strValue,
|
|
@@ -559,10 +559,16 @@ function StringPropertyPreview({ propertyKey, value, property, size }) {
|
|
|
559
559
|
});
|
|
560
560
|
else {
|
|
561
561
|
if (!strValue) return /* @__PURE__ */ jsx(Fragment, {});
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
562
|
+
if (textOnly) {
|
|
563
|
+
const singleLine = strValue.replace(/\s*\n\s*/g, " ");
|
|
564
|
+
return size === "small" ? /* @__PURE__ */ jsx("span", {
|
|
565
|
+
className: "text-sm",
|
|
566
|
+
children: singleLine
|
|
567
|
+
}) : /* @__PURE__ */ jsx(Fragment, { children: singleLine });
|
|
568
|
+
}
|
|
569
|
+
return strValue.includes("\n") ? /* @__PURE__ */ jsx("div", {
|
|
570
|
+
className: cls("whitespace-pre-line line-clamp-3 overflow-hidden", size === "small" ? "text-sm" : ""),
|
|
571
|
+
children: strValue
|
|
566
572
|
}) : size === "small" ? /* @__PURE__ */ jsx("span", {
|
|
567
573
|
className: "text-sm",
|
|
568
574
|
children: strValue
|
|
@@ -776,38 +782,34 @@ function getEntityPreviewKeys(authController, targetCollection, fields, previewP
|
|
|
776
782
|
}).slice(0, limit);
|
|
777
783
|
}
|
|
778
784
|
}
|
|
785
|
+
/**
|
|
786
|
+
* The property that fills the title slot for a collection.
|
|
787
|
+
*
|
|
788
|
+
* Ranking lives in `@rebasepro/common` so every surface (list rows, cards,
|
|
789
|
+
* board cards, detail header, relation chips) agrees on what an entity is
|
|
790
|
+
* called. Identifiers are excluded from the property *schema* — primary keys,
|
|
791
|
+
* foreign keys, UUID columns — not from the key name.
|
|
792
|
+
*/
|
|
779
793
|
function getEntityTitlePropertyKey(collection, propertyConfigs) {
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
const lowerKey = key.toLowerCase();
|
|
800
|
-
if ([
|
|
801
|
-
"name",
|
|
802
|
-
"title",
|
|
803
|
-
"label",
|
|
804
|
-
"displayname",
|
|
805
|
-
"username"
|
|
806
|
-
].includes(lowerKey)) return key;
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
return firstStringCandidate;
|
|
794
|
+
return getTitlePropertyKey(collection);
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Same as {@link getEntityTitlePropertyKey}, but skips candidates that carry
|
|
798
|
+
* no readable value for this particular entity (empty, or an opaque id).
|
|
799
|
+
*/
|
|
800
|
+
function getEntityTitlePropertyKeyForEntity(collection, values, entityId) {
|
|
801
|
+
return getTitlePropertyKeyForValues(collection, values, entityId);
|
|
802
|
+
}
|
|
803
|
+
/**
|
|
804
|
+
* True when a property key points at a user picker, whose stored value is an
|
|
805
|
+
* auth user id that has to be resolved before it can be displayed.
|
|
806
|
+
*/
|
|
807
|
+
function isUserSelectProperty(collection, propertyKey) {
|
|
808
|
+
if (!propertyKey) return false;
|
|
809
|
+
const property = collection.properties[propertyKey];
|
|
810
|
+
if (!property || isPropertyBuilder(property)) return false;
|
|
811
|
+
const prop = property;
|
|
812
|
+
return prop.type === "string" && Boolean(prop.userSelect);
|
|
811
813
|
}
|
|
812
814
|
/**
|
|
813
815
|
* Attempt to turn a title value (which might be a relation, reference, date, array, or other complex object)
|
|
@@ -895,7 +897,7 @@ function EntityPreviewBindingData({ actions, collection: collectionProp, preview
|
|
|
895
897
|
customizationController.propertyConfigs
|
|
896
898
|
]);
|
|
897
899
|
if (!collection) return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(ErrorView, { error: `Collection not found: ${entity.path}` }) });
|
|
898
|
-
const titleProperty = includeTitle ?
|
|
900
|
+
const titleProperty = includeTitle ? getEntityTitlePropertyKeyForEntity(collection, entity.values, entity.id) : void 0;
|
|
899
901
|
const imagePropertyKey = includeImage ? getEntityImagePreviewPropertyKey(collection) : void 0;
|
|
900
902
|
const imageProperty = imagePropertyKey ? collection.properties[imagePropertyKey] : void 0;
|
|
901
903
|
const ofProp = imageProperty && "of" in imageProperty ? imageProperty.of : void 0;
|
|
@@ -1109,7 +1111,7 @@ function ReferencePreviewInternal(props) {
|
|
|
1109
1111
|
}
|
|
1110
1112
|
function ReferencePreviewExisting({ reference, collection, previewProperties, size, disabled, includeEntityLink, includeId, onClick, hover, textOnly }) {
|
|
1111
1113
|
const ResolvedEntityPreview = useComponentOverride("EntityPreview", EntityPreviewBinding);
|
|
1112
|
-
|
|
1114
|
+
useCustomizationController();
|
|
1113
1115
|
const { entity, dataLoading, dataLoadingError } = useFetch({
|
|
1114
1116
|
path: reference.path,
|
|
1115
1117
|
entityId: reference.id,
|
|
@@ -1152,7 +1154,7 @@ function ReferencePreviewExisting({ reference, collection, previewProperties, si
|
|
|
1152
1154
|
});
|
|
1153
1155
|
}
|
|
1154
1156
|
if (textOnly) {
|
|
1155
|
-
const titleProperty =
|
|
1157
|
+
const titleProperty = getEntityTitlePropertyKeyForEntity(collection, usedEntity.values, usedEntity.id);
|
|
1156
1158
|
const titleValue = titleProperty ? getValueInPath(usedEntity.values, titleProperty) : void 0;
|
|
1157
1159
|
return /* @__PURE__ */ jsx("span", {
|
|
1158
1160
|
className: "truncate",
|
|
@@ -1301,7 +1303,7 @@ function RelationPreviewInternal(props) {
|
|
|
1301
1303
|
function RelationPreviewExisting({ relation, collection, previewProperties, size, disabled, includeEntityLink, includeId, onClick, hover, textOnly }) {
|
|
1302
1304
|
const passedEntity = relation.data;
|
|
1303
1305
|
const ResolvedEntityPreview = useComponentOverride("EntityPreview", EntityPreviewBinding);
|
|
1304
|
-
|
|
1306
|
+
useCustomizationController();
|
|
1305
1307
|
const { entity, dataLoading, dataLoadingError } = useFetch({
|
|
1306
1308
|
path: relation.path,
|
|
1307
1309
|
entityId: passedEntity ? void 0 : relation.id,
|
|
@@ -1344,7 +1346,7 @@ function RelationPreviewExisting({ relation, collection, previewProperties, size
|
|
|
1344
1346
|
});
|
|
1345
1347
|
}
|
|
1346
1348
|
if (textOnly) {
|
|
1347
|
-
const titleProperty =
|
|
1349
|
+
const titleProperty = getEntityTitlePropertyKeyForEntity(collection, usedEntity.values, usedEntity.id);
|
|
1348
1350
|
const titleValue = titleProperty ? getValueInPath(usedEntity.values, titleProperty) : void 0;
|
|
1349
1351
|
return /* @__PURE__ */ jsx("span", {
|
|
1350
1352
|
className: "truncate",
|
|
@@ -1679,21 +1681,129 @@ function NumberPropertyPreview({ value, property, size }) {
|
|
|
1679
1681
|
});
|
|
1680
1682
|
}
|
|
1681
1683
|
//#endregion
|
|
1684
|
+
//#region src/hooks/useResolvedUsers.ts
|
|
1685
|
+
var caches = /* @__PURE__ */ new Map();
|
|
1686
|
+
function getCache(baseUrl) {
|
|
1687
|
+
let cache = caches.get(baseUrl);
|
|
1688
|
+
if (!cache) {
|
|
1689
|
+
cache = {
|
|
1690
|
+
users: /* @__PURE__ */ new Map(),
|
|
1691
|
+
pending: /* @__PURE__ */ new Map(),
|
|
1692
|
+
queue: /* @__PURE__ */ new Set()
|
|
1693
|
+
};
|
|
1694
|
+
caches.set(baseUrl, cache);
|
|
1695
|
+
}
|
|
1696
|
+
return cache;
|
|
1697
|
+
}
|
|
1698
|
+
async function fetchUsers(baseUrl, ids, token) {
|
|
1699
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
1700
|
+
for (const id of ids) resolved.set(id, null);
|
|
1701
|
+
const params = new URLSearchParams({ ids: ids.join(",") });
|
|
1702
|
+
const response = await fetch(`${baseUrl}/api/users?${params}`, { headers: token ? { Authorization: `Bearer ${token}` } : {} });
|
|
1703
|
+
if (!response.ok) return resolved;
|
|
1704
|
+
const data = await response.json();
|
|
1705
|
+
const rows = data.users ?? data.data ?? [];
|
|
1706
|
+
for (const row of rows) {
|
|
1707
|
+
const uid = row.uid ?? row.id;
|
|
1708
|
+
if (!uid) continue;
|
|
1709
|
+
resolved.set(uid, {
|
|
1710
|
+
uid,
|
|
1711
|
+
email: row.email ?? "",
|
|
1712
|
+
displayName: row.displayName ?? row.display_name ?? null,
|
|
1713
|
+
photoURL: row.photoURL ?? row.photo_url ?? null,
|
|
1714
|
+
providerId: "custom",
|
|
1715
|
+
isAnonymous: false,
|
|
1716
|
+
roles: row.roles ?? []
|
|
1717
|
+
});
|
|
1718
|
+
}
|
|
1719
|
+
return resolved;
|
|
1720
|
+
}
|
|
1721
|
+
function requestUser(baseUrl, uid, getAuthToken) {
|
|
1722
|
+
const cache = getCache(baseUrl);
|
|
1723
|
+
const existing = cache.pending.get(uid);
|
|
1724
|
+
if (existing) return existing;
|
|
1725
|
+
cache.queue.add(uid);
|
|
1726
|
+
const promise = new Promise((resolve, reject) => {
|
|
1727
|
+
if (cache.flush) clearTimeout(cache.flush);
|
|
1728
|
+
cache.flush = setTimeout(async () => {
|
|
1729
|
+
const ids = [...cache.queue];
|
|
1730
|
+
cache.queue.clear();
|
|
1731
|
+
cache.flush = void 0;
|
|
1732
|
+
try {
|
|
1733
|
+
const resolved = await fetchUsers(baseUrl, ids, await getAuthToken?.());
|
|
1734
|
+
for (const id of ids) {
|
|
1735
|
+
cache.users.set(id, resolved.get(id) ?? null);
|
|
1736
|
+
cache.pending.delete(id);
|
|
1737
|
+
}
|
|
1738
|
+
resolve();
|
|
1739
|
+
} catch (error) {
|
|
1740
|
+
for (const id of ids) cache.pending.delete(id);
|
|
1741
|
+
reject(error);
|
|
1742
|
+
}
|
|
1743
|
+
}, 0);
|
|
1744
|
+
});
|
|
1745
|
+
cache.pending.set(uid, promise);
|
|
1746
|
+
return promise;
|
|
1747
|
+
}
|
|
1748
|
+
/**
|
|
1749
|
+
* The auth user behind a `userSelect` value, or `null` while it is loading or
|
|
1750
|
+
* when it cannot be resolved.
|
|
1751
|
+
*/
|
|
1752
|
+
function useResolvedUser(uid) {
|
|
1753
|
+
const client = useRebaseClient();
|
|
1754
|
+
const { getAuthToken } = useAuthController();
|
|
1755
|
+
const baseUrl = client?.baseUrl;
|
|
1756
|
+
const [user, setUser] = useState(() => baseUrl && uid ? getCache(baseUrl).users.get(uid) ?? null : null);
|
|
1757
|
+
useEffect(() => {
|
|
1758
|
+
if (!baseUrl || !uid) {
|
|
1759
|
+
setUser(null);
|
|
1760
|
+
return;
|
|
1761
|
+
}
|
|
1762
|
+
const cache = getCache(baseUrl);
|
|
1763
|
+
if (cache.users.has(uid)) {
|
|
1764
|
+
setUser(cache.users.get(uid) ?? null);
|
|
1765
|
+
return;
|
|
1766
|
+
}
|
|
1767
|
+
let active = true;
|
|
1768
|
+
requestUser(baseUrl, uid, getAuthToken).then(() => {
|
|
1769
|
+
if (active) setUser(getCache(baseUrl).users.get(uid) ?? null);
|
|
1770
|
+
}).catch(() => {
|
|
1771
|
+
if (active) setUser(null);
|
|
1772
|
+
});
|
|
1773
|
+
return () => {
|
|
1774
|
+
active = false;
|
|
1775
|
+
};
|
|
1776
|
+
}, [
|
|
1777
|
+
baseUrl,
|
|
1778
|
+
uid,
|
|
1779
|
+
getAuthToken
|
|
1780
|
+
]);
|
|
1781
|
+
return user;
|
|
1782
|
+
}
|
|
1783
|
+
/** Human-readable label for a resolved user. */
|
|
1784
|
+
function getUserLabel(user) {
|
|
1785
|
+
return user.displayName || user.email || user.uid;
|
|
1786
|
+
}
|
|
1787
|
+
//#endregion
|
|
1682
1788
|
//#region src/preview/components/UserPreview.tsx
|
|
1683
1789
|
/**
|
|
1684
1790
|
* Preview component for displaying a user reference value.
|
|
1685
1791
|
*
|
|
1686
|
-
*
|
|
1687
|
-
*
|
|
1792
|
+
* Resolves the stored auth user id to a name (or email) the same way a
|
|
1793
|
+
* relation resolves its target, and falls back to the raw id only while the
|
|
1794
|
+
* lookup is in flight or when the user cannot be resolved.
|
|
1688
1795
|
*
|
|
1689
1796
|
* @group Preview components
|
|
1690
1797
|
*/
|
|
1691
1798
|
function UserPreview({ value }) {
|
|
1692
|
-
|
|
1799
|
+
const uid = typeof value === "string" && value ? value : void 0;
|
|
1800
|
+
const user = useResolvedUser(uid);
|
|
1801
|
+
if (!uid) return /* @__PURE__ */ jsx(EmptyValue, {});
|
|
1693
1802
|
return /* @__PURE__ */ jsx(Typography, {
|
|
1694
1803
|
variant: "caption",
|
|
1695
1804
|
color: "secondary",
|
|
1696
|
-
|
|
1805
|
+
className: "truncate",
|
|
1806
|
+
children: user ? getUserLabel(user) : uid
|
|
1697
1807
|
});
|
|
1698
1808
|
}
|
|
1699
1809
|
//#endregion
|
|
@@ -7295,8 +7405,8 @@ function EditorCollectionAction({ path, parentCollectionSlugs, parentEntityIds,
|
|
|
7295
7405
|
}
|
|
7296
7406
|
//#endregion
|
|
7297
7407
|
//#region src/components/CollectionViewBinding/CollectionViewActions.tsx
|
|
7298
|
-
var ImportCollectionAction = lazy(() => import("./import-
|
|
7299
|
-
var ExportCollectionAction = lazy(() => import("./export-
|
|
7408
|
+
var ImportCollectionAction = lazy(() => import("./import-SVoKfekq.js").then((n) => n.t).then((m) => ({ default: m.ImportCollectionAction })));
|
|
7409
|
+
var ExportCollectionAction = lazy(() => import("./export-CIi-Zrsn.js").then((n) => n.t).then((m) => ({ default: m.ExportCollectionAction })));
|
|
7300
7410
|
function CollectionViewActions({ collection, relativePath, parentCollectionSlugs, parentEntityIds, onNewClick, onMultipleDeleteClick, selectionEnabled, path, selectionController, tableController, collectionEntitiesCount, compact, children, openNewDocument }) {
|
|
7301
7411
|
const context = useCMSContext();
|
|
7302
7412
|
const { canCreate, canDelete } = usePermissions();
|
|
@@ -7408,7 +7518,8 @@ function CollectionViewActions({ collection, relativePath, parentCollectionSlugs
|
|
|
7408
7518
|
* Pure function — call inside `useMemo`.
|
|
7409
7519
|
*/
|
|
7410
7520
|
function resolveCollectionSlotKeys(collection, authController, propertyConfigs) {
|
|
7411
|
-
const
|
|
7521
|
+
const titleKeyCandidates = getTitlePropertyCandidates(collection);
|
|
7522
|
+
const titleKey = titleKeyCandidates[0];
|
|
7412
7523
|
const imageKey = getEntityImagePreviewPropertyKey(collection);
|
|
7413
7524
|
let statusKey;
|
|
7414
7525
|
if (!statusKey && collection.previewProperties) for (const key of collection.previewProperties) {
|
|
@@ -7473,6 +7584,7 @@ function resolveCollectionSlotKeys(collection, authController, propertyConfigs)
|
|
|
7473
7584
|
});
|
|
7474
7585
|
return {
|
|
7475
7586
|
titleKey,
|
|
7587
|
+
titleKeyCandidates,
|
|
7476
7588
|
imageKey,
|
|
7477
7589
|
subtitleKey: sortedPreviewKeys.length > 0 ? sortedPreviewKeys[0] : void 0,
|
|
7478
7590
|
relationKeys,
|
|
@@ -7499,6 +7611,24 @@ function resolveImageSlot(collection, imageKey, entity) {
|
|
|
7499
7611
|
value: resolvedValue
|
|
7500
7612
|
};
|
|
7501
7613
|
}
|
|
7614
|
+
/**
|
|
7615
|
+
* Walk the collection's ranked title candidates and return the first one that
|
|
7616
|
+
* holds something a person can read for this entity. Candidates are already
|
|
7617
|
+
* ordered and free of identifier properties; this only rejects values —
|
|
7618
|
+
* empties, and strings that turn out to be an id at runtime.
|
|
7619
|
+
*/
|
|
7620
|
+
function resolveEntityTitleKey(entity, candidates) {
|
|
7621
|
+
for (const key of candidates) {
|
|
7622
|
+
const value = getValueInPath(entity.values, key);
|
|
7623
|
+
if (value === void 0 || value === null || value === "") continue;
|
|
7624
|
+
if (typeof value === "string") {
|
|
7625
|
+
if (looksLikeIdentifierValue(value)) continue;
|
|
7626
|
+
if (value === String(entity.id)) continue;
|
|
7627
|
+
}
|
|
7628
|
+
return key;
|
|
7629
|
+
}
|
|
7630
|
+
return candidates[0];
|
|
7631
|
+
}
|
|
7502
7632
|
function formatDateValue$1(value) {
|
|
7503
7633
|
if (!value) return "";
|
|
7504
7634
|
const date = value instanceof Date ? value : new Date(value);
|
|
@@ -7538,20 +7668,21 @@ function useCollectionSlotKeys(collection, authController, propertyConfigs) {
|
|
|
7538
7668
|
* `React.memo` render functions or loops without violating hook rules.
|
|
7539
7669
|
*/
|
|
7540
7670
|
function resolveEntitySlots(entity, collection, slotKeys) {
|
|
7541
|
-
const { titleKey, imageKey, subtitleKey, relationKeys, statusKey, dateKey } = slotKeys;
|
|
7671
|
+
const { titleKey, titleKeyCandidates, imageKey, subtitleKey, relationKeys, statusKey, dateKey } = slotKeys;
|
|
7542
7672
|
const image = imageKey ? resolveImageSlot(collection, imageKey, entity) : void 0;
|
|
7673
|
+
const resolvedTitleKey = resolveEntityTitleKey(entity, titleKeyCandidates ?? (titleKey ? [titleKey] : []));
|
|
7543
7674
|
let title;
|
|
7544
|
-
if (
|
|
7545
|
-
const prop = collection.properties[
|
|
7546
|
-
const val = getValueInPath(entity.values,
|
|
7675
|
+
if (resolvedTitleKey) {
|
|
7676
|
+
const prop = collection.properties[resolvedTitleKey];
|
|
7677
|
+
const val = getValueInPath(entity.values, resolvedTitleKey);
|
|
7547
7678
|
if (prop) title = {
|
|
7548
7679
|
property: prop,
|
|
7549
|
-
propertyKey:
|
|
7680
|
+
propertyKey: resolvedTitleKey,
|
|
7550
7681
|
value: val
|
|
7551
7682
|
};
|
|
7552
7683
|
}
|
|
7553
7684
|
let subtitle;
|
|
7554
|
-
if (subtitleKey) {
|
|
7685
|
+
if (subtitleKey && subtitleKey !== resolvedTitleKey) {
|
|
7555
7686
|
const prop = collection.properties[subtitleKey];
|
|
7556
7687
|
const val = getValueInPath(entity.values, subtitleKey);
|
|
7557
7688
|
if (prop && val !== void 0 && val !== null && val !== "") subtitle = {
|
|
@@ -8740,7 +8871,7 @@ var MAIN_TAB_VALUE = "__main_##Q$SC^#S6";
|
|
|
8740
8871
|
var JSON_TAB_VALUE = "__json";
|
|
8741
8872
|
//#endregion
|
|
8742
8873
|
//#region src/components/DetailViewBinding.tsx
|
|
8743
|
-
var EntityHistoryView$1 = lazy(() => import("./history-
|
|
8874
|
+
var EntityHistoryView$1 = lazy(() => import("./history-BojXI3uS.js").then((m) => ({ default: m.EntityHistoryView })));
|
|
8744
8875
|
function DetailViewBinding({ entityId, ...props }) {
|
|
8745
8876
|
const { entity, dataLoading, dataLoadingError } = useFetch({
|
|
8746
8877
|
path: props.path,
|
|
@@ -8881,9 +9012,10 @@ function DetailViewBindingInner({ path, entityId, selectedTab: selectedTabProp,
|
|
|
8881
9012
|
]));
|
|
8882
9013
|
const formViewConfig = collection.formView;
|
|
8883
9014
|
const FormViewBuilder = formViewConfig?.Builder ? resolveComponentRef(formViewConfig.Builder) : null;
|
|
8884
|
-
const titlePropertyKey =
|
|
9015
|
+
const titlePropertyKey = getEntityTitlePropertyKeyForEntity(collection, usedEntity?.values, usedEntity?.id);
|
|
8885
9016
|
const rawTitle = usedEntity?.values && titlePropertyKey ? getValueInPath(usedEntity.values, titlePropertyKey) : void 0;
|
|
8886
|
-
const
|
|
9017
|
+
const titleUser = useResolvedUser(isUserSelectProperty(collection, titlePropertyKey) && typeof rawTitle === "string" ? rawTitle : void 0);
|
|
9018
|
+
const title = titleUser ? getUserLabel(titleUser) : rawTitle !== void 0 && rawTitle !== null ? resolveTitleToString(rawTitle) : collection.singularName ?? collection.name;
|
|
8887
9019
|
const nonActionCustomViews = useMemo(() => resolvedEntityViews.filter((e) => !e.includeActions), [resolvedEntityViews]);
|
|
8888
9020
|
const customViewsView = customViews && nonActionCustomViews.map((customView) => {
|
|
8889
9021
|
if (!customView) return null;
|
|
@@ -13303,7 +13435,7 @@ function EntityActionButton({ action, enabled, props }) {
|
|
|
13303
13435
|
}
|
|
13304
13436
|
//#endregion
|
|
13305
13437
|
//#region src/components/EditViewBinding.tsx
|
|
13306
|
-
var EntityHistoryView = lazy(() => import("./history-
|
|
13438
|
+
var EntityHistoryView = lazy(() => import("./history-BojXI3uS.js").then((m) => ({ default: m.EntityHistoryView })));
|
|
13307
13439
|
/**
|
|
13308
13440
|
* This is the default view that is used as the content of a side panel when
|
|
13309
13441
|
* a record is opened.
|
|
@@ -15410,7 +15542,7 @@ function SideDialogView({ offsetPosition, panel, panelIndex, isTopPanel }) {
|
|
|
15410
15542
|
maxWidth: "4xl",
|
|
15411
15543
|
scrollable: false,
|
|
15412
15544
|
children: panel && /* @__PURE__ */ jsx("div", {
|
|
15413
|
-
className: "flex flex-col h-[75vh] min-h-[500px] min-w-
|
|
15545
|
+
className: "flex flex-col h-[75vh] min-h-[500px] w-full min-w-0 max-w-full overflow-hidden",
|
|
15414
15546
|
children: /* @__PURE__ */ jsx(ErrorBoundary, { children: panel.component })
|
|
15415
15547
|
})
|
|
15416
15548
|
}) : /* @__PURE__ */ jsxs(Sheet, {
|
|
@@ -18852,7 +18984,7 @@ function zodToFormErrors(zodError) {
|
|
|
18852
18984
|
* @group Components
|
|
18853
18985
|
*/
|
|
18854
18986
|
function EntityForm({ path, entityId: entityIdProp, collection, onValuesModified, onIdChange, onSaved, entity, initialDirtyValues, onFormContextReady, forceActionsAtTheBottom, initialStatus, className, onStatusChange, onEntityChange, openEntityMode = "full_screen", formex: formexProp, disabled: disabledProp, Builder, EntityFormActionsComponent = EntityFormActions, showDefaultActions = true, showEntityPath = true, navigateBack: navigateBackProp, children, onSubmit: onSubmitProp, onValuesChangeDeferred: onValuesChangeDeferredProp, onReset: onResetProp, uniqueFieldValidator: uniqueFieldValidatorProp, beforeFields, afterFields, pluginActions: pluginActionsProp, computedInitialValues, hasLocalChanges: hasLocalChangesProp, localChangesData, manualApplyLocalChanges, localChangesCacheKey, onClearLocalChanges }) {
|
|
18855
|
-
|
|
18987
|
+
useCustomizationController();
|
|
18856
18988
|
const navigateBack = useCallback(() => {
|
|
18857
18989
|
if (navigateBackProp) navigateBackProp();
|
|
18858
18990
|
}, [navigateBackProp]);
|
|
@@ -19012,9 +19144,10 @@ function EntityForm({ path, entityId: entityIdProp, collection, onValuesModified
|
|
|
19012
19144
|
path
|
|
19013
19145
|
]);
|
|
19014
19146
|
const actionsDisabled = disabled || formex.isSubmitting || status === "existing" && !formex.dirty || Boolean(disabledProp);
|
|
19015
|
-
const titlePropertyKey =
|
|
19147
|
+
const titlePropertyKey = getEntityTitlePropertyKeyForEntity(collection, formex.values, entityId);
|
|
19016
19148
|
const rawTitle = formex.values && titlePropertyKey ? getValueInPath(formex.values, titlePropertyKey) : void 0;
|
|
19017
|
-
const
|
|
19149
|
+
const titleUser = useResolvedUser(isUserSelectProperty(collection, titlePropertyKey) && typeof rawTitle === "string" ? rawTitle : void 0);
|
|
19150
|
+
const title = titleUser ? getUserLabel(titleUser) : rawTitle !== void 0 && rawTitle !== null ? resolveTitleToString(rawTitle) : collection.singularName ?? collection.name;
|
|
19018
19151
|
const modified = formex.dirty;
|
|
19019
19152
|
useEffect(() => {
|
|
19020
19153
|
if (!autoSave) onValuesModified?.(modified, formex.values);
|
|
@@ -20280,6 +20413,6 @@ function getFullIdPath(propertyKey, propertyNamespace) {
|
|
|
20280
20413
|
return idToPropertiesPath(propertyNamespace ? `${propertyNamespace}.${propertyKey}` : propertyKey);
|
|
20281
20414
|
}
|
|
20282
20415
|
//#endregion
|
|
20283
|
-
export { useHistory as $,
|
|
20416
|
+
export { useHistory as $, SkeletonPropertyComponent as $n, SelectableTable as $t, VectorFieldBinding as A, ReferencePreview as An, unflattenObject as At, KeyValueFieldBinding as B, getDefaultPropertiesOrder as Bn, getCollectionPathsCombinations as Bt, zodToFormErrors as C, ArrayOneOfPreview as Cn, flattenEntry as Ct, getDefaultFieldId as D, ArrayOfStorageComponentsPreview as Dn, ImportSaveInProgress as Dt, getDefaultFieldConfig as E, ArrayEnumPreview as En, useImportConfig as Et, RepeatFieldBinding as F, useCollectionRegistryController as Fn, useBreadcrumbsController as Ft, SelectionTableBinding as G, getResolvedPropertyInPath as Gn, resolveCollectionPathIds$1 as Gt, BlockFieldBinding as H, getIconForWidget as Hn, removeInitialAndTrailingSlashes$1 as Ht, ReferenceFieldBinding as I, getEntityPreviewKeys as In, BreadcrumbsProvider as It, useBuildNavigationStateController as J, ArrayPropertyPreview as Jn, resolveEntityAction as Jt, SideDialogs as K, isReferenceProperty as Kn, resolveOpenEntityMode as Kt, MultiSelectFieldBinding as L, getEntityTitlePropertyKey as Ln, mergeEntityActions as Lt, SwitchFieldBinding as M, SidePanelControllerContext as Mn, PropertyConfigBadge as Mt, StorageUploadFieldBinding as N, useSidePanel as Nn, SearchIconsView as Nt, getFieldConfig as O, RelationPreview as On, ImportFileUpload as Ot, SelectFieldBinding as P, CollectionRegistryContext as Pn, FieldCaption as Pt, useBuildCollectionRegistryController as Q, StorageThumbnailInternal as Qn, CollectionTableBinding as Qt, MarkdownEditorFieldBinding as R, getEntityTitlePropertyKeyForEntity as Rn, addInitialSlash as Rt, removeEmptyContainers as S, MapPropertyPreview as Sn, convertDataToEntity as St, DEFAULT_FIELD_CONFIGS as T, ArrayPropertyEnumPreview as Tn, getInferenceType as Tt, ArrayOfReferencesFieldBinding as U, getPropertiesWithPropertiesOrder as Un, removeInitialSlash as Ut, DateTimeFieldBinding as V, getIconForProperty as Vn, getLastSegment$1 as Vt, useSelectionDialog as W, getPropertyInPath as Wn, removeTrailingSlash$1 as Wt, useResolvedViews as X, EnumValuesChip as Xn, useSelectionController as Xt, useTopLevelNavigation as Y, StringPropertyPreview as Yn, resolveEntityView as Yt, useResolvedCollections as Z, StorageThumbnail as Zn, VirtualTableInput$1 as Zt, EntityFormBinding as _, useResolvedUser as _n, useCollectionEditorController as _t, namespaceToPropertiesPath as a, useNavigationStateController as an, ImagePreview as ar, EditViewBinding as at, getChanges as b, DatePreview as bn, ImportNewPropertyFieldPreview as bt, buildCollectionGenerationCallback as c, SelectableTableContext as cn, LabelWithIconAndTooltip as cr, deleteEntityAction as ct, fromSerializableProperties as d, ReadOnlyFieldBinding as dn, FormLayout as dr, CreationResultDialog as dt, CollectionRowActions as en, renderSkeletonCaptionText as er, resolveNavigationFrom as et, fromSerializableProperty as f, FieldHelperText as fn, FormEntry as fr, DetailViewBinding as ft, useCollectionsConfigController as g, getUserLabel as gn, CollectionViewActions as gt, toSerializableProperty as h, UserPreview as hn, EntityCardBinding as ht, namespaceToPropertiesOrderPath as i, NavigationStateContext as in, UrlComponentPreview as ir, useBuildSidePanel as it, TextFieldBinding as j, EntityPreviewBinding as jn, ArrayContainer as jt, getFieldId as k, ArrayOfReferencesPreview as kn, convertFileToJson as kt, validateCollectionJson as l, ArrayCustomShapedFieldBinding as ln, PropertyIdCopyTooltip as lr, editEntityAction as lt, toSerializableProperties as m, PropertyPreview as mn, CollectionCardViewBinding as mt, getFullIdPath as n, UrlContext as nn, renderSkeletonImageThumbnail as nr, buildSidePanelsFromUrl as nt, CollectionGenerationApiError as o, useSideDialogsController as on, sanitizeUrl as or, CollectionViewBinding as ot, toSerializableCollectionConfig as p, ArrayOfMapsPreview as pn, EntityViewBinding as pt, useBuildUrlController as q, isRelationProperty as qn, resolveViewMode as qt, idToPropertiesPath as r, useUrlController as rn, renderSkeletonText as rr, getEntityViewWidth as rt, DEFAULT_COLLECTION_GENERATION_ENDPOINT as s, SideDialogsControllerContext as sn, EmptyValue as sr, copyEntityAction as st, getFullId as t, useCMSContext as tn, renderSkeletonIcon as tr, useResolvedNavigationFrom as tt, fromSerializableCollectionConfig as u, useClearRestoreValue as un, LabelWithIcon as ur, resetPasswordAction as ut, EntityForm as v, NumberPropertyPreview as vn, ConfigControllerProvider as vt, PropertyFieldBinding as w, ArrayOfStringsPreview as wn, processValueMapping as wt, getInitialEntityValues as x, KeyValuePreview as xn, DataNewPropertiesMapping as xt, extractTouchedValues as y, BooleanPreview as yn, useCollectionEditorDialogsState as yt, MapFieldBinding as z, getBracketNotation as zn, getCollectionBySlugWithin as zt };
|
|
20284
20417
|
|
|
20285
|
-
//# sourceMappingURL=util-
|
|
20418
|
+
//# sourceMappingURL=util-aJuPuy_1.js.map
|