@dyrected/admin 2.5.46 → 2.5.48

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 (3) hide show
  1. package/dist/admin.css +32 -119
  2. package/dist/index.mjs +390 -290
  3. package/package.json +4 -3
package/dist/index.mjs CHANGED
@@ -5,7 +5,7 @@ import { HashRouter, Link, MemoryRouter, Route, Routes, useLocation, useNavigate
5
5
  import { QueryClient, QueryClientProvider, useInfiniteQuery, useMutation, useQueries, useQuery, useQueryClient } from "@tanstack/react-query";
6
6
  import { createClient } from "@dyrected/sdk";
7
7
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
- import { AlertCircle, AlertTriangle, AlignCenter, AlignLeft, AlignRight, Archive, ArrowDown, ArrowLeft, ArrowRight, ArrowUp, ArrowUpRight, Bold, BookOpen, Braces, Calendar, Check, CheckCircle, CheckCircle2, ChevronDown, ChevronDownIcon, ChevronLeft, ChevronLeftIcon, ChevronRight, ChevronRightIcon, ChevronUp, ChevronsUpDown, Circle, Clock, Clock3, Code, Code2, Compass, Copy, Database, Download, ExternalLink, Eye, EyeOff, FileDown, FileIcon, FileText, FileUp, Filter, Globe, GripVertical, Heading1, Heading2, Image as Image$1, Info, Italic, KeyRound, Layers, LayoutDashboard, Library, Link as Link$1, List, ListOrdered, Loader2, Lock, LogOut, Mail, Menu, Monitor, Moon, MoreHorizontal, PanelLeftClose, PanelLeftOpen, Pencil, Play, Plus, Quote, RotateCcw, Save, Scissors, Search, Settings, Settings2, Share2, Shield, Smartphone, Sparkles, Strikethrough, Sun, Table, Trash2, Underline, Undo2, Upload, UploadCloud, Users, Video, Volume2, X, XCircle, icons } from "lucide-react";
8
+ import { AlertCircle, AlertTriangle, AlignCenter, AlignLeft, AlignRight, Archive, ArrowDown, ArrowLeft, ArrowRight, ArrowUp, ArrowUpRight, Bold, Braces, Calendar, Check, CheckCircle, CheckCircle2, ChevronDown, ChevronDownIcon, ChevronLeft, ChevronLeftIcon, ChevronRight, ChevronRightIcon, ChevronUp, ChevronsUpDown, Circle, Clock, Clock3, Code, Copy, Database, Download, ExternalLink, Eye, EyeOff, FileDown, FileIcon, FileText, FileUp, Filter, Globe, GripVertical, Heading1, Heading2, Image as Image$1, Info, Italic, KeyRound, Layers, LayoutDashboard, Library, Link as Link$1, List, ListOrdered, Loader2, Lock, LogOut, Mail, Menu, Monitor, Moon, MoreHorizontal, PanelLeftClose, PanelLeftOpen, Pencil, Play, Plus, Quote, RotateCcw, Save, Scissors, Search, Settings, Settings2, Share2, Shield, Smartphone, Sparkles, Strikethrough, Sun, Table, Trash2, Underline, Undo2, Upload, UploadCloud, Users, Video, Volume2, X, XCircle, icons } from "lucide-react";
9
9
  import { clsx } from "clsx";
10
10
  import { extendTailwindMerge } from "tailwind-merge";
11
11
  import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
@@ -19,13 +19,13 @@ import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
19
19
  import { DndContext, KeyboardSensor, PointerSensor, closestCenter, useSensor, useSensors } from "@dnd-kit/core";
20
20
  import { SortableContext, arrayMove, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
21
21
  import { CSS } from "@dnd-kit/utilities";
22
- import * as DialogPrimitive from "@radix-ui/react-dialog";
23
22
  import Papa from "papaparse";
24
23
  import * as ProgressPrimitive from "@radix-ui/react-progress";
25
24
  import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
26
25
  import jexl from "jexl";
27
26
  import { DataSheetGrid, checkboxColumn, keyColumn, textColumn } from "react-datasheet-grid";
28
27
  import "react-datasheet-grid/dist/style.css";
28
+ import * as SheetPrimitive from "@radix-ui/react-dialog";
29
29
  import { EditorContent, useEditor } from "@tiptap/react";
30
30
  import StarterKit from "@tiptap/starter-kit";
31
31
  import TextAlign from "@tiptap/extension-text-align";
@@ -50,12 +50,11 @@ import { DayPicker, getDefaultClassNames } from "react-day-picker";
50
50
  import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio";
51
51
  import * as SeparatorPrimitive from "@radix-ui/react-separator";
52
52
  import { Blurhash } from "react-blurhash";
53
+ import { GENERATE_CMS_PROMPT } from "@dyrected/knowledge";
53
54
  //#region src/providers/admin-auth.ts
54
55
  function getAdminCollectionSlug(schemas) {
55
56
  if (!schemas) return null;
56
- const requested = schemas.adminAuth?.collectionSlug;
57
- if (requested) return requested;
58
- return findCollection(schemas.collections, (collection) => collection.slug === "__admins")?.slug ?? findCollection(schemas.collections, (collection) => !!collection.auth)?.slug ?? null;
57
+ return findCollection(schemas.collections, (collection) => collection.slug === "__admins")?.slug ?? findCollection(schemas.collections, (collection) => collection.slug === schemas.adminAuth?.collectionSlug)?.slug ?? findCollection(schemas.collections, (collection) => !!collection.auth)?.slug ?? null;
59
58
  }
60
59
  function findCollection(collections, predicate) {
61
60
  return collections.find(predicate);
@@ -75,7 +74,8 @@ function DyrectedProvider({ children, apiKey: initialApiKey, baseUrl: initialBas
75
74
  const [apiKey, setApiKey] = useState(() => initialApiKey || (typeof window !== "undefined" ? localStorage.getItem("dyrected_key") : null) || void 0);
76
75
  const [siteId, setSiteId] = useState(() => initialSiteId || (typeof window !== "undefined" ? localStorage.getItem("dyrected_site_id") : null) || void 0);
77
76
  const [schemas, setSchemas] = useState(null);
78
- const [user, setUser] = useState(null);
77
+ const initialTokenUser = useMemo(() => initialToken ? decodeTokenPayload(initialToken) : null, [initialToken]);
78
+ const [user, setUser] = useState(() => initialTokenUser);
79
79
  const [authCollectionSlug, setAuthCollectionSlug] = useState(() => (typeof window !== "undefined" ? localStorage.getItem("dyrected_admin_auth_collection") : null) || null);
80
80
  const client = useMemo(() => {
81
81
  if (!baseUrl) return null;
@@ -106,6 +106,7 @@ function DyrectedProvider({ children, apiKey: initialApiKey, baseUrl: initialBas
106
106
  cancelled = true;
107
107
  };
108
108
  }, [client]);
109
+ const activeUser = initialTokenUser ?? user;
109
110
  useEffect(() => {
110
111
  if (initialToken && client) client.setToken(initialToken);
111
112
  }, [initialToken, client]);
@@ -143,32 +144,26 @@ function DyrectedProvider({ children, apiKey: initialApiKey, baseUrl: initialBas
143
144
  schemas
144
145
  ]);
145
146
  useEffect(() => {
146
- if (initialToken || !client || !schemas || user) return;
147
+ if (initialToken || !client || !schemas || activeUser) return;
147
148
  const token = localStorage.getItem("dyrected_token");
148
149
  const resolvedCollectionSlug = authCollectionSlug || getAdminCollectionSlug(schemas);
149
150
  if (!token || !resolvedCollectionSlug) return;
150
151
  client.setToken(token);
151
152
  client.collection(resolvedCollectionSlug).me().then((nextUser) => setUser(nextUser), () => setUser(null));
152
153
  }, [
154
+ activeUser,
153
155
  authCollectionSlug,
154
156
  client,
155
157
  initialToken,
156
- schemas,
157
- user
158
+ schemas
158
159
  ]);
159
160
  const logout = useCallback(() => {
160
- localStorage.removeItem("dyrected_url");
161
- localStorage.removeItem("dyrected_key");
162
- localStorage.removeItem("dyrected_site_id");
163
161
  localStorage.removeItem("dyrected_token");
164
162
  localStorage.removeItem("dyrected_admin_auth_collection");
165
- setBaseUrl("");
166
- setApiKey(void 0);
167
- setSiteId(void 0);
163
+ if (client) client.clearToken();
168
164
  setAuthCollectionSlug(null);
169
165
  setUser(null);
170
- setSchemas(null);
171
- }, []);
166
+ }, [client]);
172
167
  return /* @__PURE__ */ jsx(DyrectedContext.Provider, {
173
168
  value: {
174
169
  client,
@@ -183,13 +178,37 @@ function DyrectedProvider({ children, apiKey: initialApiKey, baseUrl: initialBas
183
178
  logout,
184
179
  isAuthenticated: !!baseUrl && !!apiKey,
185
180
  schemas,
186
- user,
181
+ user: activeUser,
187
182
  initialToken,
188
183
  components
189
184
  },
190
185
  children
191
186
  });
192
187
  }
188
+ function decodeTokenPayload(token) {
189
+ const payload = token.split(".")[1];
190
+ if (!payload) return null;
191
+ try {
192
+ const normalized = payload.replace(/-/g, "+").replace(/_/g, "/");
193
+ const padded = normalized.padEnd(Math.ceil(normalized.length / 4) * 4, "=");
194
+ const json = decodeURIComponent(atob(padded).split("").map((char) => `%${char.charCodeAt(0).toString(16).padStart(2, "0")}`).join(""));
195
+ const parsed = JSON.parse(json);
196
+ if (!parsed || typeof parsed !== "object") return null;
197
+ const user = parsed;
198
+ const roles = Array.isArray(user.roles) ? user.roles.filter((role) => typeof role === "string").map(normalizeCloudRole) : [];
199
+ const role = typeof user.role === "string" ? normalizeCloudRole(user.role) : void 0;
200
+ return {
201
+ ...user,
202
+ ...role ? { role } : {},
203
+ roles: roles.length > 0 ? roles : role ? [role] : roles
204
+ };
205
+ } catch {
206
+ return null;
207
+ }
208
+ }
209
+ function normalizeCloudRole(role) {
210
+ return role === "owner" ? "admin" : role;
211
+ }
193
212
  //#endregion
194
213
  //#region src/providers/query-provider.tsx
195
214
  function QueryProvider({ children }) {
@@ -616,7 +635,7 @@ function ThemeSelector({ collapsed = false, mobile = false }) {
616
635
  const OptionIcon = option.icon;
617
636
  return /* @__PURE__ */ jsxs(DropdownMenuRadioItem, {
618
637
  value: option.value,
619
- className: "dy-cursor-pointer",
638
+ className: "dy-cursor-pointer dy-space-x-2",
620
639
  children: [/* @__PURE__ */ jsx(OptionIcon, { className: "dy-h-4 dy-w-4" }), option.label]
621
640
  }, option.value);
622
641
  })
@@ -915,7 +934,7 @@ function AdminShell({ children, isEmbedded = false }) {
915
934
  className: "dy-ml-auto dy-flex dy-items-center dy-gap-1.5",
916
935
  children: [/* @__PURE__ */ jsx(ThemeSelector, { mobile: true }), user && /* @__PURE__ */ jsx("div", {
917
936
  className: "dy-flex dy-h-8 dy-w-8 dy-items-center dy-justify-center dy-rounded-full dy-bg-primary/10 dy-text-primary dy-font-semibold dy-text-xs dy-shrink-0",
918
- children: (user.name || user.email || "?")[0].toUpperCase()
937
+ children: (user.name || user.email || "?").charAt(0).toUpperCase()
919
938
  })]
920
939
  })
921
940
  ]
@@ -2047,59 +2066,6 @@ var Checkbox = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__
2047
2066
  }));
