@dyrected/admin 2.5.65 → 2.6.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.
@@ -0,0 +1 @@
1
+ export {};
@@ -3,7 +3,9 @@ import * as React from "react";
3
3
  interface DataTableProps<TData, TValue> {
4
4
  columns: ColumnDef<TData, TValue>[];
5
5
  data: TData[];
6
- searchKey?: string;
6
+ searchPlaceholder?: string;
7
+ searchValue?: string;
8
+ onSearchChange?: (value: string) => void;
7
9
  rowSelection?: any;
8
10
  onRowSelectionChange?: any;
9
11
  bulkActions?: (selectedIds: string[]) => React.ReactNode;
@@ -12,5 +14,5 @@ interface DataTableProps<TData, TValue> {
12
14
  initialColumnVisibility?: VisibilityState;
13
15
  hideViewButton?: boolean;
14
16
  }
15
- export declare function DataTable<TData, TValue>({ columns, data, searchKey, rowSelection: externalRowSelection, onRowSelectionChange, bulkActions, toolbarActions, persistenceKey, initialColumnVisibility, hideViewButton, }: DataTableProps<TData, TValue>): React.JSX.Element;
17
+ export declare function DataTable<TData, TValue>({ columns, data, searchPlaceholder, searchValue, onSearchChange, rowSelection: externalRowSelection, onRowSelectionChange, bulkActions, toolbarActions, persistenceKey, initialColumnVisibility, hideViewButton, }: DataTableProps<TData, TValue>): React.JSX.Element;
16
18
  export {};
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.mjs CHANGED
@@ -15,7 +15,7 @@ import * as SheetPrimitive from "@radix-ui/react-dialog";
15
15
  import { Toaster, toast } from "sonner";
16
16
  import * as PopoverPrimitive from "@radix-ui/react-popover";
17
17
  import * as SelectPrimitive from "@radix-ui/react-select";
18
- import { flexRender, getCoreRowModel, getFilteredRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
18
+ import { flexRender, getCoreRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
19
19
  import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
20
20
  import { DndContext, KeyboardSensor, PointerSensor, closestCenter, useSensor, useSensors } from "@dnd-kit/core";
21
21
  import { SortableContext, arrayMove, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
@@ -812,10 +812,65 @@ function SidebarInner({ schemas, isLoading, location, logout, isEmbedded, collap
812
812
  const collections = (schemas?.collections)?.filter((c) => !c?.admin?.hidden && !c?.slug.startsWith("platform_")) ?? [];
813
813
  const globals = (schemas?.globals)?.filter((g) => !g?.admin?.hidden && !g?.slug.startsWith("platform_")) ?? [];
814
814
  const uploadCollections = collections.filter((c) => c.upload);
815
+ const standardCollections = collections.filter((c) => !c.upload && !c.auth);
816
+ const authCollections = collections.filter((c) => !c.upload && c.auth);
815
817
  const groupLabel = (text) => !collapsed ? /* @__PURE__ */ jsx("p", {
816
818
  className: "dy-px-3 dy-mb-1.5 dy-text-[10px] dy-font-semibold dy-uppercase dy-tracking-widest dy-text-muted-foreground/50",
817
819
  children: text
818
820
  }) : /* @__PURE__ */ jsx("div", { className: "dy-my-2 dy-mx-3 dy-h-px dy-bg-border" });
821
+ const renderCollectionItem = (col) => {
822
+ const isReadOnly = col.access?.read && !col.access?.create && !col.access?.update && !col.access?.delete;
823
+ const navLabel = /* @__PURE__ */ jsxs("div", {
824
+ className: "dy-flex dy-items-center dy-gap-1.5 dy-min-w-0",
825
+ children: [/* @__PURE__ */ jsx("span", {
826
+ className: "dy-truncate",
827
+ children: col.labels?.plural ?? col.label ?? col.slug
828
+ }), !collapsed && /* @__PURE__ */ jsxs("div", {
829
+ className: "dy-flex dy-gap-1 dy-shrink-0",
830
+ children: [
831
+ col.auth && /* @__PURE__ */ jsx(Shield, { className: "dy-h-4 dy-w-4 dy-text-primary/70" }),
832
+ col.shared && /* @__PURE__ */ jsx(Share2, { className: "dy-h-4 dy-w-4 dy-text-purple-500/70" }),
833
+ isReadOnly && /* @__PURE__ */ jsx(Lock, { className: "dy-h-4 dy-w-4 dy-text-muted-foreground/40" })
834
+ ]
835
+ })]
836
+ });
837
+ return /* @__PURE__ */ jsx(NavItem, {
838
+ to: `/collections/${col.slug}`,
839
+ icon: resolveAdminIcon(col.admin?.icon, col.auth ? Users : Database),
840
+ label: navLabel,
841
+ active: location.pathname.startsWith(`/collections/${col.slug}`),
842
+ collapsed,
843
+ onClick: onNavigate
844
+ }, col.slug);
845
+ };
846
+ const renderCollectionSection = (sectionCollections, ungroupedLabel) => {
847
+ if (sectionCollections.length === 0) return null;
848
+ const groups = /* @__PURE__ */ new Map();
849
+ const ungrouped = [];
850
+ sectionCollections.forEach((col) => {
851
+ const groupName = col.admin?.group;
852
+ if (groupName) {
853
+ if (!groups.has(groupName)) groups.set(groupName, []);
854
+ groups.get(groupName).push(col);
855
+ return;
856
+ }
857
+ ungrouped.push(col);
858
+ });
859
+ return /* @__PURE__ */ jsxs("div", {
860
+ className: "dy-space-y-1",
861
+ children: [Array.from(groups.entries()).map(([groupName, cols]) => /* @__PURE__ */ jsx(NavGroup, {
862
+ label: groupName,
863
+ collapsed,
864
+ defaultExpanded: true,
865
+ children: cols.map((col) => renderCollectionItem(col))
866
+ }, groupName)), ungrouped.length > 0 && /* @__PURE__ */ jsx(NavGroup, {
867
+ label: ungroupedLabel,
868
+ collapsed,
869
+ defaultExpanded: true,
870
+ children: ungrouped.map((col) => renderCollectionItem(col))
871
+ })]
872
+ });
873
+ };
819
874
  const branding = schemas?.admin?.branding;
820
875
  const meta = schemas?.admin?.meta;
821
876
  return /* @__PURE__ */ jsxs("div", {
@@ -870,65 +925,17 @@ function SidebarInner({ schemas, isLoading, location, logout, isEmbedded, collap
870
925
  collapsed,
871
926
  onClick: onNavigate
872
927
  }, col.slug))] }),
873
- (isLoading || collections.filter((c) => !c.upload).length > 0) && /* @__PURE__ */ jsx("div", { children: isLoading ? /* @__PURE__ */ jsx("div", {
928
+ (isLoading || standardCollections.length > 0 || authCollections.length > 0) && /* @__PURE__ */ jsx("div", { children: isLoading ? /* @__PURE__ */ jsx("div", {
874
929
  className: "dy-space-y-1 dy-px-1",
875
930
  children: [
876
931
  1,
877
932
  2,
878
933
  3
879
934
  ].map((i) => /* @__PURE__ */ jsx("div", { className: cn("dy-h-8 dy-rounded-md dy-bg-muted/60 dy-animate-pulse", collapsed ? "dy-mx-1" : "dy-mx-2") }, i))
880
- }) : (() => {
881
- const nonUpload = collections.filter((col) => !col.upload);
882
- const groups = /* @__PURE__ */ new Map();
883
- const ungrouped = [];
884
- nonUpload.forEach((col) => {
885
- let g = col.admin?.group;
886
- if (!g && col.auth) g = "System";
887
- if (g) {
888
- if (!groups.has(g)) groups.set(g, []);
889
- groups.get(g).push(col);
890
- } else ungrouped.push(col);
891
- });
892
- const renderCollectionItem = (col) => {
893
- const isReadOnly = col.access?.read && !col.access?.create && !col.access?.update && !col.access?.delete;
894
- const navLabel = /* @__PURE__ */ jsxs("div", {
895
- className: "dy-flex dy-items-center dy-gap-1.5 dy-min-w-0",
896
- children: [/* @__PURE__ */ jsx("span", {
897
- className: "dy-truncate",
898
- children: col.labels?.plural ?? col.label ?? col.slug
899
- }), !collapsed && /* @__PURE__ */ jsxs("div", {
900
- className: "dy-flex dy-gap-1 dy-shrink-0",
901
- children: [
902
- col.auth && /* @__PURE__ */ jsx(Shield, { className: "dy-h-4 dy-w-4 dy-text-primary/70" }),
903
- col.shared && /* @__PURE__ */ jsx(Share2, { className: "dy-h-4 dy-w-4 dy-text-purple-500/70" }),
904
- isReadOnly && /* @__PURE__ */ jsx(Lock, { className: "dy-h-4 dy-w-4 dy-text-muted-foreground/40" })
905
- ]
906
- })]
907
- });
908
- return /* @__PURE__ */ jsx(NavItem, {
909
- to: `/collections/${col.slug}`,
910
- icon: resolveAdminIcon(col.admin?.icon, col.auth ? Users : Database),
911
- label: navLabel,
912
- active: location.pathname.startsWith(`/collections/${col.slug}`),
913
- collapsed,
914
- onClick: onNavigate
915
- }, col.slug);
916
- };
917
- return /* @__PURE__ */ jsxs("div", {
918
- className: "dy-space-y-1",
919
- children: [Array.from(groups.entries()).map(([groupName, cols]) => /* @__PURE__ */ jsx(NavGroup, {
920
- label: groupName,
921
- collapsed,
922
- defaultExpanded: true,
923
- children: cols.map((col) => renderCollectionItem(col))
924
- }, groupName)), ungrouped.length > 0 && /* @__PURE__ */ jsx(NavGroup, {
925
- label: "Collections",
926
- collapsed,
927
- defaultExpanded: true,
928
- children: ungrouped.map((col) => renderCollectionItem(col))
929
- })]
930
- });
931
- })() }),
935
+ }) : /* @__PURE__ */ jsxs("div", {
936
+ className: "dy-space-y-1",
937
+ children: [renderCollectionSection(standardCollections, "Collections"), renderCollectionSection(authCollections, "Auth")]
938
+ }) }),
932
939
  globals.length > 0 && /* @__PURE__ */ jsxs("div", { children: [groupLabel("Configuration"), /* @__PURE__ */ jsx("div", {
933
940
  className: "dy-space-y-0.5",
934
941
  children: globals.map((glob) => /* @__PURE__ */ jsx(NavItem, {
@@ -2238,9 +2245,8 @@ function usePreferences(key, defaultValue) {
2238
2245
  //#endregion
2239
2246
  //#region src/components/ui/data-table.tsx
2240
2247
  var EMPTY_VISIBILITY = {};
2241
- function DataTable({ columns, data, searchKey, rowSelection: externalRowSelection, onRowSelectionChange, bulkActions, toolbarActions, persistenceKey, initialColumnVisibility, hideViewButton = false }) {
2248
+ function DataTable({ columns, data, searchPlaceholder, searchValue, onSearchChange, rowSelection: externalRowSelection, onRowSelectionChange, bulkActions, toolbarActions, persistenceKey, initialColumnVisibility, hideViewButton = false }) {
2242
2249
  const [sorting, setSorting] = React$1.useState([]);
2243
- const [columnFilters, setColumnFilters] = React$1.useState([]);
2244
2250
  const [columnVisibility, setColumnVisibility] = usePreferences(persistenceKey ? `visibility_${persistenceKey}` : "temp_visibility", initialColumnVisibility ?? EMPTY_VISIBILITY);
2245
2251
  const [internalRowSelection, setInternalRowSelection] = React$1.useState({});
2246
2252
  const rowSelection = externalRowSelection || internalRowSelection;
@@ -2249,15 +2255,12 @@ function DataTable({ columns, data, searchKey, rowSelection: externalRowSelectio
2249
2255
  data,
2250
2256
  columns,
2251
2257
  onSortingChange: setSorting,
2252
- onColumnFiltersChange: setColumnFilters,
2253
2258
  getCoreRowModel: getCoreRowModel(),
2254
2259
  getSortedRowModel: getSortedRowModel(),
2255
- getFilteredRowModel: getFilteredRowModel(),
2256
2260
  onColumnVisibilityChange: setColumnVisibility,
2257
2261
  onRowSelectionChange: setRowSelection,
2258
2262
  state: {
2259
2263
  sorting,
2260
- columnFilters,
2261
2264
  columnVisibility,
2262
2265
  rowSelection
2263
2266
  }
@@ -2289,15 +2292,12 @@ function DataTable({ columns, data, searchKey, rowSelection: externalRowSelectio
2289
2292
  })
2290
2293
  })] })]
2291
2294
  }),
2292
- searchKey && (() => {
2293
- const col = table.getColumn(searchKey);
2294
- return /* @__PURE__ */ jsx(Input, {
2295
- placeholder: `Search by ${(col && typeof col.columnDef.header === "string" ? col.columnDef.header : searchKey).toLowerCase()}...`,
2296
- value: table.getColumn(searchKey)?.getFilterValue() ?? "",
2297
- onChange: (event) => table.getColumn(searchKey)?.setFilterValue(event.target.value),
2298
- className: "dy-order-2 dy-h-9 dy-w-full sm:dy-order-1 sm:dy-max-w-sm"
2299
- });
2300
- })(),
2295
+ onSearchChange ? /* @__PURE__ */ jsx(Input, {
2296
+ placeholder: searchPlaceholder || "Search...",
2297
+ value: searchValue ?? "",
2298
+ onChange: (event) => onSearchChange(event.target.value),
2299
+ className: "dy-order-2 dy-h-9 dy-w-full sm:dy-order-1 sm:dy-max-w-sm"
2300
+ }) : null,
2301
2301
  bulkActions && table.getFilteredSelectedRowModel().rows.length > 0 && /* @__PURE__ */ jsx("div", {
2302
2302
  className: "dy-order-3 dy-flex dy-w-full dy-items-center dy-gap-2 dy-animate-in dy-slide-in-from-left-2 sm:dy-order-2 sm:dy-w-auto",
2303
2303
  children: bulkActions(table.getFilteredSelectedRowModel().rows.map((r) => r.original.id))
@@ -3058,6 +3058,135 @@ function CsvImporter({ slug, schema, onClose }) {
3058
3058
  });
3059
3059
  }
3060
3060
  //#endregion
3061
+ //#region src/lib/document-title.ts
3062
+ var FALLBACK_FIELD_NAMES = [
3063
+ "title",
3064
+ "name",
3065
+ "label",
3066
+ "heading",
3067
+ "email",
3068
+ "subject"
3069
+ ];
3070
+ var MAX_TITLE_DEPTH = 8;
3071
+ function findField(collection, fieldName) {
3072
+ if (!collection?.fields || !fieldName) return void 0;
3073
+ return collection.fields.find((field) => field.name === fieldName);
3074
+ }
3075
+ function findNestedField(fields, fieldName) {
3076
+ if (!fields || !fieldName) return void 0;
3077
+ return fields.find((field) => field.name === fieldName);
3078
+ }
3079
+ function resolveCollectionTitleFieldName(collection) {
3080
+ const configured = findField(collection, collection?.admin?.useAsTitle);
3081
+ if (configured?.name) return configured.name;
3082
+ for (const fieldName of FALLBACK_FIELD_NAMES) {
3083
+ const field = findField(collection, fieldName);
3084
+ if (field?.name) return field.name;
3085
+ }
3086
+ return collection?.fields?.find((field) => !!field.name && field.type !== "join" && field.type !== "row")?.name;
3087
+ }
3088
+ function getObjectFallbackTitle(value) {
3089
+ for (const key of FALLBACK_FIELD_NAMES) {
3090
+ const candidate = value[key];
3091
+ if (typeof candidate === "string" && candidate.trim()) return candidate;
3092
+ }
3093
+ const idValue = value.id;
3094
+ if (typeof idValue === "string" && idValue.trim()) return idValue;
3095
+ if (typeof idValue === "number") return String(idValue);
3096
+ return null;
3097
+ }
3098
+ function resolveStructuredTitleFieldName(field) {
3099
+ if (!field?.fields?.length) return void 0;
3100
+ const configured = findNestedField(field.fields, field.admin?.useAsTitle);
3101
+ if (configured?.name) return configured.name;
3102
+ for (const fieldName of FALLBACK_FIELD_NAMES) {
3103
+ const candidate = findNestedField(field.fields, fieldName);
3104
+ if (candidate?.name) return candidate.name;
3105
+ }
3106
+ return field.fields.find((childField) => !!childField.name && childField.type !== "join" && childField.type !== "row")?.name;
3107
+ }
3108
+ function summarizeTitles(values, limit = 3) {
3109
+ const unique = values.filter(Boolean);
3110
+ if (unique.length === 0) return null;
3111
+ if (unique.length <= limit) return unique.join(", ");
3112
+ return `${unique.slice(0, limit).join(", ")} +${unique.length - limit} more`;
3113
+ }
3114
+ function resolveRelationshipValueTitle(value, field, collections, depth) {
3115
+ if (!field || field.type !== "relationship" || depth >= MAX_TITLE_DEPTH) return null;
3116
+ const relatedCollection = collections?.find((collection) => collection.slug === field.relationTo || collection.slug === field.collection);
3117
+ const relatedTitleFieldName = resolveCollectionTitleFieldName(relatedCollection);
3118
+ const relatedTitleField = findField(relatedCollection, relatedTitleFieldName);
3119
+ const resolveOne = (item) => {
3120
+ if (item == null) return null;
3121
+ if (typeof item === "string" || typeof item === "number") return String(item);
3122
+ if (typeof item !== "object" || Array.isArray(item)) return null;
3123
+ const record = item;
3124
+ if (relatedTitleFieldName && relatedTitleField) {
3125
+ const relatedValue = record[relatedTitleFieldName];
3126
+ const resolved = resolveValueTitleInternal(relatedValue, relatedTitleField, collections, depth + 1);
3127
+ if (resolved) return resolved;
3128
+ }
3129
+ return getObjectFallbackTitle(record);
3130
+ };
3131
+ if (Array.isArray(value)) return summarizeTitles(value.map((item) => resolveOne(item)).filter((item) => !!item));
3132
+ return resolveOne(value);
3133
+ }
3134
+ function resolveStructuredObjectTitle(value, field, collections, depth) {
3135
+ if (!field?.fields?.length || depth >= MAX_TITLE_DEPTH) return getObjectFallbackTitle(value);
3136
+ const titleFieldName = resolveStructuredTitleFieldName(field);
3137
+ const titleField = findNestedField(field.fields, titleFieldName);
3138
+ if (titleFieldName && titleField) {
3139
+ const resolved = resolveValueTitleInternal(value[titleFieldName], titleField, collections, depth + 1);
3140
+ if (resolved) return resolved;
3141
+ }
3142
+ return getObjectFallbackTitle(value);
3143
+ }
3144
+ function resolveStructuredArrayTitle(value, field, collections, depth) {
3145
+ if (depth >= MAX_TITLE_DEPTH) return null;
3146
+ return summarizeTitles(value.map((item) => {
3147
+ if (item == null) return null;
3148
+ if (typeof item !== "object" || Array.isArray(item)) return resolveValueTitleInternal(item, void 0, collections, depth + 1);
3149
+ return resolveStructuredObjectTitle(item, field, collections, depth + 1);
3150
+ }).filter((item) => !!item));
3151
+ }
3152
+ function resolveValueTitle(value, field, collections) {
3153
+ return resolveValueTitleInternal(value, field, collections, 0);
3154
+ }
3155
+ function resolveValueTitleInternal(value, field, collections, depth) {
3156
+ if (value == null) return null;
3157
+ if (field?.type === "relationship") {
3158
+ const relationTitle = resolveRelationshipValueTitle(value, field, collections, depth);
3159
+ if (relationTitle) return relationTitle;
3160
+ }
3161
+ if (field?.type === "array" && Array.isArray(value)) {
3162
+ const arrayTitle = resolveStructuredArrayTitle(value, field, collections, depth);
3163
+ if (arrayTitle) return arrayTitle;
3164
+ }
3165
+ if (field?.type === "object" && typeof value === "object" && !Array.isArray(value)) {
3166
+ const objectTitle = resolveStructuredObjectTitle(value, field, collections, depth);
3167
+ if (objectTitle) return objectTitle;
3168
+ }
3169
+ if (Array.isArray(value)) return summarizeTitles(value.map((item) => resolveValueTitleInternal(item, void 0, collections, depth + 1)).filter((item) => !!item));
3170
+ if (typeof value === "string") return value;
3171
+ if (typeof value === "number") return String(value);
3172
+ if (typeof value === "boolean") return value ? "true" : "false";
3173
+ if (typeof value === "object") return getObjectFallbackTitle(value);
3174
+ return null;
3175
+ }
3176
+ function resolveDocumentTitle(args) {
3177
+ const { entry, collection, collections } = args;
3178
+ if (!entry) return "";
3179
+ const titleFieldName = resolveCollectionTitleFieldName(collection);
3180
+ const titleField = findField(collection, titleFieldName);
3181
+ if (titleFieldName && titleField) {
3182
+ const title = resolveValueTitle(entry[titleFieldName], titleField, collections);
3183
+ if (title) return title;
3184
+ }
3185
+ const fallback = getObjectFallbackTitle(entry);
3186
+ if (fallback) return fallback;
3187
+ return String(entry.id ?? "");
3188
+ }
3189
+ //#endregion
3061
3190
  //#region src/lib/format.ts
3062
3191
  /** Normalize the shorthand string form into the object form. */
3063
3192
  function resolveNumberFormat(format) {
@@ -3409,6 +3538,23 @@ function RenderCell({ value, field, client, schemas }) {
3409
3538
  children: formatDate(value, field.admin?.format, field.type)
3410
3539
  })]
3411
3540
  });
3541
+ if (field.type === "icon") {
3542
+ if (!isAdminIconName(value)) return /* @__PURE__ */ jsx("span", {
3543
+ className: "dy-text-muted-foreground",
3544
+ children: "-"
3545
+ });
3546
+ return /* @__PURE__ */ jsxs("div", {
3547
+ className: "dy-inline-flex dy-items-center dy-gap-2 dy-text-foreground",
3548
+ children: [/* @__PURE__ */ jsx(resolveAdminIcon(value, Calendar), {
3549
+ className: "dy-h-4 dy-w-4 dy-shrink-0",
3550
+ "aria-hidden": "true"
3551
+ }), /* @__PURE__ */ jsx("span", {
3552
+ className: "dy-text-sm dy-font-medium",
3553
+ title: String(value),
3554
+ children: String(value)
3555
+ })]
3556
+ });
3557
+ }
3412
3558
  const relationTo = field.relationTo || field.collection;
3413
3559
  if (field.type === "image" || field.type === "relationship" && isUploadCollection(relationTo, schemas)) {
3414
3560
  if (!value) return /* @__PURE__ */ jsx("span", {
@@ -3430,8 +3576,7 @@ function RenderCell({ value, field, client, schemas }) {
3430
3576
  });
3431
3577
  }
3432
3578
  if (field.type === "relationship" && typeof value === "object") {
3433
- const relTo = field.relationTo || field.collection;
3434
- const displayValue = value[(schemas?.collections?.find((c) => c?.slug === relTo))?.admin?.useAsTitle || "title"] || value.name || value.id || "Unknown";
3579
+ const displayValue = resolveValueTitle(value, field, schemas?.collections) || "Unknown";
3435
3580
  return /* @__PURE__ */ jsx("div", {
3436
3581
  className: "dy-flex dy-items-center dy-gap-2",
3437
3582
  children: /* @__PURE__ */ jsx(Badge, {
@@ -3445,22 +3590,34 @@ function RenderCell({ value, field, client, schemas }) {
3445
3590
  className: "dy-text-[11px] dy-text-muted-foreground dy-font-mono dy-bg-muted/30 dy-px-1.5 dy-py-0.5 dy-rounded",
3446
3591
  children: formatJson(value, field.admin?.format)
3447
3592
  });
3448
- if (Array.isArray(value)) return /* @__PURE__ */ jsxs("div", {
3449
- className: "dy-flex dy-flex-wrap dy-gap-1",
3450
- children: [value.slice(0, 2).map((item, i) => /* @__PURE__ */ jsx(Badge, {
3451
- variant: "outline",
3452
- className: "dy-text-[10px] dy-px-1.5 dy-h-5",
3453
- children: typeof item === "object" ? item.title || item.name || item.id : String(item)
3454
- }, i)), value.length > 2 && /* @__PURE__ */ jsxs("span", {
3455
- className: "dy-text-[10px] dy-text-muted-foreground",
3456
- children: [
3457
- "+",
3458
- value.length - 2,
3459
- " more"
3460
- ]
3461
- })]
3462
- });
3593
+ if (Array.isArray(value)) {
3594
+ const structuredTitle = resolveValueTitle(value, field, schemas?.collections);
3595
+ if (structuredTitle) return /* @__PURE__ */ jsx("span", {
3596
+ className: "dy-text-[11px] dy-text-muted-foreground dy-font-medium dy-leading-tight",
3597
+ children: structuredTitle
3598
+ });
3599
+ return /* @__PURE__ */ jsxs("div", {
3600
+ className: "dy-flex dy-flex-wrap dy-gap-1",
3601
+ children: [value.slice(0, 2).map((item, i) => /* @__PURE__ */ jsx(Badge, {
3602
+ variant: "outline",
3603
+ className: "dy-text-[10px] dy-px-1.5 dy-h-5",
3604
+ children: typeof item === "object" ? item.title || item.name || item.id : String(item)
3605
+ }, i)), value.length > 2 && /* @__PURE__ */ jsxs("span", {
3606
+ className: "dy-text-[10px] dy-text-muted-foreground",
3607
+ children: [
3608
+ "+",
3609
+ value.length - 2,
3610
+ " more"
3611
+ ]
3612
+ })]
3613
+ });
3614
+ }
3463
3615
  if (typeof value === "object" && !Array.isArray(value)) {
3616
+ const structuredTitle = resolveValueTitle(value, field, schemas?.collections);
3617
+ if (structuredTitle) return /* @__PURE__ */ jsx("span", {
3618
+ className: "dy-text-[11px] dy-text-muted-foreground dy-font-medium dy-leading-tight",
3619
+ children: structuredTitle
3620
+ });
3464
3621
  const entries = Object.entries(value).filter(([, v]) => typeof v !== "object" && v !== null && v !== void 0).slice(0, 3);
3465
3622
  if (entries.length > 0) return /* @__PURE__ */ jsxs("span", {
3466
3623
  className: "dy-text-[11px] dy-text-muted-foreground dy-font-medium dy-leading-tight",
@@ -6389,6 +6546,16 @@ function JoinField({ schema, control }) {
6389
6546
  name: schema.name
6390
6547
  })?.docs;
6391
6548
  const getLabel = (item) => item[displayField] || item.name || item.slug || item.id;
6549
+ const showCreateButton = schema.admin?.showCreateButton !== false;
6550
+ const showViewButton = schema.admin?.showViewButton !== false;
6551
+ const handleCreateNew = () => {
6552
+ const params = new URLSearchParams({ [onField]: docId });
6553
+ navigate(`/collections/${targetCollection}/new?${params.toString()}`);
6554
+ };
6555
+ const handleViewAll = () => {
6556
+ const params = new URLSearchParams({ where: JSON.stringify({ [onField]: { equals: docId } }) });
6557
+ navigate(`/collections/${targetCollection}?${params.toString()}`);
6558
+ };
6392
6559
  if (!docId) return /* @__PURE__ */ jsxs("p", {
6393
6560
  className: "dy-text-xs dy-text-muted-foreground/60 dy-italic dy-py-2",
6394
6561
  children: [
@@ -6423,17 +6590,27 @@ function JoinField({ schema, control }) {
6423
6590
  " found."
6424
6591
  ]
6425
6592
  })
6426
- }), /* @__PURE__ */ jsxs(Button, {
6427
- type: "button",
6428
- variant: "outline",
6429
- size: "sm",
6430
- className: "dy-h-8 dy-text-[11px] dy-font-bold dy-rounded-lg dy-border-primary/20 hover:dy-bg-primary/5 hover:dy-text-primary dy-transition-all dy-shadow-sm",
6431
- onClick: () => navigate(`/collections/${targetCollection}/new?${onField}=${docId}`),
6432
- children: [
6433
- /* @__PURE__ */ jsx(Plus, { className: "dy-w-3 dy-h-3 dy-mr-1.5" }),
6434
- "Create new ",
6435
- targetSchema?.labels?.singular || targetCollection
6436
- ]
6593
+ }), (showCreateButton || showViewButton) && /* @__PURE__ */ jsxs("div", {
6594
+ className: cn("dy-flex dy-items-center dy-gap-3", showCreateButton && showViewButton ? "dy-justify-between" : showCreateButton ? "dy-justify-end" : "dy-justify-start"),
6595
+ children: [showViewButton && /* @__PURE__ */ jsxs(Button, {
6596
+ type: "button",
6597
+ variant: "ghost",
6598
+ size: "sm",
6599
+ className: "dy-h-8 dy-text-[11px] dy-font-semibold dy-rounded-lg dy-text-muted-foreground hover:dy-text-foreground hover:dy-bg-muted/60 dy-transition-all",
6600
+ onClick: handleViewAll,
6601
+ children: [/* @__PURE__ */ jsx(ExternalLink, { className: "dy-w-3 dy-h-3 dy-mr-1.5" }), "View all"]
6602
+ }), showCreateButton && /* @__PURE__ */ jsxs(Button, {
6603
+ type: "button",
6604
+ variant: "outline",
6605
+ size: "sm",
6606
+ className: "dy-h-8 dy-text-[11px] dy-font-bold dy-rounded-lg dy-border-primary/20 hover:dy-bg-primary/5 hover:dy-text-primary dy-transition-all dy-shadow-sm",
6607
+ onClick: handleCreateNew,
6608
+ children: [
6609
+ /* @__PURE__ */ jsx(Plus, { className: "dy-w-3 dy-h-3 dy-mr-1.5" }),
6610
+ "Create new ",
6611
+ targetSchema?.labels?.singular || targetCollection
6612
+ ]
6613
+ })]
6437
6614
  })]
6438
6615
  });
6439
6616
  }
@@ -10967,6 +11144,8 @@ function CollectionListPage({ slug }) {
10967
11144
  const [isImportOpen, setIsImportOpen] = React$1.useState(false);
10968
11145
  const [searchParams, setSearchParams] = useSearchParams();
10969
11146
  const whereParam = searchParams.get("where");
11147
+ const searchParam = searchParams.get("search") || "";
11148
+ const debouncedSearch = useDebouncedValue(searchParam.trim(), 250);
10970
11149
  const rules = React$1.useMemo(() => {
10971
11150
  if (!whereParam) return [];
10972
11151
  try {
@@ -11139,7 +11318,8 @@ function CollectionListPage({ slug }) {
11139
11318
  "collection",
11140
11319
  slug,
11141
11320
  page,
11142
- whereParam
11321
+ whereParam,
11322
+ debouncedSearch
11143
11323
  ],
11144
11324
  queryFn: () => {
11145
11325
  const queryParams = {
@@ -11150,6 +11330,7 @@ function CollectionListPage({ slug }) {
11150
11330
  if (whereParam) try {
11151
11331
  queryParams.where = JSON.parse(whereParam);
11152
11332
  } catch {}
11333
+ if (debouncedSearch) queryParams.search = debouncedSearch;
11153
11334
  return client.collection(slug).find(queryParams).exec();
11154
11335
  },
11155
11336
  enabled: !!client
@@ -11644,7 +11825,7 @@ function CollectionListPage({ slug }) {
11644
11825
  }),
11645
11826
  /* @__PURE__ */ jsx(PageHeader, {
11646
11827
  title: schema.labels?.plural || schema.slug,
11647
- description: `Manage your ${schema.labels?.plural || schema.slug} entries and update content.`,
11828
+ description: schema.admin?.description || `Manage your ${schema.labels?.plural || schema.slug} entries and update content.`,
11648
11829
  icon: resolveAdminIcon(schema.admin?.icon, schema.auth ? Users : Database),
11649
11830
  children: /* @__PURE__ */ jsxs("div", {
11650
11831
  className: "dy-flex dy-items-center dy-gap-2 dy-w-full sm:dy-w-auto",
@@ -11826,7 +12007,16 @@ function CollectionListPage({ slug }) {
11826
12007
  }) : /* @__PURE__ */ jsx(DataTable, {
11827
12008
  columns,
11828
12009
  data: response?.docs || [],
11829
- searchKey: schema.admin?.useAsTitle || schema.fields.find((f) => !f.admin?.hidden)?.name || "id",
12010
+ searchPlaceholder: `Search ${schema.labels?.plural || schema.slug}...`,
12011
+ searchValue: searchParam,
12012
+ onSearchChange: (value) => {
12013
+ setSearchParams((prev) => {
12014
+ if (value.trim()) prev.set("search", value);
12015
+ else prev.delete("search");
12016
+ return prev;
12017
+ }, { replace: true });
12018
+ setPage(1);
12019
+ },
11830
12020
  onRowSelectionChange: setRowSelection,
11831
12021
  rowSelection,
11832
12022
  hideViewButton: true,
@@ -12595,20 +12785,6 @@ function PreviewPaneWithNav({ previewUrl, data, mode, collectionSlug, documentId
12595
12785
  onFieldFocus: handleFieldFocus
12596
12786
  });
12597
12787
  }
12598
- function getEntryTitle(entry, schema) {
12599
- if (!entry) return "";
12600
- const titleField = schema?.admin?.useAsTitle || "title";
12601
- if (entry[titleField]) return String(entry[titleField]);
12602
- for (const f of [
12603
- "title",
12604
- "name",
12605
- "label",
12606
- "heading",
12607
- "email",
12608
- "subject"
12609
- ]) if (entry[f]) return String(entry[f]);
12610
- return String(entry.id);
12611
- }
12612
12788
  function EditEntryPage() {
12613
12789
  const { slug, id } = useParams();
12614
12790
  const [searchParams] = useSearchParams();
@@ -12761,12 +12937,10 @@ function EditEntryPage() {
12761
12937
  slug,
12762
12938
  debouncedSearchQuery
12763
12939
  ],
12764
- queryFn: async () => {
12765
- const displayField = schema?.admin?.useAsTitle || "title";
12766
- let qb = client.collection(slug).find({ limit: 50 });
12767
- if (debouncedSearchQuery) qb = qb.where({ [displayField]: { like: `%${debouncedSearchQuery}%` } });
12768
- return qb.exec();
12769
- },
12940
+ queryFn: () => client.collection(slug).find({
12941
+ limit: 50,
12942
+ search: debouncedSearchQuery || void 0
12943
+ }).exec(),
12770
12944
  enabled: !!client && !!slug && isEdit && switcherOpen
12771
12945
  });
12772
12946
  const syncPreviewData = useState(() => (next) => {
@@ -12933,7 +13107,11 @@ function EditEntryPage() {
12933
13107
  className: "dy-flex dy-items-center dy-gap-1 dy-text-base dy-font-serif dy-font-bold dy-tracking-tight dy-text-foreground hover:dy-bg-muted/80 dy-px-2 dy-py-1 dy-rounded-lg dy-transition-all dy-outline-none dy-min-w-0",
12934
13108
  children: [/* @__PURE__ */ jsx("span", {
12935
13109
  className: "dy-truncate dy-max-w-[150px] sm:dy-max-w-none dy-inline-block",
12936
- children: isEdit && entry ? getEntryTitle(entry, schema) : "..."
13110
+ children: isEdit && entry ? resolveDocumentTitle({
13111
+ entry,
13112
+ collection: schema,
13113
+ collections: schemas?.collections
13114
+ }) : "..."
12937
13115
  }), /* @__PURE__ */ jsx(ChevronDown, { className: "dy-h-4 dy-w-4 dy-text-muted-foreground/80 dy-shrink-0" })]
12938
13116
  }), /* @__PURE__ */ jsx(PopoverContent, {
12939
13117
  align: "start",
@@ -12957,7 +13135,11 @@ function EditEntryPage() {
12957
13135
  className: "dy-cursor-pointer dy-py-2 dy-px-3 dy-rounded-lg",
12958
13136
  children: /* @__PURE__ */ jsx("span", {
12959
13137
  className: "dy-text-sm dy-text-foreground",
12960
- children: getEntryTitle(sibling, schema)
13138
+ children: resolveDocumentTitle({
13139
+ entry: sibling,
13140
+ collection: schema,
13141
+ collections: schemas?.collections
13142
+ })
12961
13143
  })
12962
13144
  }, sibling.id)) }) })]
12963
13145
  })
@@ -0,0 +1,16 @@
1
+ import { Field } from '@dyrected/core';
2
+ type SchemaCollection = {
3
+ slug: string;
4
+ admin?: {
5
+ useAsTitle?: string;
6
+ };
7
+ fields?: Field[];
8
+ };
9
+ export declare function resolveCollectionTitleFieldName(collection: SchemaCollection | undefined): string | undefined;
10
+ export declare function resolveValueTitle(value: unknown, field: Field | undefined, collections?: SchemaCollection[]): string | null;
11
+ export declare function resolveDocumentTitle(args: {
12
+ entry: Record<string, unknown> | null | undefined;
13
+ collection: SchemaCollection | undefined;
14
+ collections?: SchemaCollection[];
15
+ }): string;
16
+ export {};
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dyrected/admin",
3
- "version": "2.5.65",
3
+ "version": "2.6.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"
@@ -64,9 +64,9 @@
64
64
  "tailwind-merge": "^3.5.0",
65
65
  "tailwindcss-animate": "^1.0.7",
66
66
  "zod": "^3.25.76",
67
- "@dyrected/knowledge": "^0.2.15",
68
- "@dyrected/core": "^2.5.65",
69
- "@dyrected/sdk": "^2.5.65"
67
+ "@dyrected/core": "^2.6.0",
68
+ "@dyrected/sdk": "^2.6.0",
69
+ "@dyrected/knowledge": "^0.2.15"
70
70
  },
71
71
  "peerDependencies": {
72
72
  "@tanstack/react-query": "^5.0.0",