@rebasepro/admin 0.13.0 → 0.13.1-canary.g18cfeb7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/dist/{CollectionEditorDialog-CjXkWZ64.js → CollectionEditorDialog-CUFXevVg.js} +3 -3
  2. package/dist/{CollectionEditorDialog-CjXkWZ64.js.map → CollectionEditorDialog-CUFXevVg.js.map} +1 -1
  3. package/dist/{PropertyEditView-W8I3YV89.js → PropertyEditView-DDUC7giL.js} +2 -2
  4. package/dist/{PropertyEditView-W8I3YV89.js.map → PropertyEditView-DDUC7giL.js.map} +1 -1
  5. package/dist/{RouterCollectionsStudioView-BsLTNoqX.js → RouterCollectionsStudioView-vzS848Gf.js} +9 -13
  6. package/dist/RouterCollectionsStudioView-vzS848Gf.js.map +1 -0
  7. package/dist/collection_editor/serializable_types.d.ts +1 -0
  8. package/dist/collection_editor_ui.js +4 -4
  9. package/dist/components/AdditionalFieldValue.d.ts +25 -0
  10. package/dist/components/CollectionViewBinding/CollectionViewBinding.d.ts +16 -0
  11. package/dist/components/CollectionViewBinding/SlotValue.d.ts +63 -0
  12. package/dist/components/CollectionViewBinding/SplitListCloseButton.d.ts +17 -4
  13. package/dist/components/CollectionViewBinding/SplitListShowButton.d.ts +12 -0
  14. package/dist/components/CollectionViewBinding/usePreviewSlots.d.ts +55 -5
  15. package/dist/components/DetailViewBinding.d.ts +15 -0
  16. package/dist/components/DrawerNavigationGroup.d.ts +9 -6
  17. package/dist/components/DrawerNavigationItem.d.ts +16 -6
  18. package/dist/components/EditViewBinding.d.ts +30 -1
  19. package/dist/components/EntityDisplayHeader.d.ts +53 -0
  20. package/dist/components/EntityIdentityBar.d.ts +43 -1
  21. package/dist/components/EntityViewBinding.d.ts +60 -6
  22. package/dist/components/RebaseLayout.d.ts +1 -1
  23. package/dist/components/RebaseShell.d.ts +1 -1
  24. package/dist/components/RelationSelector.d.ts +8 -1
  25. package/dist/components/UserSelector.d.ts +8 -1
  26. package/dist/components/app/Scaffold.d.ts +9 -3
  27. package/dist/components/index.d.ts +2 -0
  28. package/dist/{export-J79rMoNj.js → export-5bl2EoSs.js} +2 -2
  29. package/dist/{export-J79rMoNj.js.map → export-5bl2EoSs.js.map} +1 -1
  30. package/dist/form/components/FieldBlock.d.ts +58 -4
  31. package/dist/form/components/FormRail.d.ts +13 -0
  32. package/dist/form/components/FormSections.d.ts +15 -1
  33. package/dist/form/components/useRailVisible.d.ts +5 -4
  34. package/dist/form/form_utils.d.ts +38 -1
  35. package/dist/{history-D7L_JlLO.js → history-BuKKAhoY.js} +4 -4
  36. package/dist/{history-D7L_JlLO.js.map → history-BuKKAhoY.js.map} +1 -1
  37. package/dist/hooks/useEntityDisplay.d.ts +102 -0
  38. package/dist/hooks/useEntityDisplayTitle.d.ts +18 -9
  39. package/dist/{import-BvYl--U4.js → import-BhdWs34H.js} +3 -3
  40. package/dist/{import-BvYl--U4.js.map → import-BhdWs34H.js.map} +1 -1
  41. package/dist/index.d.ts +2 -2
  42. package/dist/index.js +152 -57
  43. package/dist/index.js.map +1 -1
  44. package/dist/preview/compact_text.d.ts +23 -0
  45. package/dist/preview/components/BooleanPreview.d.ts +3 -1
  46. package/dist/preview/property_previews/ArrayOfStringsPreview.d.ts +1 -1
  47. package/dist/preview/property_previews/ArrayOneOfPreview.d.ts +1 -1
  48. package/dist/preview/property_previews/ArrayPropertyPreview.d.ts +1 -1
  49. package/dist/preview/property_previews/MapPropertyPreview.d.ts +1 -1
  50. package/dist/preview/property_previews/StringPropertyPreview.d.ts +1 -1
  51. package/dist/routes/RebaseRoute.d.ts +1 -1
  52. package/dist/types/components/PropertyPreviewProps.d.ts +19 -0
  53. package/dist/util/number_format.d.ts +14 -0
  54. package/dist/util/previews.d.ts +9 -2
  55. package/dist/{util-GiwPgxnL.js → util-DNG7iK0w.js} +3756 -2300
  56. package/dist/util-DNG7iK0w.js.map +1 -0
  57. package/package.json +9 -9
  58. package/src/collection_editor/serializable_types.ts +1 -0
  59. package/src/collection_editor/ui/collection_editor/CollectionsStudioView.tsx +9 -11
  60. package/src/components/AdditionalFieldValue.tsx +68 -0
  61. package/src/components/CollectionTableBinding/CollectionTableBinding.tsx +7 -8
  62. package/src/components/CollectionTableBinding/PropertyTableCell.tsx +2 -2
  63. package/src/components/CollectionTableBinding/column_utils.tsx +15 -6
  64. package/src/components/CollectionTableBinding/internal/popup_field/PopupFormField.tsx +2 -2
  65. package/src/components/CollectionViewBinding/BoardCardBinding.tsx +26 -27
  66. package/src/components/CollectionViewBinding/CollectionListViewBinding.tsx +723 -348
  67. package/src/components/CollectionViewBinding/CollectionViewActions.tsx +1 -1
  68. package/src/components/CollectionViewBinding/CollectionViewBinding.tsx +38 -8
  69. package/src/components/CollectionViewBinding/EntityCardBinding.tsx +23 -36
  70. package/src/components/CollectionViewBinding/SlotValue.tsx +156 -0
  71. package/src/components/CollectionViewBinding/SplitListCloseButton.tsx +23 -7
  72. package/src/components/CollectionViewBinding/SplitListShowButton.tsx +28 -0
  73. package/src/components/CollectionViewBinding/SplitListView.tsx +33 -2
  74. package/src/components/CollectionViewBinding/useBoardDataController.tsx +12 -3
  75. package/src/components/CollectionViewBinding/usePreviewSlots.ts +183 -64
  76. package/src/components/CollectionViewBinding/utils.ts +7 -4
  77. package/src/components/DefaultDrawer.tsx +19 -23
  78. package/src/components/DetailViewBinding.tsx +93 -56
  79. package/src/components/DrawerNavigationGroup.tsx +16 -39
  80. package/src/components/DrawerNavigationItem.tsx +44 -16
  81. package/src/components/EditViewBinding.tsx +215 -42
  82. package/src/components/EntityDisplayHeader.tsx +170 -0
  83. package/src/components/EntityIdentityBar.tsx +163 -23
  84. package/src/components/EntityInspector.tsx +24 -6
  85. package/src/components/EntityPreviewBinding.tsx +58 -18
  86. package/src/components/EntityViewBinding.tsx +342 -41
  87. package/src/components/InlineEntityPreview.tsx +10 -10
  88. package/src/components/RebaseLayout.tsx +2 -2
  89. package/src/components/RebaseShell.tsx +2 -2
  90. package/src/components/ReferenceTable/SelectionTableBinding.tsx +4 -2
  91. package/src/components/RelationSelector.tsx +15 -4
  92. package/src/components/SideDialogs.tsx +15 -3
  93. package/src/components/SidePanelBinding.tsx +122 -66
  94. package/src/components/UserSelector.tsx +15 -4
  95. package/src/components/app/Scaffold.tsx +112 -10
  96. package/src/components/history/LastEditedByIndicator.tsx +7 -12
  97. package/src/components/index.ts +3 -0
  98. package/src/data_import/utils/data.ts +9 -1
  99. package/src/form/EntityForm.tsx +64 -52
  100. package/src/form/EntityFormBinding.tsx +24 -11
  101. package/src/form/PropertyFieldBinding.tsx +2 -2
  102. package/src/form/components/FieldBlock.tsx +80 -10
  103. package/src/form/components/FormRail.tsx +19 -2
  104. package/src/form/components/FormSections.tsx +32 -5
  105. package/src/form/components/StorageUploadProgress.tsx +9 -3
  106. package/src/form/components/useRailVisible.tsx +5 -4
  107. package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +2 -2
  108. package/src/form/field_bindings/BinaryFieldBinding.tsx +10 -5
  109. package/src/form/field_bindings/BlockFieldBinding.tsx +3 -3
  110. package/src/form/field_bindings/GeopointFieldBinding.tsx +23 -16
  111. package/src/form/field_bindings/MapFieldBinding.tsx +2 -2
  112. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +13 -7
  113. package/src/form/field_bindings/MultiSelectFieldBinding.tsx +7 -1
  114. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +7 -1
  115. package/src/form/field_bindings/ReferenceFieldBinding.tsx +1 -1
  116. package/src/form/field_bindings/RelationFieldBinding.tsx +5 -2
  117. package/src/form/field_bindings/RepeatFieldBinding.tsx +3 -3
  118. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +2 -2
  119. package/src/form/field_bindings/UserSelectFieldBinding.tsx +4 -1
  120. package/src/form/field_bindings/VectorFieldBinding.tsx +6 -4
  121. package/src/form/form_utils.ts +78 -0
  122. package/src/hooks/navigation/useBuildCollectionRegistryController.tsx +29 -5
  123. package/src/hooks/useEntityDisplay.tsx +262 -0
  124. package/src/hooks/useEntityDisplayTitle.tsx +27 -38
  125. package/src/index.ts +9 -1
  126. package/src/preview/PropertyPreview.tsx +25 -5
  127. package/src/preview/compact_text.ts +68 -0
  128. package/src/preview/components/BooleanPreview.tsx +5 -2
  129. package/src/preview/components/StorageThumbnail.tsx +5 -2
  130. package/src/preview/property_previews/ArrayOfRelationsPreview.tsx +6 -3
  131. package/src/preview/property_previews/ArrayOfStringsPreview.tsx +26 -1
  132. package/src/preview/property_previews/ArrayOneOfPreview.tsx +10 -1
  133. package/src/preview/property_previews/ArrayPropertyPreview.tsx +10 -1
  134. package/src/preview/property_previews/MapPropertyPreview.tsx +52 -1
  135. package/src/preview/property_previews/NumberPropertyPreview.tsx +9 -1
  136. package/src/preview/property_previews/StringPropertyPreview.tsx +8 -5
  137. package/src/routes/RebaseRoute.tsx +62 -4
  138. package/src/types/components/PropertyPreviewProps.tsx +21 -0
  139. package/src/util/number_format.ts +46 -0
  140. package/src/util/previews.ts +8 -55
  141. package/src/util/property_utils.tsx +6 -1
  142. package/dist/RouterCollectionsStudioView-BsLTNoqX.js.map +0 -1
  143. package/dist/util-GiwPgxnL.js.map +0 -1
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rebasepro/admin",
3
3
  "type": "module",