2048
2067
  Checkbox.displayName = CheckboxPrimitive.Root.displayName;
2049
2068
  //#endregion
2050
- //#region src/components/ui/dialog.tsx
2051
- var Dialog = DialogPrimitive.Root;
2052
- var DialogTrigger = DialogPrimitive.Trigger;
2053
- var DialogPortal = DialogPrimitive.Portal;
2054
- var DialogOverlay = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Overlay, {
2055
- ref,
2056
- className: cn("dy-fixed dy-inset-0 dy-z-50 dy-bg-black/40 dy-backdrop-blur-[2px] data-[state=open]:dy-animate-in data-[state=closed]:dy-animate-out data-[state=closed]:dy-fade-out-0 data-[state=open]:dy-fade-in-0", className),
2057
- ...props
2058
- }));
2059
- DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
2060
- var DialogContent = React$1.forwardRef(({ className, children, ...props }, ref) => {
2061
- const { resolvedTheme, themeClassName } = useAdminTheme();
2062
- return /* @__PURE__ */ jsx(DialogPortal, { children: /* @__PURE__ */ jsxs("div", {
2063
- className: themeClassName,
2064
- "data-theme": resolvedTheme,
2065
- children: [/* @__PURE__ */ jsx(DialogOverlay, {}), /* @__PURE__ */ jsxs(DialogPrimitive.Content, {
2066
- ref,
2067
- className: cn("dy-fixed dy-left-[50%] dy-top-[50%] dy-z-50 dy-grid dy-w-full dy-max-w-lg dy-translate-x-[-50%] dy-translate-y-[-50%] dy-gap-4 dy-border dy-bg-background dy-p-6 dy-shadow-2xl dy-duration-200 data-[state=open]:dy-animate-in data-[state=closed]:dy-animate-out data-[state=closed]:dy-fade-out-0 data-[state=open]:dy-fade-in-0 data-[state=closed]:dy-zoom-out-95 data-[state=open]:dy-zoom-in-95 data-[state=closed]:dy-slide-out-to-left-1/2 data-[state=closed]:dy-slide-out-to-top-[48%] data-[state=open]:dy-slide-in-from-left-1/2 data-[state=open]:dy-slide-in-from-top-[48%] sm:dy-rounded-xl", className),
2068
- ...props,
2069
- children: [children, /* @__PURE__ */ jsxs(DialogPrimitive.Close, {
2070
- className: "dy-absolute dy-right-4 dy-top-4 dy-rounded-sm dy-opacity-70 dy-ring-offset-background dy-transition-opacity hover:dy-opacity-100 focus:dy-outline-none focus:dy-ring-2 focus:dy-ring-ring focus:dy-ring-offset-2 disabled:dy-pointer-events-none data-[state=open]:dy-bg-accent data-[state=open]:dy-text-muted-foreground",
2071
- children: [/* @__PURE__ */ jsx(X, { className: "dy-h-4 dy-w-4" }), /* @__PURE__ */ jsx("span", {
2072
- className: "dy-sr-only",
2073
- children: "Close"
2074
- })]
2075
- })]
2076
- })]
2077
- }) });
2078
- });
2079
- DialogContent.displayName = DialogPrimitive.Content.displayName;
2080
- var DialogHeader = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
2081
- className: cn("dy-flex dy-flex-col dy-space-y-1.5 dy-text-center sm:dy-text-left", className),
2082
- ...props
2083
- });
2084
- DialogHeader.displayName = "DialogHeader";
2085
- var DialogFooter = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
2086
- className: cn("dy-flex dy-flex-col-reverse sm:dy-flex-row sm:dy-justify-end sm:dy-space-x-2", className),
2087
- ...props
2088
- });
2089
- DialogFooter.displayName = "DialogFooter";
2090
- var DialogTitle = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Title, {
2091
- ref,
2092
- className: cn("dy-text-lg dy-font-semibold dy-leading-none dy-tracking-tight", className),
2093
- ...props
2094
- }));
2095
- DialogTitle.displayName = DialogPrimitive.Title.displayName;
2096
- var DialogDescription = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Description, {
2097
- ref,
2098
- className: cn("dy-text-sm dy-text-muted-foreground", className),
2099
- ...props
2100
- }));
2101
- DialogDescription.displayName = DialogPrimitive.Description.displayName;
2102
- //#endregion
2103
2069
  //#region src/components/ui/progress.tsx
