@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.
- package/dist/components/forms/fields/join-field.test.d.ts +1 -0
- package/dist/components/ui/data-table.d.ts +4 -2
- package/dist/components/ui/render-cell.test.d.ts +1 -0
- package/dist/index.mjs +303 -121
- package/dist/lib/document-title.d.ts +16 -0
- package/dist/lib/document-title.test.d.ts +1 -0
- package/package.json +4 -4
|
@@ -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
|
-
|
|
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,
|
|
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,
|
|
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 ||
|
|
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
|
-
|
|
882
|
-
|
|
883
|
-
|
|
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,
|
|
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
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
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
|
|
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))
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
className: "dy-
|
|
3456
|
-
children: [
|
|
3457
|
-
"
|
|
3458
|
-
|
|
3459
|
-
"
|
|
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(
|
|
6427
|
-
|
|
6428
|
-
|
|
6429
|
-
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
"
|
|
6435
|
-
|
|
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
|
-
|
|
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:
|
|
12765
|
-
|
|
12766
|
-
|
|
12767
|
-
|
|
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 ?
|
|
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:
|
|
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.
|
|
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/
|
|
68
|
-
"@dyrected/
|
|
69
|
-
"@dyrected/
|
|
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",
|