4
- "version": "0.13.0",
4
+ "version": "0.13.1-canary.g18cfeb7",
5
5
  "description": "Rebase CMS — content management views, forms, and routing",
6
6
  "funding": {
7
7
  "url": "https://github.com/sponsors/rebaseco"
@@ -83,14 +83,14 @@
83
83
  "react-dropzone": "^19.1.1",
84
84
  "react-use-measure": "^2.1.7",
85
85
  "zod": "^4.4.3",
86
- "@rebasepro/admin-types": "0.13.0",
87
- "@rebasepro/app": "0.13.0",
88
- "@rebasepro/common": "0.13.0",
89
- "@rebasepro/inference": "0.13.0",
90
- "@rebasepro/forms": "0.13.0",
91
- "@rebasepro/ui": "0.13.0",
92
- "@rebasepro/types": "0.13.0",
93
- "@rebasepro/utils": "0.13.0"
86
+ "@rebasepro/admin-types": "0.13.1-canary.g18cfeb7",
87
+ "@rebasepro/common": "0.13.1-canary.g18cfeb7",
88
+ "@rebasepro/forms": "0.13.1-canary.g18cfeb7",
89
+ "@rebasepro/inference": "0.13.1-canary.g18cfeb7",
90
+ "@rebasepro/app": "0.13.1-canary.g18cfeb7",
91
+ "@rebasepro/utils": "0.13.1-canary.g18cfeb7",
92
+ "@rebasepro/types": "0.13.1-canary.g18cfeb7",
93
+ "@rebasepro/ui": "0.13.1-canary.g18cfeb7"
94
94
  },