2104
2070
  var Progress = React$1.forwardRef(({ className, value, ...props }, ref) => /* @__PURE__ */ jsx(ProgressPrimitive.Root, {
2105
2071
  ref,
@@ -2145,39 +2111,72 @@ function CsvImporter({ slug, schema, onClose }) {
2145
2111
  const [parsedRows, setParsedRows] = React$1.useState([]);
2146
2112
  const [mapping, setMapping] = React$1.useState({});
2147
2113
  const [validatedData, setValidatedData] = React$1.useState([]);
2114
+ const [isDragging, setIsDragging] = React$1.useState(false);
2115
+ const [fileError, setFileError] = React$1.useState(null);
2116
+ const [confirmedSkip, setConfirmedSkip] = React$1.useState(false);
2148
2117
  const [totalRows, setTotalRows] = React$1.useState(0);
2149
2118
  const [processedCount, setProcessedCount] = React$1.useState(0);
2150
2119
  const [successCount, setSuccessCount] = React$1.useState(0);
2151
2120
  const [failedRows, setFailedRows] = React$1.useState([]);
2121
+ const [retryableRows, setRetryableRows] = React$1.useState([]);
2152
2122
  const importableFields = React$1.useMemo(() => {
2153
2123
  return schema.fields.filter((f) => f.name !== "id" && f.name !== "createdAt" && f.name !== "updatedAt" && f.type !== "row" && f.type !== "join" && !f.admin?.hidden);
2154
2124
  }, [schema]);
2155
2125
  const requiredFields = React$1.useMemo(() => {
2156
2126
  return importableFields.filter((f) => f.required);
2157
2127
  }, [importableFields]);
2158
- const handleFileChange = (e) => {
2159
- const selectedFile = e.target.files?.[0];
2160
- if (!selectedFile) return;
2161
- Papa.parse(selectedFile, {
2128
+ const processFile = (file) => {
2129
+ setFileError(null);
2130
+ if (!(file.type === "text/csv" || file.name.toLowerCase().endsWith(".csv"))) {
2131
+ setFileError(`Unsupported file type: "${file.name}". Please upload a CSV file.`);
2132
+ return;
2133
+ }
2134
+ Papa.parse(file, {
2162
2135
  header: true,
2163
2136
  skipEmptyLines: "greedy",
2164
2137
  complete: (results) => {
2165
2138
  const headers = results.meta.fields || [];
2139
+ const rows = results.data;
2140
+ if (rows.length === 0) {
2141
+ setFileError("This CSV file has no importable rows. Please upload a file with at least one data row.");
2142
+ return;
2143
+ }
2166
2144
  setCsvHeaders(headers);
2167
- setParsedRows(results.data);
2145
+ setParsedRows(rows);
2168
2146
  const initialMapping = {};
2169
2147
  headers.forEach((header) => {
2170
- const match = importableFields.find((f) => f.name.toLowerCase() === header.toLowerCase() || f.label?.toLowerCase() === header.toLowerCase());
2171
- initialMapping[header] = match ? match.name : "__ignore__";
2148
+ initialMapping[header] = importableFields.find((f) => f.name?.toLowerCase() === header.toLowerCase() || f.label?.toLowerCase() === header.toLowerCase())?.name ?? "__ignore__";
2172
2149
  });
2173
2150
  setMapping(initialMapping);
2174
2151
  setStep("map");
2175
2152
  },
2176
2153
  error: (error) => {
2177
- console.error("PapaParse error:", error);
2154
+ setFileError(`Failed to parse CSV: ${error.message}`);
2178
2155
  }
2179
2156
  });
2180
2157
  };
2158
+ const handleFileChange = (e) => {
2159
+ const selectedFile = e.target.files?.[0];
2160
+ if (selectedFile) processFile(selectedFile);
2161
+ e.target.value = "";
2162
+ };
2163
+ const handleDragOver = (e) => {
2164
+ e.preventDefault();
2165
+ e.stopPropagation();
2166
+ setIsDragging(true);
2167
+ };
2168
+ const handleDragLeave = (e) => {
2169
+ e.preventDefault();
2170
+ e.stopPropagation();
2171
+ setIsDragging(false);
2172
+ };
2173
+ const handleDrop = (e) => {
2174
+ e.preventDefault();
2175
+ e.stopPropagation();
2176
+ setIsDragging(false);
2177
+ const file = e.dataTransfer.files?.[0];
2178
+ if (file) processFile(file);
2179
+ };
2181
2180
  const handleMapChange = (header, fieldName) => {
2182
2181
  setMapping((prev) => ({
2183
2182
  ...prev,
@@ -2292,18 +2291,21 @@ function CsvImporter({ slug, schema, onClose }) {
2292
2291
  isValid
2293
2292
  };
2294
2293
  }));
2294
+ setConfirmedSkip(false);
2295
2295
  setStep("preview");
2296
2296
  };
2297
- const startImport = async () => {
2297
+ const startImport = async (rows = validatedData) => {
2298
2298
  setStep("importing");
2299
- setTotalRows(validatedData.length);
2299
+ setTotalRows(rows.length);
2300
2300
  setProcessedCount(0);
2301
2301
  setSuccessCount(0);
2302
2302
  setFailedRows([]);
2303
+ setRetryableRows([]);
2303
2304
  let success = 0;
2304
2305
  const failures = [];
2305
- for (let i = 0; i < validatedData.length; i++) {
2306
- const rowResult = validatedData[i];
2306
+ const apiFailures = [];
2307
+ for (let i = 0; i < rows.length; i++) {
2308
+ const rowResult = rows[i];
2307
2309
  setProcessedCount(i + 1);
2308
2310
  if (!rowResult.isValid) {
2309
2311
  failures.push({
@@ -2333,17 +2335,24 @@ function CsvImporter({ slug, schema, onClose }) {
2333
2335
  success++;
2334
2336
  setSuccessCount(success);
2335
2337
  } catch (error) {
2338
+ const message = error.message || "Failed to create entry";
2336
2339
  failures.push({
2337
2340
  row: rowResult.rowNumber,
2338
2341
  data: rowResult.data,
2339
- error: error.message || "Failed to create entry"
2342
+ error: message
2340
2343
  });
2341
2344
  setFailedRows([...failures]);
2345
+ apiFailures.push(rowResult);
2342
2346
  }
2343
2347
  }
2348
+ setRetryableRows(apiFailures);
2344
2349
  queryClient.invalidateQueries({ queryKey: ["collection", slug] });
2345
2350
  setStep("complete");
2346
2351
  };
2352
+ const handleRetryFailed = () => {
2353
+ setValidatedData(retryableRows);
2354
+ startImport(retryableRows);
2355
+ };
2347
2356
  const downloadFailedCsv = () => {
2348
2357
  if (failedRows.length === 0) return;
2349
2358
  const headers = [...csvHeaders, "Import Error"];
@@ -2375,14 +2384,17 @@ function CsvImporter({ slug, schema, onClose }) {
2375
2384
  return /* @__PURE__ */ jsxs("div", {
2376
2385
  className: "dy-space-y-6",
2377
2386
  children: [
2378
- step === "upload" && /* @__PURE__ */ jsx("div", {
2387
+ step === "upload" && /* @__PURE__ */ jsxs("div", {
2379
2388
  className: "dy-space-y-4",
2380
- children: /* @__PURE__ */ jsx("div", {
2381
- className: "dy-text-center dy-py-10 dy-border-2 dy-border-dashed dy-border-border dy-rounded-xl hover:dy-bg-muted/30 dy-transition-colors",
2389
+ children: [/* @__PURE__ */ jsx("div", {
2390
+ className: cn("dy-text-center dy-py-10 dy-border-2 dy-border-dashed dy-rounded-xl dy-transition-colors", isDragging ? "dy-border-primary dy-bg-primary/5" : "dy-border-border hover:dy-bg-muted/30"),
2391
+ onDragOver: handleDragOver,
2392
+ onDragLeave: handleDragLeave,
2393
+ onDrop: handleDrop,
2382
2394
  children: /* @__PURE__ */ jsxs("label", {
2383
2395
  className: "dy-cursor-pointer dy-block dy-space-y-4 dy-px-6",
2384
2396
  children: [
2385
- /* @__PURE__ */ jsx(Upload, { className: "dy-h-10 dy-w-10 dy-mx-auto dy-text-muted-foreground" }),
2397
+ /* @__PURE__ */ jsx(Upload, { className: cn("dy-h-10 dy-w-10 dy-mx-auto", isDragging ? "dy-text-primary" : "dy-text-muted-foreground") }),
2386
2398
  /* @__PURE__ */ jsxs("div", {
2387
2399
  className: "dy-space-y-1",
2388
2400
  children: [/* @__PURE__ */ jsx("p", {
@@ -2401,7 +2413,10 @@ function CsvImporter({ slug, schema, onClose }) {
2401
2413
  })
2402
2414
  ]
2403
2415
  })
2404
- })
2416
+ }), fileError && /* @__PURE__ */ jsxs("div", {
2417
+ className: "dy-flex dy-items-center dy-gap-2 dy-p-3 dy-rounded-lg dy-bg-destructive/10 dy-border dy-border-destructive/20 dy-text-destructive dy-text-sm",
2418
+ children: [/* @__PURE__ */ jsx(AlertCircle, { className: "dy-h-4 dy-w-4 dy-shrink-0" }), fileError]
2419
+ })]
2405
2420
  }),
2406
2421
  step === "map" && /* @__PURE__ */ jsxs("div", {
2407
2422
  className: "dy-space-y-4",
@@ -2505,19 +2520,22 @@ function CsvImporter({ slug, schema, onClose }) {
2505
2520
  })]
2506
2521
  }),
2507
2522
  /* @__PURE__ */ jsx("div", {
2508
- className: "dy-h-[300px] dy-overflow-auto dy-border dy-border-border dy-rounded-xl",
2523
+ className: "dy-overflow-x-auto dy-overflow-y-auto dy-max-h-[60vh] dy-border dy-border-border dy-rounded-xl",
2509
2524
  children: /* @__PURE__ */ jsxs("table", {
2510
- className: "dy-w-full dy-min-w-max dy-text-left dy-border-collapse",
2511
- children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", {
2512
- className: "dy-border-b dy-border-border dy-bg-muted/40 dy-text-xs dy-font-semibold dy-text-muted-foreground",
2513
- children: [/* @__PURE__ */ jsx("th", {
2514
- className: "dy-p-3 dy-w-16",
2515
- children: "Row"
2516
- }), importableFields.filter((f) => Object.values(mapping).includes(f.name)).map((f) => /* @__PURE__ */ jsx("th", {
2517
- className: "dy-p-3",
2518
- children: f.label || f.name
2519
- }, f.name))]
2520
- }) }), /* @__PURE__ */ jsx("tbody", {
2525
+ className: "dy-min-w-max dy-w-full dy-text-left dy-border-collapse",
2526
+ children: [/* @__PURE__ */ jsx("thead", {
2527
+ className: "dy-sticky dy-top-0 dy-z-10",
2528
+ children: /* @__PURE__ */ jsxs("tr", {
2529
+ className: "dy-border-b dy-border-border dy-bg-muted/40 dy-text-xs dy-font-semibold dy-text-muted-foreground",
2530
+ children: [/* @__PURE__ */ jsx("th", {
2531
+ className: "dy-p-3 dy-w-16",
2532
+ children: "Row"
2533
+ }), importableFields.filter((f) => Object.values(mapping).includes(f.name)).map((f) => /* @__PURE__ */ jsx("th", {
2534
+ className: "dy-p-3",
2535
+ children: f.label || f.name
2536
+ }, f.name))]
2537
+ })
2538
+ }), /* @__PURE__ */ jsx("tbody", {
2521
2539
  className: "dy-text-sm",
2522
2540
  children: previewRows.map((rowResult) => /* @__PURE__ */ jsxs("tr", {
2523
2541
  className: "dy-border-b dy-border-border hover:dy-bg-muted/20",
@@ -2539,6 +2557,39 @@ function CsvImporter({ slug, schema, onClose }) {
2539
2557
  })]
2540
2558
  })
2541
2559
  }),
2560
+ totalErrors > 0 && totalErrors < validatedData.length && /* @__PURE__ */ jsxs("label", {
2561
+ className: "dy-flex dy-items-start dy-gap-3 dy-p-3 dy-rounded-lg dy-border dy-border-amber-300/60 dy-bg-amber-500/10 dy-cursor-pointer",
2562
+ children: [/* @__PURE__ */ jsx("input", {
2563
+ type: "checkbox",
2564
+ className: "dy-mt-0.5 dy-accent-amber-500",
2565
+ checked: confirmedSkip,
2566
+ onChange: (e) => setConfirmedSkip(e.target.checked)
2567
+ }), /* @__PURE__ */ jsxs("span", {
2568
+ className: "dy-text-xs dy-text-amber-700 dark:dy-text-amber-400",
2569
+ children: [
2570
+ "I understand that ",
2571
+ /* @__PURE__ */ jsxs("strong", { children: [
2572
+ totalErrors,
2573
+ " row",
2574
+ totalErrors !== 1 ? "s" : ""
2575
+ ] }),
2576
+ " with validation errors will be skipped. Only the ",
2577
+ validatedData.length - totalErrors,
2578
+ " valid row",
2579
+ validatedData.length - totalErrors !== 1 ? "s" : "",
2580
+ " will be imported."
2581
+ ]
2582
+ })]
2583
+ }),
2584
+ totalErrors === validatedData.length && /* @__PURE__ */ jsxs("div", {
2585
+ className: "dy-flex dy-items-center dy-gap-2 dy-p-3 dy-rounded-lg dy-bg-destructive/10 dy-border dy-border-destructive/20 dy-text-destructive dy-text-sm",
2586
+ children: [
2587
+ /* @__PURE__ */ jsx(AlertCircle, { className: "dy-h-4 dy-w-4 dy-shrink-0" }),
2588
+ "All ",
2589
+ totalErrors,
2590
+ " rows have validation errors. Fix your CSV and re-upload before importing."
2591
+ ]
2592
+ }),
2542
2593
  /* @__PURE__ */ jsxs("div", {
2543
2594
  className: "dy-flex dy-justify-between dy-pt-4",
2544
2595
  children: [/* @__PURE__ */ jsx(Button, {
@@ -2546,14 +2597,17 @@ function CsvImporter({ slug, schema, onClose }) {
2546
2597
  onClick: () => setStep("map"),
2547
2598
  children: "Back"
2548
2599
  }), /* @__PURE__ */ jsxs(Button, {
2549
- onClick: startImport,
2600
+ onClick: () => startImport(),
2550
2601
  variant: "default",
2551
2602
  className: "dy-gap-2",
2603
+ disabled: totalErrors === validatedData.length || totalErrors > 0 && !confirmedSkip,
2552
2604
  children: [
2553
2605
  /* @__PURE__ */ jsx(Play, { className: "dy-h-4 dy-w-4" }),
2554
2606
  " Start Import (",
2555
2607
  validatedData.length - totalErrors,
2556
- " Rows)"
2608
+ " Row",
2609
+ validatedData.length - totalErrors !== 1 ? "s" : "",
2610
+ ")"
2557
2611
  ]
2558
2612
  })]
2559
2613
  })
@@ -2631,12 +2685,27 @@ function CsvImporter({ slug, schema, onClose }) {
2631
2685
  failedRows.length,
2632
2686
  " rows)"
2633
2687
  ]
2634
- }), /* @__PURE__ */ jsxs(Button, {
2635
- size: "sm",
2636
- variant: "outline",
2637
- className: "dy-h-8 dy-text-xs dy-gap-1.5",
2638
- onClick: downloadFailedCsv,
2639
- children: [/* @__PURE__ */ jsx(Download, { className: "dy-h-3.5 dy-w-3.5" }), " Download Errors CSV"]
2688
+ }), /* @__PURE__ */ jsxs("div", {
2689
+ className: "dy-flex dy-items-center dy-gap-2",
2690
+ children: [retryableRows.length > 0 && /* @__PURE__ */ jsxs(Button, {
2691
+ size: "sm",
2692
+ variant: "outline",
2693
+ className: "dy-h-8 dy-text-xs dy-gap-1.5",
2694
+ onClick: handleRetryFailed,
2695
+ children: [
2696
+ /* @__PURE__ */ jsx(RotateCcw, { className: "dy-h-3.5 dy-w-3.5" }),
2697
+ " Retry ",
2698
+ retryableRows.length,
2699
+ " Failed Row",
2700
+ retryableRows.length !== 1 ? "s" : ""
2701
+ ]
2702
+ }), /* @__PURE__ */ jsxs(Button, {
2703
+ size: "sm",
2704
+ variant: "outline",
2705
+ className: "dy-h-8 dy-text-xs dy-gap-1.5",
2706
+ onClick: downloadFailedCsv,
2707
+ children: [/* @__PURE__ */ jsx(Download, { className: "dy-h-3.5 dy-w-3.5" }), " Download Errors CSV"]
2708
+ })]
2640
2709
  })]
2641
2710
  }), /* @__PURE__ */ jsx(ScrollArea, {
2642
2711
  className: "dy-h-[180px] dy-border dy-border-border dy-rounded-xl",
@@ -2681,27 +2750,6 @@ function CsvImporter({ slug, schema, onClose }) {
2681
2750
  });
2682
2751
  }
2683
2752
  //#endregion
2684
- //#region src/components/ui/csv-importer-dialog.tsx
2685
- function CsvImporterDialog({ open, onOpenChange, slug, schema }) {
2686
- const title = schema.labels?.singular || schema.slug || slug;
2687
- return /* @__PURE__ */ jsx(Dialog, {
2688
- open,
2689
- onOpenChange,
2690
- children: /* @__PURE__ */ jsxs(DialogContent, {
2691
- className: "sm:dy-max-w-2xl",
2692
- children: [/* @__PURE__ */ jsxs(DialogHeader, { children: [/* @__PURE__ */ jsx(DialogTitle, { children: "Import CSV Data" }), /* @__PURE__ */ jsxs(DialogDescription, { children: [
2693
- "Import records into the ",
2694
- title,
2695
- " collection."
2696
- ] })] }), /* @__PURE__ */ jsx(CsvImporter, {
2697
- slug,
2698
- schema,
2699
- onClose: () => onOpenChange(false)
2700
- })]
2701
- })
2702
- });
2703
- }
2704
- //#endregion
2705
2753
  //#region src/components/ui/render-cell.tsx
2706
2754
  function RenderCell({ value, field, client, schemas }) {
2707
2755
  if (value === null || value === void 0) return /* @__PURE__ */ jsx("span", {
@@ -2917,14 +2965,14 @@ function MediaGrid({ items, baseUrl, onDelete, slug }) {
2917
2965
  }
2918
2966
  //#endregion
2919
2967
  //#region src/components/ui/sheet.tsx
2920
- var Sheet = DialogPrimitive.Root;
2921
- var SheetPortal = DialogPrimitive.Portal;
2922
- var SheetOverlay = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Overlay, {
2968
+ var Sheet = SheetPrimitive.Root;
2969
+ var SheetPortal = SheetPrimitive.Portal;
2970
+ var SheetOverlay = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(SheetPrimitive.Overlay, {
2923
2971
  className: cn("dy-fixed dy-inset-0 dy-z-50 dy-bg-black/40 dy-backdrop-blur-[2px] data-[state=open]:dy-animate-in data-[state=closed]:dy-animate-out data-[state=closed]:dy-fade-out-0 data-[state=open]:dy-fade-in-0", className),
2924
2972
  ...props,
2925
2973
  ref
2926
2974
  }));
2927
- SheetOverlay.displayName = DialogPrimitive.Overlay.displayName;
2975
+ SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
2928
2976
  var sheetVariants = cva("dy-fixed dy-z-50 dy-gap-4 dy-bg-background dy-p-6 dy-shadow-2xl dy-transition dy-ease-in-out data-[state=open]:dy-animate-in data-[state=closed]:dy-animate-out data-[state=closed]:dy-duration-300 data-[state=open]:dy-duration-500", {
2929
2977
  variants: { side: {
2930
2978
  top: "dy-inset-x-0 dy-top-0 dy-border-b data-[state=closed]:dy-slide-out-to-top data-[state=open]:dy-slide-in-from-top",
@@ -2939,11 +2987,11 @@ var SheetContent = React$1.forwardRef(({ side = "right", className, children, ..
2939
2987
  return /* @__PURE__ */ jsx(SheetPortal, { children: /* @__PURE__ */ jsxs("div", {
2940
2988
  className: themeClassName,
2941
2989
  "data-theme": resolvedTheme,
2942
- children: [/* @__PURE__ */ jsx(SheetOverlay, {}), /* @__PURE__ */ jsxs(DialogPrimitive.Content, {
2990
+ children: [/* @__PURE__ */ jsx(SheetOverlay, {}), /* @__PURE__ */ jsxs(SheetPrimitive.Content, {
2943
2991
  ref,
2944
2992
  className: cn(sheetVariants({ side }), className),
2945
2993
  ...props,
2946
- children: [children, /* @__PURE__ */ jsxs(DialogPrimitive.Close, {
2994
+ children: [children, /* @__PURE__ */ jsxs(SheetPrimitive.Close, {
2947
2995
  className: "dy-absolute dy-right-4 dy-top-4 dy-rounded-sm dy-opacity-70 dy-ring-offset-background dy-transition-opacity hover:dy-opacity-100 focus:dy-outline-none focus:dy-ring-2 focus:dy-ring-ring focus:dy-ring-offset-2 disabled:dy-pointer-events-none data-[state=open]:dy-bg-secondary",
2948
2996
  children: [/* @__PURE__ */ jsx(X, { className: "dy-h-4 dy-w-4" }), /* @__PURE__ */ jsx("span", {
2949
2997
  className: "dy-sr-only",
@@ -2953,7 +3001,7 @@ var SheetContent = React$1.forwardRef(({ side = "right", className, children, ..
2953
3001
  })]
2954
3002
  }) });
2955
3003
  });
2956
- SheetContent.displayName = DialogPrimitive.Content.displayName;
3004
+ SheetContent.displayName = SheetPrimitive.Content.displayName;
2957
3005
  var SheetHeader = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
2958
3006
  className: cn("dy-flex dy-flex-col dy-space-y-2 dy-text-center sm:dy-text-left", className),
2959
3007
  ...props
@@ -2964,18 +3012,18 @@ var SheetFooter = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
2964
3012
  ...props
2965
3013
  });
2966
3014
  SheetFooter.displayName = "SheetFooter";
2967
- var SheetTitle = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Title, {
3015
+ var SheetTitle = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(SheetPrimitive.Title, {
2968
3016
  ref,
2969
3017
  className: cn("dy-text-lg dy-font-semibold dy-text-foreground", className),
2970
3018
  ...props
2971
3019
  }));
2972
- SheetTitle.displayName = DialogPrimitive.Title.displayName;
2973
- var SheetDescription = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Description, {
3020
+ SheetTitle.displayName = SheetPrimitive.Title.displayName;
3021
+ var SheetDescription = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(SheetPrimitive.Description, {
2974
3022
  ref,
2975
3023
  className: cn("dy-text-sm dy-text-muted-foreground", className),
2976
3024
  ...props
2977
3025
  }));
2978
- SheetDescription.displayName = DialogPrimitive.Description.displayName;
3026
+ SheetDescription.displayName = SheetPrimitive.Description.displayName;
2979
3027
  //#endregion
2980
3028
  //#region src/components/ui/toggle.tsx
2981
3029
  var toggleVariants = cva("dy-inline-flex dy-items-center dy-justify-center dy-rounded-md dy-text-sm dy-font-medium dy-ring-offset-background dy-transition-colors hover:dy-bg-muted hover:dy-text-muted-foreground focus-visible:dy-outline-none focus-visible:dy-ring-2 focus-visible:dy-ring-ring focus-visible:dy-ring-offset-2 disabled:dy-pointer-events-none disabled:dy-opacity-50 data-[state=on]:dy-bg-accent data-[state=on]:dy-text-accent-foreground [&_svg]:dy-pointer-events-none [&_svg]:dy-size-4 [&_svg]:dy-shrink-0 dy-gap-2", {
@@ -3006,6 +3054,59 @@ var Toggle = React$1.forwardRef(({ className, variant, size, ...props }, ref) =>
3006
3054
  }));
3007
3055
  Toggle.displayName = TogglePrimitive.Root.displayName;
3008
3056
  //#endregion
3057
+ //#region src/components/ui/dialog.tsx
3058
+ var Dialog = SheetPrimitive.Root;
3059
+ var DialogTrigger = SheetPrimitive.Trigger;
3060
+ var DialogPortal = SheetPrimitive.Portal;
3061
+ var DialogOverlay = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(SheetPrimitive.Overlay, {
3062
+ ref,
3063
+ className: cn("dy-fixed dy-inset-0 dy-z-50 dy-bg-black/40 dy-backdrop-blur-[2px] data-[state=open]:dy-animate-in data-[state=closed]:dy-animate-out data-[state=closed]:dy-fade-out-0 data-[state=open]:dy-fade-in-0", className),
3064
+ ...props
3065
+ }));
3066
+ DialogOverlay.displayName = SheetPrimitive.Overlay.displayName;
3067
+ var DialogContent = React$1.forwardRef(({ className, children, ...props }, ref) => {
3068
+ const { resolvedTheme, themeClassName } = useAdminTheme();
3069
+ return /* @__PURE__ */ jsx(DialogPortal, { children: /* @__PURE__ */ jsxs("div", {
3070
+ className: themeClassName,
3071
+ "data-theme": resolvedTheme,
3072
+ children: [/* @__PURE__ */ jsx(DialogOverlay, {}), /* @__PURE__ */ jsxs(SheetPrimitive.Content, {
3073
+ ref,
3074
+ className: cn("dy-fixed dy-left-[50%] dy-top-[50%] dy-z-50 dy-grid dy-w-full dy-max-w-lg dy-translate-x-[-50%] dy-translate-y-[-50%] dy-gap-4 dy-border dy-bg-background dy-p-6 dy-shadow-2xl dy-duration-200 data-[state=open]:dy-animate-in data-[state=closed]:dy-animate-out data-[state=closed]:dy-fade-out-0 data-[state=open]:dy-fade-in-0 data-[state=closed]:dy-zoom-out-95 data-[state=open]:dy-zoom-in-95 data-[state=closed]:dy-slide-out-to-left-1/2 data-[state=closed]:dy-slide-out-to-top-[48%] data-[state=open]:dy-slide-in-from-left-1/2 data-[state=open]:dy-slide-in-from-top-[48%] sm:dy-rounded-xl", className),
3075
+ ...props,
3076
+ children: [children, /* @__PURE__ */ jsxs(SheetPrimitive.Close, {
3077
+ className: "dy-absolute dy-right-4 dy-top-4 dy-rounded-sm dy-opacity-70 dy-ring-offset-background dy-transition-opacity hover:dy-opacity-100 focus:dy-outline-none focus:dy-ring-2 focus:dy-ring-ring focus:dy-ring-offset-2 disabled:dy-pointer-events-none data-[state=open]:dy-bg-accent data-[state=open]:dy-text-muted-foreground",
3078
+ children: [/* @__PURE__ */ jsx(X, { className: "dy-h-4 dy-w-4" }), /* @__PURE__ */ jsx("span", {
3079
+ className: "dy-sr-only",
3080
+ children: "Close"
3081
+ })]
3082
+ })]
3083
+ })]
3084
+ }) });
3085
+ });
3086
+ DialogContent.displayName = SheetPrimitive.Content.displayName;
3087
+ var DialogHeader = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
3088
+ className: cn("dy-flex dy-flex-col dy-space-y-1.5 dy-text-center sm:dy-text-left", className),
3089
+ ...props
3090
+ });
3091
+ DialogHeader.displayName = "DialogHeader";
3092
+ var DialogFooter = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
3093
+ className: cn("dy-flex dy-flex-col-reverse sm:dy-flex-row sm:dy-justify-end sm:dy-space-x-2", className),
3094
+ ...props
3095
+ });
3096
+ DialogFooter.displayName = "DialogFooter";
3097
+ var DialogTitle = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(SheetPrimitive.Title, {
3098
+ ref,
3099
+ className: cn("dy-text-lg dy-font-semibold dy-leading-none dy-tracking-tight", className),
3100
+ ...props
3101
+ }));
3102
+ DialogTitle.displayName = SheetPrimitive.Title.displayName;
3103
+ var DialogDescription = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(SheetPrimitive.Description, {
3104
+ ref,
3105
+ className: cn("dy-text-sm dy-text-muted-foreground", className),
3106
+ ...props
3107
+ }));
3108
+ DialogDescription.displayName = SheetPrimitive.Description.displayName;
3109
+ //#endregion
3009
3110
  //#region src/components/ui/tabs.tsx
3010
3111
  var Tabs = TabsPrimitive.Root;
3011
3112
  var TabsList = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(TabsPrimitive.List, {
@@ -9680,7 +9781,7 @@ function CollectionListPage({ slug }) {
9680
9781
  const allAvailableColumns = React$1.useMemo(() => {
9681
9782
  if (!schema) return [];
9682
9783
  return [
9683
- ...schema.fields.filter((f) => f.name !== "password" && !f.admin?.hidden && f.type !== "row" && f.type !== "join").map((field) => field.name),
9784
+ ...schema.fields.filter((f) => f.name && f.name !== "password" && !f.admin?.hidden && f.type !== "row" && f.type !== "join").map((field) => field.name),
9684
9785
  "id",
9685
9786
  "createdAt",
9686
9787
  "updatedAt"
@@ -9688,7 +9789,7 @@ function CollectionListPage({ slug }) {
9688
9789
  }, [schema]);
9689
9790
  const defaultListColumns = React$1.useMemo(() => {
9690
9791
  if (!schema) return [];
9691
- const allDisplayFields = schema.fields.filter((f) => f.name !== "password" && !f.admin?.hidden && f.type !== "row" && f.type !== "join");
9792
+ const allDisplayFields = schema.fields.filter((f) => f.name && f.name !== "password" && !f.admin?.hidden && f.type !== "row" && f.type !== "join");
9692
9793
  const configuredColumns = Array.isArray(schema.admin?.defaultColumns) ? schema.admin.defaultColumns : [];
9693
9794
  return configuredColumns.length > 0 ? configuredColumns : allDisplayFields.slice(0, 3).map((field) => field.name);
9694
9795
  }, [schema]);
@@ -9887,7 +9988,7 @@ function CollectionListPage({ slug }) {
9887
9988
  key: "id",
9888
9989
  label: "ID"
9889
9990
  },
9890
- ...schema.fields.filter((f) => f.name !== "password" && !f.admin?.hidden && f.type !== "row" && f.type !== "join").map((f) => ({
9991
+ ...schema.fields.filter((f) => f.name !== "password" && !f.admin?.hidden && f.type !== "row" && f.type !== "join").filter((f) => !!f.name).map((f) => ({
9891
9992
  key: f.name,
9892
9993
  label: f.label || f.name
9893
9994
  })),
@@ -9962,7 +10063,7 @@ function CollectionListPage({ slug }) {
9962
10063
  }
9963
10064
  const columns = React$1.useMemo(() => {
9964
10065
  if (!schema) return [];
9965
- const allDisplayFields = schema.fields.filter((f) => f.name !== "password" && !f.admin?.hidden && f.type !== "row" && f.type !== "join");
10066
+ const allDisplayFields = schema.fields.filter((f) => f.name && f.name !== "password" && !f.admin?.hidden && f.type !== "row" && f.type !== "join");
9966
10067
  const fieldByName = new Map(allDisplayFields.map((field) => [field.name, field]));
9967
10068
  const configuredColumns = localPreference.columns.filter((col) => col.visible).map((col) => col.name);
9968
10069
  const visibleColumnNames = configuredColumns.length > 0 ? configuredColumns : allDisplayFields.slice(0, 3).map((field) => field.name);
@@ -10212,6 +10313,38 @@ function CollectionListPage({ slug }) {
10212
10313
  })
10213
10314
  ]
10214
10315
  });
10316
+ if (isImportOpen && schema) {
10317
+ const collectionTitle = schema.labels?.plural || schema.slug;
10318
+ return /* @__PURE__ */ jsxs("div", {
10319
+ className: "dy-space-y-6 dy-animate-in lg:dy-space-y-8",
10320
+ children: [
10321
+ /* @__PURE__ */ jsx("div", {
10322
+ className: "dy-flex dy-items-center dy-gap-3",
10323
+ children: /* @__PURE__ */ jsxs(Button, {
10324
+ variant: "ghost",
10325
+ size: "sm",
10326
+ className: "dy-h-8 dy-px-2 dy-gap-1.5 dy-text-xs dy-text-muted-foreground hover:dy-text-foreground",
10327
+ onClick: () => setIsImportOpen(false),
10328
+ children: [
10329
+ /* @__PURE__ */ jsx(ArrowLeft, { className: "dy-h-3.5 dy-w-3.5" }),
10330
+ "Back to ",
10331
+ collectionTitle
10332
+ ]
10333
+ })
10334
+ }),
10335
+ /* @__PURE__ */ jsx(PageHeader, {
10336
+ title: `Import CSV — ${collectionTitle}`,
10337
+ description: `Upload a CSV file to bulk-import records into the ${collectionTitle} collection.`,
10338
+ icon: FileUp
10339
+ }),
10340
+ /* @__PURE__ */ jsx(CsvImporter, {
10341
+ slug,
10342
+ schema,
10343
+ onClose: () => setIsImportOpen(false)
10344
+ })
10345
+ ]
10346
+ });
10347
+ }
10215
10348
  return /* @__PURE__ */ jsxs("div", {
10216
10349
  className: "dy-space-y-6 dy-animate-in lg:dy-space-y-8",
10217
10350
  children: [
@@ -10485,12 +10618,6 @@ function CollectionListPage({ slug }) {
10485
10618
  componentProps: collectionComponentProps
10486
10619
  })
10487
10620
  ]
10488
- }),
10489
- /* @__PURE__ */ jsx(CsvImporterDialog, {
10490
- open: isImportOpen,
10491
- onOpenChange: setIsImportOpen,
10492
- slug,
10493
- schema
10494
10621
  })
10495
10622
  ]
10496
10623
  });
@@ -12578,6 +12705,18 @@ function GlobalEditorPage() {
12578
12705
  //#region src/pages/setup/setup-prompt.tsx
12579
12706
  var GUIDE_URL = "https://www.dyrected.com/guide";
12580
12707
  var DOCS_URL = "https://docs.dyrected.com";
12708
+ var steps = [
12709
+ "Paste the prompt into your AI builder — the same one that owns the website code.",
12710
+ "Review the content list the AI sends back. Correct anything missing or wrong, then say \"approved\".",
12711
+ "Your Dyrected credentials are already in the prompt. Give them to the AI when it asks at Stage 4.",
12712
+ "Test one real edit in Dyrected. If the change appears on the website, invite the client."
12713
+ ];
12714
+ var stepsNoCredentials = [
12715
+ "Paste the prompt into your AI builder — the same one that owns the website code.",
12716
+ "Review the content list the AI sends back. Correct anything missing or wrong, then say \"approved\".",
12717
+ "When the AI reaches Stage 4, it will ask for your Site ID, Site API key, and Base URL.",
12718
+ "Test one real edit in Dyrected. If the change appears on the website, invite the client."
12719
+ ];
12581
12720
  function normalizeTechStack(techStack) {
12582
12721
  if (!techStack) return void 0;
12583
12722
  if (techStack === "next") return "nextjs";
@@ -12593,154 +12732,116 @@ function buildGuideUrl(config) {
12593
12732
  if (config.baseUrl) url.searchParams.set("endpoint", config.baseUrl);
12594
12733
  return url.toString();
12595
12734
  }
12735
+ function buildPrompt(config) {
12736
+ const { siteId, apiKey, baseUrl } = config;
12737
+ if (!(siteId && apiKey && baseUrl)) return GENERATE_CMS_PROMPT;
12738
+ const placeholder = `Ask me for the following in one message:
12739
+
12740
+ - Site ID
12741
+ - Site API key
12742
+ - Base URL
12743
+
12744
+ Wait for my reply.`;
12745
+ const replacement = `Use the following credentials:
12746
+
12747
+ - Site ID: ${siteId}
12748
+ - Site API key: ${apiKey}
12749
+ - Base URL: ${baseUrl}`;
12750
+ return GENERATE_CMS_PROMPT.replace(placeholder, replacement);
12751
+ }
12596
12752
  function SetupPromptUI({ config }) {
12753
+ const [copied, setCopied] = useState(false);
12597
12754
  const guideUrl = buildGuideUrl(config);
12598
- const stack = normalizeTechStack(config.defaultTechStack);
12755
+ const hasCredentials = !!(config.siteId && config.apiKey && config.baseUrl);
12756
+ const guideSteps = hasCredentials ? steps : stepsNoCredentials;
12757
+ const promptText = buildPrompt(config);
12758
+ async function copyPrompt() {
12759
+ await navigator.clipboard.writeText(promptText);
12760
+ setCopied(true);
12761
+ window.setTimeout(() => setCopied(false), 1800);
12762
+ }
12599
12763
  return /* @__PURE__ */ jsxs("div", {
12600
- className: "dy-mx-auto dy-max-w-5xl dy-py-6 lg:dy-py-10",
12601
- children: [/* @__PURE__ */ jsxs("section", {
12602
- className: "dy-relative dy-overflow-hidden dy-rounded-[28px] dy-border dy-border-border dy-bg-card dy-text-card-foreground dy-shadow-2xl",
12603
- children: [
12604
- /* @__PURE__ */ jsx("div", {
12605
- className: "dy-absolute dy-inset-y-0 dy-right-0 dy-w-1/3 dy-bg-primary",
12606
- "aria-hidden": "true"
12607
- }),
12608
- /* @__PURE__ */ jsx("div", {
12609
- className: "dy-absolute dy-inset-y-0 dy-right-[12%] dy-w-40 dy-skew-x-[-12deg] dy-bg-card",
12610
- "aria-hidden": "true"
12611
- }),
12612
- /* @__PURE__ */ jsxs("div", {
12613
- className: "dy-relative dy-grid dy-min-h-[340px] dy-gap-10 dy-p-7 sm:dy-p-10 lg:dy-grid-cols-[1.35fr_0.65fr] lg:dy-p-12",
12614
- children: [/* @__PURE__ */ jsxs("div", {
12615
- className: "dy-flex dy-flex-col dy-justify-between dy-gap-12",
12616
- children: [/* @__PURE__ */ jsxs("div", {
12617
- className: "dy-space-y-6",
12618
- children: [/* @__PURE__ */ jsxs("div", {
12619
- className: "dy-inline-flex dy-items-center dy-gap-2 dy-rounded-full dy-border dy-border-border dy-bg-muted/60 dy-px-3 dy-py-1.5 dy-text-[11px] dy-font-bold dy-uppercase dy-tracking-[0.14em] dy-text-muted-foreground",
12620
- children: [/* @__PURE__ */ jsx(Sparkles, { className: "dy-h-3.5 dy-w-3.5 dy-text-primary" }), "Dyrected setup"]
12621
- }), /* @__PURE__ */ jsxs("div", {
12622
- className: "dy-max-w-2xl dy-space-y-4",
12623
- children: [/* @__PURE__ */ jsx("h1", {
12624
- className: "dy-font-serif dy-text-4xl dy-font-bold dy-leading-[0.98] dy-tracking-tight sm:dy-text-5xl lg:dy-text-6xl",
12625
- children: "Let the guide do the heavy lifting."
12626
- }), /* @__PURE__ */ jsx("p", {
12627
- className: "dy-max-w-xl dy-text-base dy-leading-7 dy-text-muted-foreground sm:dy-text-lg",
12628
- children: "Get a guided path tailored to your project, whether you are building with an AI coding tool or wiring up the SDK yourself."
12629
- })]
12630
- })]
12631
- }), /* @__PURE__ */ jsxs("div", {
12632
- className: "dy-flex dy-flex-col dy-gap-3 sm:dy-flex-row sm:dy-items-center",
12633
- children: [/* @__PURE__ */ jsx(Button, {
12634
- asChild: true,
12635
- size: "lg",
12636
- className: "dy-h-12 dy-bg-primary dy-px-6 dy-text-primary-foreground hover:dy-bg-primary/90",
12637
- children: /* @__PURE__ */ jsxs("a", {
12638
- href: guideUrl,
12639
- target: "_blank",
12640
- rel: "noopener noreferrer",
12641
- children: [
12642
- /* @__PURE__ */ jsx(Compass, { className: "dy-h-4 dy-w-4" }),
12643
- "Open guided setup",
12644
- /* @__PURE__ */ jsx(ArrowUpRight, { className: "dy-h-4 dy-w-4" })
12645
- ]
12646
- })
12647
- }), /* @__PURE__ */ jsx(Button, {
12648
- asChild: true,
12649
- size: "lg",
12650
- variant: "ghost",
12651
- className: "dy-h-12 dy-text-card-foreground hover:dy-bg-muted hover:dy-text-card-foreground",
12652
- children: /* @__PURE__ */ jsxs("a", {
12653
- href: DOCS_URL,
12654
- target: "_blank",
12655
- rel: "noopener noreferrer",
12656
- children: [/* @__PURE__ */ jsx(BookOpen, { className: "dy-h-4 dy-w-4" }), "Browse developer docs"]
12657
- })
12658
- })]
12659
- })]
12660
- }), /* @__PURE__ */ jsx("div", {
12661
- className: "dy-relative dy-flex dy-items-end lg:dy-justify-end",
12662
- children: /* @__PURE__ */ jsxs("div", {
12663
- className: "dy-w-full dy-max-w-sm dy-rounded-2xl dy-border dy-border-border dy-bg-background/90 dy-p-5 dy-text-foreground dy-shadow-lg dy-backdrop-blur-sm",
12664
- children: [/* @__PURE__ */ jsxs("div", {
12665
- className: "dy-mb-5 dy-flex dy-items-center dy-justify-between",
12666
- children: [/* @__PURE__ */ jsx("span", {
12667
- className: "dy-text-xs dy-font-bold dy-uppercase dy-tracking-[0.12em] dy-text-muted-foreground",
12668
- children: "Context included"
12669
- }), /* @__PURE__ */ jsx(Code2, { className: "dy-h-4 dy-w-4 dy-text-primary" })]
12670
- }), /* @__PURE__ */ jsxs("dl", {
12671
- className: "dy-space-y-4 dy-text-sm",
12672
- children: [/* @__PURE__ */ jsxs("div", {
12673
- className: "dy-flex dy-items-center dy-justify-between dy-gap-4",
12674
- children: [/* @__PURE__ */ jsx("dt", {
12675
- className: "dy-text-muted-foreground",
12676
- children: "Tech stack"
12677
- }), /* @__PURE__ */ jsx("dd", {
12678
- className: "dy-font-mono dy-font-semibold dy-text-foreground",
12679
- children: stack ?? "Choose in guide"
12680
- })]
12681
- }), /* @__PURE__ */ jsxs("div", {
12682
- className: "dy-flex dy-items-center dy-justify-between dy-gap-4",
12683
- children: [/* @__PURE__ */ jsx("dt", {
12684
- className: "dy-text-muted-foreground",
12685
- children: "Site"
12686
- }), /* @__PURE__ */ jsx("dd", {
12687
- className: "dy-max-w-[180px] dy-truncate dy-font-mono dy-font-semibold dy-text-foreground",
12688
- children: config.siteId || "Not provided"
12689
- })]
12690
- })]
12691
- })]
12692
- })
12764
+ className: "dy-mx-auto dy-max-w-3xl dy-space-y-10 dy-px-4 dy-py-8",
12765
+ children: [
12766
+ /* @__PURE__ */ jsxs("div", {
12767
+ className: "dy-space-y-2",
12768
+ children: [/* @__PURE__ */ jsx("h1", {
12769
+ className: "dy-text-2xl dy-font-semibold dy-tracking-tight dy-text-foreground",
12770
+ children: "Set up Dyrected"
12771
+ }), /* @__PURE__ */ jsx("p", {
12772
+ className: "dy-text-sm dy-leading-6 dy-text-muted-foreground",
12773
+ children: "Copy the prompt below and paste it into your AI builder. It handles the setup in stages."
12774
+ })]
12775
+ }),
12776
+ /* @__PURE__ */ jsx("ol", {
12777
+ className: "dy-space-y-3",
12778
+ children: guideSteps.map((step, index) => /* @__PURE__ */ jsxs("li", {
12779
+ className: "dy-flex dy-gap-3 dy-text-sm",
12780
+ children: [/* @__PURE__ */ jsx("span", {
12781
+ className: "dy-flex dy-h-6 dy-w-6 dy-shrink-0 dy-items-center dy-justify-center dy-rounded-full dy-bg-muted dy-text-xs dy-font-semibold dy-tabular-nums dy-text-foreground",
12782
+ children: index + 1
12783
+ }), /* @__PURE__ */ jsx("span", {
12784
+ className: "dy-leading-6 dy-text-muted-foreground",
12785
+ children: step
12693
12786
  })]
12694
- })
12695
- ]
12696
- }), /* @__PURE__ */ jsxs("div", {
12697
- className: "dy-grid dy-gap-4 dy-px-1 dy-pt-5 md:dy-grid-cols-2",
12698
- children: [/* @__PURE__ */ jsxs("a", {
12699
- href: guideUrl,
12700
- target: "_blank",
12701
- rel: "noopener noreferrer",
12702
- className: "dy-group dy-flex dy-items-start dy-gap-4 dy-rounded-xl dy-border dy-bg-card dy-p-5 dy-transition-colors hover:dy-border-primary/60 hover:dy-bg-primary/[0.04] focus-visible:dy-outline-none focus-visible:dy-ring-2 focus-visible:dy-ring-ring",
12787
+ }, index))
12788
+ }),
12789
+ /* @__PURE__ */ jsxs("div", {
12790
+ className: "dy-overflow-hidden dy-rounded-xl dy-border dy-border-border dy-bg-card",
12703
12791
  children: [
12704
- /* @__PURE__ */ jsx("div", {
12705
- className: "dy-rounded-lg dy-bg-primary/15 dy-p-2.5 dy-text-foreground",
12706
- children: /* @__PURE__ */ jsx(Sparkles, { className: "dy-h-5 dy-w-5" })
12707
- }),
12708
12792
  /* @__PURE__ */ jsxs("div", {
12709
- className: "dy-min-w-0 dy-flex-1",
12710
- children: [/* @__PURE__ */ jsx("h2", {
12711
- className: "dy-font-semibold dy-text-card-foreground",
12712
- children: "Building with AI?"
12713
- }), /* @__PURE__ */ jsx("p", {
12714
- className: "dy-mt-1 dy-text-sm dy-leading-6 dy-text-muted-foreground",
12715
- children: "The guide gives your coding agent the right context and walks you through each decision."
12793
+ className: "dy-flex dy-items-center dy-justify-between dy-gap-4 dy-border-b dy-border-border dy-px-4 dy-py-3",
12794
+ children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("p", {
12795
+ className: "dy-text-xs dy-font-semibold dy-uppercase dy-tracking-[0.12em] dy-text-muted-foreground",
12796
+ children: "Dyrected setup prompt"
12797
+ }), hasCredentials && /* @__PURE__ */ jsxs("p", {
12798
+ className: "dy-mt-0.5 dy-text-xs dy-text-muted-foreground",
12799
+ children: ["Credentials pre-filled for ", config.siteName || config.siteId]
12800
+ })] }), /* @__PURE__ */ jsxs("button", {
12801
+ type: "button",
12802
+ onClick: copyPrompt,
12803
+ className: "dy-inline-flex dy-items-center dy-gap-1.5 dy-rounded-lg dy-border dy-border-border dy-bg-background dy-px-3 dy-py-1.5 dy-text-xs dy-font-semibold dy-text-foreground dy-transition-colors hover:dy-bg-muted",
12804
+ children: [copied ? /* @__PURE__ */ jsx(Check, { className: "dy-h-3.5 dy-w-3.5 dy-text-primary" }) : /* @__PURE__ */ jsx(Copy, { className: "dy-h-3.5 dy-w-3.5" }), copied ? "Copied" : "Copy prompt"]
12716
12805
  })]
12717
12806
  }),
12718
- /* @__PURE__ */ jsx(ArrowUpRight, { className: "dy-h-4 dy-w-4 dy-text-muted-foreground dy-transition-transform group-hover:dy-translate-x-0.5 group-hover:dy--translate-y-0.5" })
12807
+ /* @__PURE__ */ jsx("pre", {
12808
+ className: "dy-max-h-64 dy-overflow-y-auto dy-p-4 dy-text-[11px] dy-leading-5 dy-text-muted-foreground",
12809
+ children: /* @__PURE__ */ jsx("code", { children: promptText })
12810
+ }),
12811
+ /* @__PURE__ */ jsx("div", {
12812
+ className: "dy-border-t dy-border-border dy-px-4 dy-py-3",
12813
+ children: /* @__PURE__ */ jsxs("p", {
12814
+ className: "dy-text-xs dy-text-muted-foreground",
12815
+ children: ["Paste this into the AI builder or code agent that can edit the website code.", !hasCredentials && " The prompt will ask for your Dyrected details at the right stage."]
12816
+ })
12817
+ })
12719
12818
  ]
12720
- }), /* @__PURE__ */ jsxs("a", {
12721
- href: DOCS_URL,
12722
- target: "_blank",
12723
- rel: "noopener noreferrer",
12724
- className: "dy-group dy-flex dy-items-start dy-gap-4 dy-rounded-xl dy-border dy-bg-card dy-p-5 dy-transition-colors hover:dy-border-foreground/30 hover:dy-bg-muted/40 focus-visible:dy-outline-none focus-visible:dy-ring-2 focus-visible:dy-ring-ring",
12819
+ }),
12820
+ /* @__PURE__ */ jsxs("div", {
12821
+ className: "dy-flex dy-flex-col dy-gap-2 sm:dy-flex-row",
12725
12822
  children: [
12726
- /* @__PURE__ */ jsx("div", {
12727
- className: "dy-rounded-lg dy-bg-muted dy-p-2.5 dy-text-foreground",
12728
- children: /* @__PURE__ */ jsx(BookOpen, { className: "dy-h-5 dy-w-5" })
12823
+ /* @__PURE__ */ jsxs("a", {
12824
+ href: guideUrl,
12825
+ target: "_blank",
12826
+ rel: "noopener noreferrer",
12827
+ className: "dy-inline-flex dy-items-center dy-gap-1.5 dy-text-sm dy-text-muted-foreground dy-underline-offset-4 hover:dy-text-foreground hover:dy-underline",
12828
+ children: ["Full guide", /* @__PURE__ */ jsx(ArrowUpRight, { className: "dy-h-3.5 dy-w-3.5" })]
12729
12829
  }),
12730
- /* @__PURE__ */ jsxs("div", {
12731
- className: "dy-min-w-0 dy-flex-1",
12732
- children: [/* @__PURE__ */ jsx("h2", {
12733
- className: "dy-font-semibold dy-text-card-foreground",
12734
- children: "Need implementation detail?"
12735
- }), /* @__PURE__ */ jsx("p", {
12736
- className: "dy-mt-1 dy-text-sm dy-leading-6 dy-text-muted-foreground",
12737
- children: "Use the docs for SDK APIs, framework integrations, configuration, and production reference."
12738
- })]
12830
+ /* @__PURE__ */ jsx("span", {
12831
+ className: "dy-hidden dy-text-muted-foreground sm:dy-inline",
12832
+ "aria-hidden": "true",
12833
+ children: "·"
12739
12834
  }),
12740
- /* @__PURE__ */ jsx(ArrowUpRight, { className: "dy-h-4 dy-w-4 dy-text-muted-foreground dy-transition-transform group-hover:dy-translate-x-0.5 group-hover:dy--translate-y-0.5" })
12835
+ /* @__PURE__ */ jsxs("a", {
12836
+ href: DOCS_URL,
12837
+ target: "_blank",
12838
+ rel: "noopener noreferrer",
12839
+ className: "dy-inline-flex dy-items-center dy-gap-1.5 dy-text-sm dy-text-muted-foreground dy-underline-offset-4 hover:dy-text-foreground hover:dy-underline",
12840
+ children: ["Developer docs", /* @__PURE__ */ jsx(ArrowUpRight, { className: "dy-h-3.5 dy-w-3.5" })]
12841
+ })
12741
12842
  ]
12742
- })]
12743
- })]
12843
+ })
12844
+ ]
12744
12845
  });
12745
12846
  }
12746
12847
  //#endregion
@@ -13384,8 +13485,7 @@ function buildProviderStartUrl(baseUrl, providerId) {
13384
13485
  }
13385
13486
  function resolveAdminAuthCollection(collections, collectionSlug) {
13386
13487
  if (!collections) return void 0;
13387
- if (collectionSlug) return collections.find((collection) => collection.slug === collectionSlug);
13388
- return collections.find((collection) => collection.slug === "__admins") ?? collections.find((collection) => collection.auth);
13488
+ return collections.find((collection) => collection.slug === "__admins") ?? collections.find((collection) => collection.slug === collectionSlug) ?? collections.find((collection) => collection.auth);
13389
13489
  }
13390
13490
  function readToken(value) {
13391
13491
  if (!value || typeof value !== "object" || !("token" in value)) return null;