95
95
  "peerDependencies": {
96
96
  "react": ">=19.2.7",
@@ -155,6 +155,7 @@ export interface SerializableAdminRelationOptions extends SerializableAdminBaseO
155
155
  fixedFilter?: FilterValues<string>;
156
156
  includeId?: boolean;
157
157
  includeEntityLink?: boolean;
158
+ renderInForm?: boolean;
158
159
  }
159
160
 
160
161
  /** JSON-serializable version of `AdminArrayOptions`. */
@@ -17,6 +17,7 @@ import { CollectionsConfigController } from "../../types/config_controller";
17
17
  import { CollectionStudioView } from "./CollectionStudioView";
18
18
  import type { CollectionEditorExtensionProps } from "../../extensibility_types";
19
19
  import type { AdminCollection } from "@rebasepro/admin-types";
20
+ import { readStoredString, writeStoredString } from "@rebasepro/utils";
20
21
 
21
22
  export interface CollectionsStudioViewProps extends CollectionEditorExtensionProps {
22
23
  configController: CollectionsConfigController;
@@ -77,21 +78,18 @@ export function CollectionsStudioView({
77
78
  const collections = collectionsProp ?? configController.collections ?? [];
78
79
 
79
80
  // ── Sidebar sizing ──────────────────────────────────────────────────
81
+ // Checked, not just parsed: `parseFloat` answers NaN for anything it cannot
82
+ // read, and a NaN pane size lays the sidebar out to nothing — with the bad
83
+ // value still in storage on reload. `SplitListView.getSavedPanelSize` is the
84
+ // same read, done this way.
80
85
  const [sidebarSize, setSidebarSize] = useState(() => {
81
- try {
82
- const saved = localStorage.getItem("rebase_collections_editor_sidebar_size");
83
- return saved !== null ? parseFloat(saved) : 25;
84
- } catch (e) {
85
- return 25;
86
- }
86
+ const saved = readStoredString("rebase_collections_editor_sidebar_size");
87
+ const parsed = saved === null ? NaN : parseFloat(saved);
88
+ return Number.isFinite(parsed) && parsed > 0 && parsed < 100 ? parsed : 25;
87
89
  });
88
90
 
89
91
  useEffect(() => {
90
- try {
91
- localStorage.setItem("rebase_collections_editor_sidebar_size", sidebarSize.toString());
92
- } catch (e) {
93
- // ignore local storage error
94
- }
92
+ writeStoredString("rebase_collections_editor_sidebar_size", sidebarSize.toString());
95
93
  }, [sidebarSize]);
96
94
 
97
95
  return (
@@ -0,0 +1,68 @@
1
+ import type { AdditionalFieldDelegate, RebaseContext } from "@rebasepro/admin-types";
2
+ import type { Entity } from "@rebasepro/types";
3
+ import React, { useEffect, useState } from "react";
4
+
5
+ export interface AdditionalFieldValueProps<M extends Record<string, unknown>> {
6
+ field: AdditionalFieldDelegate<M>;
7
+ entity: Entity<M>;
8
+ context: RebaseContext;
9
+ }
10
+
11
+ /**
12
+ * Renders an `additionalFields` entry that supplies a `value` rather than a
13
+ * `Builder`.
14
+ *
15
+ * `value` is typed `string | number | Promise<string | number>`, and the promise
16
+ * arm has been part of that type from the start — the CSV export awaits it
17
+ * correctly. Every *renderer* called `.toString()` on the result instead, so an
18
+ * async value drew the literal text `[object Promise]` in the cell: a shape the
19
+ * type promised, that worked in one of its two consumers, and failed silently in
20
+ * the other. That is what made the obvious port of a FireCMS column — one that
21
+ * fetches a title from a subcollection — look supported and not be.
22
+ *
23
+ * Three call sites had the same three lines copied between them (the table cell,
24
+ * the form and the read-only record), which is why the bug reached all three.
25
+ * Now there is one.
26
+ */
27
+ export function AdditionalFieldValue<M extends Record<string, unknown>>({
28
+ field,
29
+ entity,
30
+ context
31
+ }: AdditionalFieldValueProps<M>) {
32
+
33
+ const [resolved, setResolved] = useState<string | number | undefined>(() => {
34
+ const value = field.value?.({ entity, context });
35
+ // A synchronous value is available on the first render, so it never
36
+ // flashes empty on its way in.
37
+ return isPromise(value) ? undefined : value;
38
+ });
39
+
40
+ useEffect(() => {
41
+ const value = field.value?.({ entity, context });
42
+ if (!isPromise(value)) {
43
+ setResolved(value);
44
+ return;
45
+ }
46
+
47
+ // The row may scroll out, or the record change, before this lands.
48
+ let live = true;
49
+ value.then(
50
+ result => {
51
+ if (live) setResolved(result);
52
+ },
53
+ error => {
54
+ if (live) setResolved(undefined);
55
+ console.warn(`[rebase] Additional field "${field.key}" failed to resolve:`, error);
56
+ }
57
+ );
58
+ return () => {
59
+ live = false;
60
+ };
61
+ }, [field, entity, context]);
62
+
63
+ return <>{resolved === undefined || resolved === null ? null : String(resolved)}</>;
64
+ }
65
+
66
+ function isPromise(value: unknown): value is Promise<string | number | undefined> {
67
+ return typeof (value as Promise<unknown> | undefined)?.then === "function";
68
+ }
@@ -1,4 +1,5 @@
1
1
  import type { AdditionalFieldDelegate, AdminCollection } from "@rebasepro/admin-types";
2
+ import { AdditionalFieldValue } from "../AdditionalFieldValue";
2
3
  import React, { useCallback, useMemo, useRef } from "react";
3
4
  import { Entity, User, Property } from "@rebasepro/types";
4
5
  import { CollectionSize, RebaseContext } from "@rebasepro/admin-types";
@@ -16,7 +17,7 @@ import { propertiesToColumns } from "./column_utils";
16
17
  import { ErrorView } from "@rebasepro/app";
17
18
  import { SelectableTable } from "../SelectableTable/SelectableTable";
18
19
  import { cls } from "@rebasepro/ui";
19
- import { getRowHeight } from "@rebasepro/app";
20
+ import { getRowHeight, isDisabled } from "@rebasepro/app";
20
21
  import { getValueInPath } from "@rebasepro/utils";
21
22
 
22
23
  /**
@@ -146,7 +147,7 @@ export const CollectionTableBinding = function CollectionTableBinding<M extends
146
147
  propertyKey,
147
148
  entity
148
149
  }) ?? columnCustom?.resolvedProperty;
149
- if (!property?.admin?.disabled) {
150
+ if (!property || !isDisabled(property)) {
150
151
  disabled = false;
151
152
  }
152
153
 
@@ -214,12 +215,10 @@ export const CollectionTableBinding = function CollectionTableBinding<M extends
214
215
 
215
216
  const child: React.ReactNode = Builder
216
217
  ? <Builder entity={entity} context={context}/>
217
- : <>
218
- {additionalField.value?.({
219
- entity,
220
- context: context as RebaseContext
221
- })?.toString()}
222
- </>;
218
+ : <AdditionalFieldValue
219
+ field={additionalField}
220
+ entity={entity}
221
+ context={context as RebaseContext}/>;
223
222
 
224
223
  return (
225
224
  <EntityTableCell
@@ -18,7 +18,7 @@ import { EntityTableCellActions } from "./internal/EntityTableCellActions";
18
18
  import { useSelectableTableController } from "../SelectableTable/SelectableTableContext";
19
19
  import { useClearRestoreValue } from "../../form/useClearRestoreValue";
20
20
  import { getRowHeight } from "@rebasepro/app";
21
- import { isReadOnly } from "@rebasepro/app";
21
+ import { isDisabled, isReadOnly } from "@rebasepro/app";
22
22
  import { TableRelationField } from "./fields/TableRelationField";
23
23
  import { TableRelationSelectorField } from "./fields/TableRelationSelectorField";
24
24
 
@@ -95,7 +95,7 @@ export const PropertyTableCell = React.memo<PropertyTableCellProps<any>>(
95
95
  const customPreview = Boolean(property.admin?.Preview);
96
96
  const readOnlyProperty = isReadOnly(property);
97
97
  const disabledTooltip: string | undefined = typeof property.admin?.disabled === "object" ? property.admin?.disabled.disabledMessage : undefined;
98
- const disabled = readonly || disabledProp || Boolean(property.admin?.disabled);
98
+ const disabled = readonly || disabledProp || isDisabled(property);
99
99
 
100
100
  const validation = useMemo(() => mapPropertyToZod({
101
101
  property,
@@ -39,15 +39,24 @@ export function propertiesToColumns<M extends Record<string, unknown>>({ propert
39
39
  const disabledFilter = Boolean(fixedFilter);
40
40
  return Object.entries<Property>(properties)
41
41
  .flatMap(([key, property]) => getColumnKeysForProperty(property, key))
42
- .map(({
42
+ .flatMap(({
43
43
  key,
44
44
  disabled
45
45
  }) => {
46
- const property = getResolvedPropertyInPath(properties, key) as Property;
47
- if (!property)
48
- throw Error("Internal error: no property found in path " + key);
46
+ const property = getResolvedPropertyInPath(properties, key) as Property | undefined;
47
+ // One unresolvable key used to throw, and this runs inside the
48
+ // memo that builds the table's columns so a single bad column
49
+ // took down the header, the rows and the empty state together,
50
+ // leaving a blank pane with nothing to attribute it to. A column
51
+ // that cannot be resolved is one column the table cannot offer;
52
+ // say so and carry the rest. Same choice, and now the same
53
+ // behaviour, as `getSortablePropertyOptions` below.
54
+ if (!property) {
55
+ console.warn(`No property found in path "${key}" — skipping that column.`);
56
+ return [];
57
+ }
49
58
  const filterable = filterableProperty(property, false, engine);
50
- return {
59
+ return [{
51
60
  key: key as string,
52
61
  align: getTableCellAlignment(property),
53
62
  icon: getIconForProperty(property, "small"),
@@ -63,7 +72,7 @@ export function propertiesToColumns<M extends Record<string, unknown>>({ propert
63
72
  AdditionalHeaderWidget: AdditionalHeaderWidget
64
73
  ? ({ onHover }: { onHover: boolean }) => <AdditionalHeaderWidget property={property} propertyKey={key} onHover={onHover}/>
65
74
  : undefined
66
- } satisfies VirtualTableColumn;
75
+ } satisfies VirtualTableColumn];
67
76
  });
68
77
  }
69
78
 
@@ -16,7 +16,7 @@ import { getPropertyInPath } from "../../../../util/property_utils";
16
16
  import { PropertyFieldBinding, zodToFormErrors } from "../../../../form";
17
17
  import { useAuthController, useCustomizationController, useData, useRebaseContext } from "@rebasepro/app";
18
18
  import type { OnCellValueChangeParams } from "@rebasepro/app";
19
- import { isReadOnly } from "@rebasepro/app";
19
+ import { isDisabled, isReadOnly } from "@rebasepro/app";
20
20
  import type { AdminCollection } from "@rebasepro/admin-types";
21
21
 
22
22
  interface PopupFormFieldProps<M extends Record<string, unknown>> {
@@ -295,7 +295,7 @@ export function PopupFormFieldInternal<M extends Record<string, unknown>>({
295
295
  const fieldProps: PropertyFieldBindingProps<M> | undefined = propertyKey && property
296
296
  ? {
297
297
  propertyKey: propertyKey as string,
298
- disabled: isSubmitting || isReadOnly(property) || !!property.admin?.disabled,
298
+ disabled: isSubmitting || isReadOnly(property) || isDisabled(property),
299
299
  property,
300
300
  includeDescription: false,
301
301
  underlyingValueHasChanged: false,
@@ -6,7 +6,8 @@ import { PropertyPreview } from "../../preview";
6
6
  import { useAuthController, useCustomizationController } from "@rebasepro/app";
7
7
  import { IconForView } from "@rebasepro/app";
8
8
  import { BoardItemViewProps } from "@rebasepro/ui";
9
- import { useCollectionSlotKeys, resolveEntitySlots } from "./usePreviewSlots";
9
+ import { useCollectionSlotKeys, useEntitySlots } from "./usePreviewSlots";
10
+ import { SlotValue, TagChips } from "./SlotValue";
10
11
  import type { AdminCollection } from "@rebasepro/admin-types";
11
12
 
12
13
  export type BoardCardBindingProps<M extends Record<string, unknown> = Record<string, unknown>> = BoardItemViewProps<Entity<M>> & {
@@ -46,13 +47,10 @@ function BoardCardBindingInner<M extends Record<string, unknown> = Record<string
46
47
  customizationController.propertyConfigs
47
48
  );
48
49
 
49
- const slots = useMemo(
50
- () => resolveEntitySlots(
51
- entity as Entity<Record<string, unknown>>,
52
- collection as AdminCollection<Record<string, unknown>>,
53
- slotKeys
54
- ),
55
- [entity, collection, slotKeys]
50
+ const slots = useEntitySlots(
51
+ entity as Entity<Record<string, unknown>>,
52
+ collection as AdminCollection<Record<string, unknown>>,
53
+ slotKeys
56
54
  );
57
55
 
58
56
  const handleClick = useCallback((e: React.MouseEvent) => {
@@ -126,13 +124,7 @@ function BoardCardBindingInner<M extends Record<string, unknown> = Record<string
126
124
  selectionEnabled && "group-hover/card:opacity-30",
127
125
  selected && "opacity-0"
128
126
  )}>
129
- <PropertyPreview
130
- property={slots.image.property}
131
- propertyKey={slots.image.propertyKey}
132
- size="small"
133
- value={slots.image.value}
134
- fill={true}
135
- />
127
+ <SlotValue slot={slots.image} size="small" fill={true}/>
136
128
  </div>
137
129
  ) : (
138
130
  <div className={cls(
@@ -177,12 +169,7 @@ function BoardCardBindingInner<M extends Record<string, unknown> = Record<string
177
169
  {/* Title slot */}
178
170
  <div className="line-clamp-2 text-sm font-medium">
179
171
  {slots.title ? (
180
- <PropertyPreview
181
- propertyKey={slots.title.propertyKey}
182
- value={slots.title.value}
183
- property={slots.title.property}
184
- size="small"
185
- />
172
+ <SlotValue slot={slots.title} size="small"/>
186
173
  ) : (
187
174
  <span className="text-surface-500">{entity.id}</span>
188
175
  )}
@@ -193,12 +180,7 @@ function BoardCardBindingInner<M extends Record<string, unknown> = Record<string
193
180
  {typeof slots.subtitle.value === "string" ? (
194
181
  <Markdown source={slots.subtitle.value} size="small" />
195
182
  ) : (
196
- <PropertyPreview
197
- propertyKey={slots.subtitle.propertyKey}
198
- value={slots.subtitle.value}
199
- property={slots.subtitle.property}
200
- size="small"
201
- />
183
+ <SlotValue slot={slots.subtitle} size="small"/>
202
184
  )}
203
185
  </div>
204
186
  ) : (
@@ -228,6 +210,23 @@ function BoardCardBindingInner<M extends Record<string, unknown> = Record<string
228
210
  )}
229
211
  </div>
230
212
  )}
213
+
214
+ {/* Tags slot */}
215
+ {slots.tags && (
216
+ <div className="flex items-center gap-1 mt-1 flex-wrap">
217
+ <TagChips slot={slots.tags} max={3}/>
218
+ </div>
219
+ )}
220
+
221
+ {/* Date slot — a board card is a list row without the image, and
222
+ the row shows it. The status is deliberately not repeated
223
+ here: on a board grouped by it, the column header is already
224
+ saying it. */}
225
+ {slots.date && (
226
+ <div className="text-[10px] text-surface-500 dark:text-surface-400 mt-1">
227
+ {slots.date.formatted}
228
+ </div>
229
+ )}
231
230
  </div>
232
231
  </div>
233
232
  </div>