@dyrected/admin 2.5.47 → 2.5.49

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/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);
@@ -159,18 +158,12 @@ function DyrectedProvider({ children, apiKey: initialApiKey, baseUrl: initialBas
159
158
  schemas
160
159
  ]);
161
160
  const logout = useCallback(() => {
162
- localStorage.removeItem("dyrected_url");
163
- localStorage.removeItem("dyrected_key");
164
- localStorage.removeItem("dyrected_site_id");
165
161
  localStorage.removeItem("dyrected_token");
166
162
  localStorage.removeItem("dyrected_admin_auth_collection");
167
- setBaseUrl("");
168
- setApiKey(void 0);
169
- setSiteId(void 0);
163
+ if (client) client.clearToken();
170
164
  setAuthCollectionSlug(null);
171
165
  setUser(null);
172
- setSchemas(null);
173
- }, []);
166
+ }, [client]);
174
167
  return /* @__PURE__ */ jsx(DyrectedContext.Provider, {
175
168
  value: {
176
169
  client,
@@ -593,7 +586,7 @@ function NavGroup({ label, children, collapsed, defaultExpanded = true }) {
593
586
  })]
594
587
  });
595
588
  }
596
- function ThemeSelector({ collapsed = false, mobile = false }) {
589
+ function ThemeSelector({ collapsed = false, mobile = false, iconOnly = false }) {
597
590
  const { resolvedTheme, setTheme, theme } = useAdminTheme();
598
591
  const Icon = resolvedTheme === "dark" ? Moon : Sun;
599
592
  const options = [
@@ -618,11 +611,11 @@ function ThemeSelector({ collapsed = false, mobile = false }) {
618
611
  children: /* @__PURE__ */ jsxs(Button, {
619
612
  type: "button",
620
613
  variant: "ghost",
621
- size: mobile || collapsed ? "icon" : "sm",
622
- className: cn("dy-text-muted-foreground hover:dy-bg-accent hover:dy-text-foreground", collapsed || mobile ? "dy-h-8 dy-w-8" : "dy-h-7 dy-w-full dy-justify-start dy-px-2.5 dy-text-[11px]"),
614
+ size: mobile || collapsed || iconOnly ? "icon" : "sm",
615
+ className: cn("dy-text-muted-foreground hover:dy-bg-accent hover:dy-text-foreground", collapsed || mobile || iconOnly ? "dy-h-7 dy-w-7 dy-px-0 dy-justify-center" : "dy-h-7 dy-w-full dy-justify-start dy-px-2.5 dy-text-[11px]"),
623
616
  title: "Theme",
624
617
  "aria-label": "Change admin theme",
625
- children: [/* @__PURE__ */ jsx(Icon, { className: "dy-h-3.5 dy-w-3.5" }), !collapsed && !mobile && /* @__PURE__ */ jsx("span", { children: "Theme" })]
618
+ children: [/* @__PURE__ */ jsx(Icon, { className: "dy-h-3.5 dy-w-3.5" }), !collapsed && !mobile && !iconOnly && /* @__PURE__ */ jsx("span", { children: "Theme" })]
626
619
  })
627
620
  }), /* @__PURE__ */ jsxs(DropdownMenuContent, {
628
621
  side: collapsed || mobile ? "bottom" : "top",
@@ -642,7 +635,7 @@ function ThemeSelector({ collapsed = false, mobile = false }) {
642
635
  const OptionIcon = option.icon;
643
636
  return /* @__PURE__ */ jsxs(DropdownMenuRadioItem, {
644
637
  value: option.value,
645
- className: "dy-cursor-pointer",
638
+ className: "dy-cursor-pointer dy-space-x-2",
646
639
  children: [/* @__PURE__ */ jsx(OptionIcon, { className: "dy-h-4 dy-w-4" }), option.label]
647
640
  }, option.value);
648
641
  })
@@ -650,16 +643,16 @@ function ThemeSelector({ collapsed = false, mobile = false }) {
650
643
  ]
651
644
  })] });
652
645
  }
653
- function SidebarInner({ schemas, isLoading, location, logout, isEmbedded, collapsed, onToggleCollapse, onNavigate }) {
646
+ function SidebarInner({ schemas, isLoading, location, logout, isEmbedded, collapsed, onToggleCollapse, onNavigate, updateInfo }) {
654
647
  const { client, user } = useDyrected();
655
- const collections = schemas?.collections?.filter((c) => !c?.admin?.hidden && !c?.slug.startsWith("platform_")) ?? [];
656
- const globals = schemas?.globals?.filter((g) => !g?.admin?.hidden && !g?.slug.startsWith("platform_")) ?? [];
648
+ const collections = (schemas?.collections)?.filter((c) => !c?.admin?.hidden && !c?.slug.startsWith("platform_")) ?? [];
649
+ const globals = (schemas?.globals)?.filter((g) => !g?.admin?.hidden && !g?.slug.startsWith("platform_")) ?? [];
657
650
  const uploadCollections = collections.filter((c) => c.upload);
658
651
  const groupLabel = (text) => !collapsed ? /* @__PURE__ */ jsx("p", {
659
652
  className: "dy-px-3 dy-mb-1.5 dy-text-[10px] dy-font-semibold dy-uppercase dy-tracking-widest dy-text-muted-foreground/50",
660
653
  children: text
661
654
  }) : /* @__PURE__ */ jsx("div", { className: "dy-my-2 dy-mx-3 dy-h-px dy-bg-border" });
662
- const branding = schemas?.admin?.branding;
655
+ const branding = (schemas?.admin)?.branding;
663
656
  return /* @__PURE__ */ jsxs("div", {
664
657
  className: "dy-flex dy-flex-col dy-min-h-screen",
665
658
  children: [
@@ -790,12 +783,17 @@ function SidebarInner({ schemas, isLoading, location, logout, isEmbedded, collap
790
783
  /* @__PURE__ */ jsx(NavItem, {
791
784
  to: "/setup",
792
785
  icon: Sparkles,
793
- label: "Setup & Help",
786
+ label: /* @__PURE__ */ jsxs("div", {
787
+ className: "dy-flex dy-items-center dy-justify-between dy-w-full dy-min-w-0",
788
+ children: [/* @__PURE__ */ jsx("span", {
789
+ className: "dy-truncate",
790
+ children: "Setup & Help"
791
+ }), updateInfo?.hasUpdate && /* @__PURE__ */ jsx("span", { className: "dy-h-1.5 dy-w-1.5 dy-rounded-full dy-bg-primary dy-shrink-0 dy-ml-2" })]
792
+ }),
794
793
  active: location.pathname === "/setup",
795
794
  collapsed,
796
795
  onClick: onNavigate
797
796
  }),
798
- /* @__PURE__ */ jsx(ThemeSelector, { collapsed }),
799
797
  !isEmbedded && user && /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
800
798
  asChild: true,
801
799
  children: /* @__PURE__ */ jsxs("button", {
@@ -841,26 +839,99 @@ function SidebarInner({ schemas, isLoading, location, logout, isEmbedded, collap
841
839
  })
842
840
  ]
843
841
  })] }),
844
- onToggleCollapse && !isEmbedded && /* @__PURE__ */ jsx("button", {
845
- onClick: onToggleCollapse,
846
- className: cn("dy-group/btn dy-mt-1 dy-flex dy-h-7 dy-w-full dy-items-center dy-gap-2 dy-rounded-md dy-px-2.5 dy-text-[11px] dy-font-medium dy-text-muted-foreground/45 dy-transition-colors hover:dy-bg-accent/40 hover:dy-text-muted-foreground focus-visible:dy-outline-none focus-visible:dy-ring-2 focus-visible:dy-ring-ring", collapsed ? "dy-justify-center dy-px-2" : ""),
847
- title: collapsed ? "Expand sidebar" : "Collapse sidebar",
848
- "aria-label": collapsed ? "Expand sidebar" : "Collapse sidebar",
849
- children: collapsed ? /* @__PURE__ */ jsx(PanelLeftOpen, { className: "dy-h-3.5 dy-w-3.5" }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(PanelLeftClose, { className: "dy-h-3.5 dy-w-3.5 dy-transition-transform dy-group-hover/btn:dy--translate-x-0.5" }), /* @__PURE__ */ jsx("span", { children: "Collapse sidebar" })] })
842
+ /* @__PURE__ */ jsxs("div", {
843
+ className: cn("dy-flex dy-items-center dy-gap-1.5 dy-w-full dy-mt-1", collapsed ? "dy-flex-col dy-items-center" : "dy-flex-row dy-justify-between"),
844
+ children: [onToggleCollapse && !isEmbedded && /* @__PURE__ */ jsx("button", {
845
+ onClick: onToggleCollapse,
846
+ className: cn("dy-group/btn dy-flex dy-h-7 dy-items-center dy-gap-2 dy-rounded-md dy-px-2.5 dy-text-[11px] dy-font-medium dy-text-muted-foreground/45 dy-transition-colors hover:dy-bg-accent/40 hover:dy-text-muted-foreground focus-visible:dy-outline-none focus-visible:dy-ring-2 focus-visible:dy-ring-ring", collapsed ? "dy-justify-center dy-px-2 dy-w-full" : "dy-flex-1"),
847
+ title: collapsed ? "Expand sidebar" : "Collapse sidebar",
848
+ "aria-label": collapsed ? "Expand sidebar" : "Collapse sidebar",
849
+ children: collapsed ? /* @__PURE__ */ jsx(PanelLeftOpen, { className: "dy-h-3.5 dy-w-3.5" }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(PanelLeftClose, { className: "dy-h-3.5 dy-w-3.5 dy-transition-transform dy-group-hover/btn:dy--translate-x-0.5" }), /* @__PURE__ */ jsx("span", {
850
+ className: "dy-truncate",
851
+ children: "Collapse"
852
+ })] })
853
+ }), /* @__PURE__ */ jsx(ThemeSelector, {
854
+ collapsed,
855
+ iconOnly: !collapsed
856
+ })]
850
857
  })
851
858
  ]
852
859
  })
853
860
  ]
854
861
  });
855
862
  }
863
+ function isNewerVersion(latest, current) {
864
+ if (latest === current) return false;
865
+ const lParts = latest.split(".").map(Number);
866
+ const cParts = current.split(".").map(Number);
867
+ for (let i = 0; i < 3; i++) {
868
+ const l = lParts[i] || 0;
869
+ const c = cParts[i] || 0;
870
+ if (l > c) return true;
871
+ if (l < c) return false;
872
+ }
873
+ return false;
874
+ }
875
+ function useUpdateCheck() {
876
+ const currentVersion = "2.5.49";
877
+ const [updateInfo, setUpdateInfo] = useState(() => {
878
+ if (typeof window === "undefined") return null;
879
+ const cacheKey = "dyrected_latest_release";
880
+ const cacheTimeKey = "dyrected_latest_release_timestamp";
881
+ const cachedVersion = localStorage.getItem(cacheKey);
882
+ const cachedTimestamp = localStorage.getItem(cacheTimeKey);
883
+ if (cachedVersion && cachedTimestamp && Date.now() - Number(cachedTimestamp) < 1440 * 60 * 1e3) return {
884
+ latestVersion: cachedVersion,
885
+ hasUpdate: isNewerVersion(cachedVersion, currentVersion)
886
+ };
887
+ return null;
888
+ });
889
+ useEffect(() => {
890
+ let cancelled = false;
891
+ const cacheKey = "dyrected_latest_release";
892
+ const cacheTimeKey = "dyrected_latest_release_timestamp";
893
+ const oneDay = 1440 * 60 * 1e3;
894
+ const cachedVersion = localStorage.getItem(cacheKey);
895
+ const cachedTimestamp = localStorage.getItem(cacheTimeKey);
896
+ if (cachedVersion && cachedTimestamp && Date.now() - Number(cachedTimestamp) < oneDay) return;
897
+ async function fetchLatest() {
898
+ try {
899
+ const res = await fetch("https://registry.npmjs.org/@dyrected/core/latest");
900
+ if (!res.ok) return;
901
+ const latest = (await res.json())?.version;
902
+ if (latest) {
903
+ localStorage.setItem(cacheKey, latest);
904
+ localStorage.setItem(cacheTimeKey, String(Date.now()));
905
+ if (!cancelled) setUpdateInfo({
906
+ latestVersion: latest,
907
+ hasUpdate: isNewerVersion(latest, currentVersion)
908
+ });
909
+ }
910
+ } catch (err) {
911
+ console.error("Failed to check for updates:", err);
912
+ }
913
+ }
914
+ fetchLatest();
915
+ return () => {
916
+ cancelled = true;
917
+ };
918
+ }, [currentVersion]);
919
+ return updateInfo;
920
+ }
856
921
  function AdminShell({ children, isEmbedded = false }) {
857
922
  const { client, logout, user } = useDyrected();
858
923
  const location = useLocation();
924
+ const updateInfo = useUpdateCheck();
859
925
  const [collapsed, setCollapsed] = useState(false);
860
926
  const [mobileOpen, setMobileOpen] = useState(false);
861
927
  useEffect(() => {
862
- setMobileOpen(false);
863
- }, [location.pathname]);
928
+ if (mobileOpen) {
929
+ const timer = setTimeout(() => {
930
+ setMobileOpen(false);
931
+ }, 0);
932
+ return () => clearTimeout(timer);
933
+ }
934
+ }, [location.pathname, mobileOpen]);
864
935
  useEffect(() => {
865
936
  document.body.style.overflow = mobileOpen ? "hidden" : "";
866
937
  return () => {
@@ -875,7 +946,7 @@ function AdminShell({ children, isEmbedded = false }) {
875
946
  },
876
947
  enabled: !!client
877
948
  });
878
- const mobileBranding = schemas?.admin?.branding;
949
+ const mobileBranding = (schemas?.admin)?.branding;
879
950
  return /* @__PURE__ */ jsx(BrandingProvider, { children: /* @__PURE__ */ jsxs("div", {
880
951
  className: cn("dy-flex dy-w-full dy-relative", isEmbedded ? "dy-h-full dy-min-h-[600px]" : "dy-min-h-screen"),
881
952
  children: [
@@ -888,7 +959,8 @@ function AdminShell({ children, isEmbedded = false }) {
888
959
  logout,
889
960
  isEmbedded,
890
961
  collapsed,
891
- onToggleCollapse: () => setCollapsed((v) => !v)
962
+ onToggleCollapse: () => setCollapsed((v) => !v),
963
+ updateInfo
892
964
  })
893
965
  }),
894
966
  mobileOpen && /* @__PURE__ */ jsx("div", {
@@ -908,7 +980,8 @@ function AdminShell({ children, isEmbedded = false }) {
908
980
  logout,
909
981
  isEmbedded,
910
982
  collapsed: false,
911
- onNavigate: () => setMobileOpen(false)
983
+ onNavigate: () => setMobileOpen(false),
984
+ updateInfo
912
985
  })]
913
986
  }),
914
987
  /* @__PURE__ */ jsxs("main", {
@@ -941,7 +1014,7 @@ function AdminShell({ children, isEmbedded = false }) {
941
1014
  className: "dy-ml-auto dy-flex dy-items-center dy-gap-1.5",
942
1015
  children: [/* @__PURE__ */ jsx(ThemeSelector, { mobile: true }), user && /* @__PURE__ */ jsx("div", {
943
1016
  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",
944
- children: (user.name || user.email || "?")[0].toUpperCase()
1017
+ children: (user.name || user.email || "?").charAt(0).toUpperCase()
945
1018
  })]
946
1019
  })
947
1020
  ]
@@ -1036,6 +1109,31 @@ function getStatusLabel(doc) {
1036
1109
  }
1037
1110
  function Dashboard() {
1038
1111
  const { client, components, user } = useDyrected();
1112
+ const currentVersion = "2.5.49";
1113
+ const [latestVersion, setLatestVersion] = useState(() => {
1114
+ if (typeof window === "undefined") return null;
1115
+ return localStorage.getItem("dyrected_latest_release");
1116
+ });
1117
+ useEffect(() => {
1118
+ if (!latestVersion) fetch("https://registry.npmjs.org/@dyrected/core/latest").then((r) => r.json()).then((data) => {
1119
+ if (data?.version) {
1120
+ setLatestVersion(data.version);
1121
+ localStorage.setItem("dyrected_latest_release", data.version);
1122
+ localStorage.setItem("dyrected_latest_release_timestamp", String(Date.now()));
1123
+ }
1124
+ }).catch(() => {});
1125
+ }, [latestVersion]);
1126
+ const hasUpdate = latestVersion && latestVersion !== currentVersion && (() => {
1127
+ const lParts = latestVersion.split(".").map(Number);
1128
+ const cParts = currentVersion.split(".").map(Number);
1129
+ for (let i = 0; i < 3; i++) {
1130
+ const l = lParts[i] || 0;
1131
+ const c = cParts[i] || 0;
1132
+ if (l > c) return true;
1133
+ if (l < c) return false;
1134
+ }
1135
+ return false;
1136
+ })();
1039
1137
  const { data: schemas, isLoading: isLoadingSchemas } = useQuery({
1040
1138
  queryKey: ["schemas"],
1041
1139
  queryFn: () => client.getSchemas(),
@@ -1072,6 +1170,12 @@ function Dashboard() {
1072
1170
  return (b.updatedAt ? new Date(b.updatedAt).getTime() : 0) - aTime;
1073
1171
  }).slice(0, 6);
1074
1172
  const attentionItems = [
1173
+ ...hasUpdate ? [{
1174
+ key: "dyrected-update",
1175
+ title: `A system update is available (v${latestVersion})`,
1176
+ description: "Please notify your developer or site administrator to apply this update.",
1177
+ to: "/setup"
1178
+ }] : [],
1075
1179
  ...collections.filter((collection) => !collection.upload && !collection.admin?.useAsTitle).map((collection) => ({
1076
1180
  key: `title-${collection.slug}`,
1077
1181
  title: `${getCollectionLabel(collection)} needs a title field`,
@@ -2073,59 +2177,6 @@ var Checkbox = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__
2073
2177
  }));
2074
2178
  Checkbox.displayName = CheckboxPrimitive.Root.displayName;
2075
2179
  //#endregion
2076
- //#region src/components/ui/dialog.tsx
2077
- var Dialog = DialogPrimitive.Root;
2078
- var DialogTrigger = DialogPrimitive.Trigger;
2079
- var DialogPortal = DialogPrimitive.Portal;
2080
- var DialogOverlay = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Overlay, {
2081
- ref,
2082
- 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),
2083
- ...props
2084
- }));
2085
- DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
2086
- var DialogContent = React$1.forwardRef(({ className, children, ...props }, ref) => {
2087
- const { resolvedTheme, themeClassName } = useAdminTheme();
2088
- return /* @__PURE__ */ jsx(DialogPortal, { children: /* @__PURE__ */ jsxs("div", {
2089
- className: themeClassName,
2090
- "data-theme": resolvedTheme,
2091
- children: [/* @__PURE__ */ jsx(DialogOverlay, {}), /* @__PURE__ */ jsxs(DialogPrimitive.Content, {
2092
- ref,
2093
- 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),
2094
- ...props,
2095
- children: [children, /* @__PURE__ */ jsxs(DialogPrimitive.Close, {
2096
- 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",
2097
- children: [/* @__PURE__ */ jsx(X, { className: "dy-h-4 dy-w-4" }), /* @__PURE__ */ jsx("span", {
2098
- className: "dy-sr-only",
2099
- children: "Close"
2100
- })]
2101
- })]
2102
- })]
2103
- }) });
2104
- });
2105
- DialogContent.displayName = DialogPrimitive.Content.displayName;
2106
- var DialogHeader = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
2107
- className: cn("dy-flex dy-flex-col dy-space-y-1.5 dy-text-center sm:dy-text-left", className),
2108
- ...props
2109
- });
2110
- DialogHeader.displayName = "DialogHeader";
2111
- var DialogFooter = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
2112
- className: cn("dy-flex dy-flex-col-reverse sm:dy-flex-row sm:dy-justify-end sm:dy-space-x-2", className),
2113
- ...props
2114
- });
2115
- DialogFooter.displayName = "DialogFooter";
2116
- var DialogTitle = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Title, {
2117
- ref,
2118
- className: cn("dy-text-lg dy-font-semibold dy-leading-none dy-tracking-tight", className),
2119
- ...props
2120
- }));
2121
- DialogTitle.displayName = DialogPrimitive.Title.displayName;
2122
- var DialogDescription = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Description, {
2123
- ref,
2124
- className: cn("dy-text-sm dy-text-muted-foreground", className),
2125
- ...props
2126
- }));
2127
- DialogDescription.displayName = DialogPrimitive.Description.displayName;
2128
- //#endregion
2129
2180
  //#region src/components/ui/progress.tsx
2130
2181
  var Progress = React$1.forwardRef(({ className, value, ...props }, ref) => /* @__PURE__ */ jsx(ProgressPrimitive.Root, {
2131
2182
  ref,
@@ -2171,39 +2222,72 @@ function CsvImporter({ slug, schema, onClose }) {
2171
2222
  const [parsedRows, setParsedRows] = React$1.useState([]);
2172
2223
  const [mapping, setMapping] = React$1.useState({});
2173
2224
  const [validatedData, setValidatedData] = React$1.useState([]);
2225
+ const [isDragging, setIsDragging] = React$1.useState(false);
2226
+ const [fileError, setFileError] = React$1.useState(null);
2227
+ const [confirmedSkip, setConfirmedSkip] = React$1.useState(false);
2174
2228
  const [totalRows, setTotalRows] = React$1.useState(0);
2175
2229
  const [processedCount, setProcessedCount] = React$1.useState(0);
2176
2230
  const [successCount, setSuccessCount] = React$1.useState(0);
2177
2231
  const [failedRows, setFailedRows] = React$1.useState([]);
2232
+ const [retryableRows, setRetryableRows] = React$1.useState([]);
2178
2233
  const importableFields = React$1.useMemo(() => {
2179
2234
  return schema.fields.filter((f) => f.name !== "id" && f.name !== "createdAt" && f.name !== "updatedAt" && f.type !== "row" && f.type !== "join" && !f.admin?.hidden);
2180
2235
  }, [schema]);
2181
2236
  const requiredFields = React$1.useMemo(() => {
2182
2237
  return importableFields.filter((f) => f.required);
2183
2238
  }, [importableFields]);
2184
- const handleFileChange = (e) => {
2185
- const selectedFile = e.target.files?.[0];
2186
- if (!selectedFile) return;
2187
- Papa.parse(selectedFile, {
2239
+ const processFile = (file) => {
2240
+ setFileError(null);
2241
+ if (!(file.type === "text/csv" || file.name.toLowerCase().endsWith(".csv"))) {
2242
+ setFileError(`Unsupported file type: "${file.name}". Please upload a CSV file.`);
2243
+ return;
2244
+ }
2245
+ Papa.parse(file, {
2188
2246
  header: true,
2189
2247
  skipEmptyLines: "greedy",
2190
2248
  complete: (results) => {
2191
2249
  const headers = results.meta.fields || [];
2250
+ const rows = results.data;
2251
+ if (rows.length === 0) {
2252
+ setFileError("This CSV file has no importable rows. Please upload a file with at least one data row.");
2253
+ return;
2254
+ }
2192
2255
  setCsvHeaders(headers);
2193
- setParsedRows(results.data);
2256
+ setParsedRows(rows);
2194
2257
  const initialMapping = {};
2195
2258
  headers.forEach((header) => {
2196
- const match = importableFields.find((f) => f.name.toLowerCase() === header.toLowerCase() || f.label?.toLowerCase() === header.toLowerCase());
2197
- initialMapping[header] = match ? match.name : "__ignore__";
2259
+ initialMapping[header] = importableFields.find((f) => f.name?.toLowerCase() === header.toLowerCase() || f.label?.toLowerCase() === header.toLowerCase())?.name ?? "__ignore__";
2198
2260
  });
2199
2261
  setMapping(initialMapping);
2200
2262
  setStep("map");
2201
2263
  },
2202
2264
  error: (error) => {
2203
- console.error("PapaParse error:", error);
2265
+ setFileError(`Failed to parse CSV: ${error.message}`);
2204
2266
  }
2205
2267
  });
2206
2268
  };
2269
+ const handleFileChange = (e) => {
2270
+ const selectedFile = e.target.files?.[0];
2271
+ if (selectedFile) processFile(selectedFile);
2272
+ e.target.value = "";
2273
+ };
2274
+ const handleDragOver = (e) => {
2275
+ e.preventDefault();
2276
+ e.stopPropagation();
2277
+ setIsDragging(true);
2278
+ };
2279
+ const handleDragLeave = (e) => {
2280
+ e.preventDefault();
2281
+ e.stopPropagation();
2282
+ setIsDragging(false);
2283
+ };
2284
+ const handleDrop = (e) => {
2285
+ e.preventDefault();
2286
+ e.stopPropagation();
2287
+ setIsDragging(false);
2288
+ const file = e.dataTransfer.files?.[0];
2289
+ if (file) processFile(file);
2290
+ };
2207
2291
  const handleMapChange = (header, fieldName) => {
2208
2292
  setMapping((prev) => ({
2209
2293
  ...prev,
@@ -2318,18 +2402,21 @@ function CsvImporter({ slug, schema, onClose }) {
2318
2402
  isValid
2319
2403
  };
2320
2404
  }));
2405
+ setConfirmedSkip(false);
2321
2406
  setStep("preview");
2322
2407
  };
2323
- const startImport = async () => {
2408
+ const startImport = async (rows = validatedData) => {
2324
2409
  setStep("importing");
2325
- setTotalRows(validatedData.length);
2410
+ setTotalRows(rows.length);
2326
2411
  setProcessedCount(0);
2327
2412
  setSuccessCount(0);
2328
2413
  setFailedRows([]);
2414
+ setRetryableRows([]);
2329
2415
  let success = 0;
2330
2416
  const failures = [];
2331
- for (let i = 0; i < validatedData.length; i++) {
2332
- const rowResult = validatedData[i];
2417
+ const apiFailures = [];
2418
+ for (let i = 0; i < rows.length; i++) {
2419
+ const rowResult = rows[i];
2333
2420
  setProcessedCount(i + 1);
2334
2421
  if (!rowResult.isValid) {
2335
2422
  failures.push({
@@ -2359,17 +2446,24 @@ function CsvImporter({ slug, schema, onClose }) {
2359
2446
  success++;
2360
2447
  setSuccessCount(success);
2361
2448
  } catch (error) {
2449
+ const message = error.message || "Failed to create entry";
2362
2450
  failures.push({
2363
2451
  row: rowResult.rowNumber,
2364
2452
  data: rowResult.data,
2365
- error: error.message || "Failed to create entry"
2453
+ error: message
2366
2454
  });
2367
2455
  setFailedRows([...failures]);
2456
+ apiFailures.push(rowResult);
2368
2457
  }
2369
2458
  }
2459
+ setRetryableRows(apiFailures);
2370
2460
  queryClient.invalidateQueries({ queryKey: ["collection", slug] });
2371
2461
  setStep("complete");
2372
2462
  };
2463
+ const handleRetryFailed = () => {
2464
+ setValidatedData(retryableRows);
2465
+ startImport(retryableRows);
2466
+ };
2373
2467
  const downloadFailedCsv = () => {
2374
2468
  if (failedRows.length === 0) return;
2375
2469
  const headers = [...csvHeaders, "Import Error"];
@@ -2401,14 +2495,17 @@ function CsvImporter({ slug, schema, onClose }) {
2401
2495
  return /* @__PURE__ */ jsxs("div", {
2402
2496
  className: "dy-space-y-6",
2403
2497
  children: [
2404
- step === "upload" && /* @__PURE__ */ jsx("div", {
2498
+ step === "upload" && /* @__PURE__ */ jsxs("div", {
2405
2499
  className: "dy-space-y-4",
2406
- children: /* @__PURE__ */ jsx("div", {
2407
- 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",
2500
+ children: [/* @__PURE__ */ jsx("div", {
2501
+ 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"),
2502
+ onDragOver: handleDragOver,
2503
+ onDragLeave: handleDragLeave,
2504
+ onDrop: handleDrop,
2408
2505
  children: /* @__PURE__ */ jsxs("label", {
2409
2506
  className: "dy-cursor-pointer dy-block dy-space-y-4 dy-px-6",
2410
2507
  children: [
2411
- /* @__PURE__ */ jsx(Upload, { className: "dy-h-10 dy-w-10 dy-mx-auto dy-text-muted-foreground" }),
2508
+ /* @__PURE__ */ jsx(Upload, { className: cn("dy-h-10 dy-w-10 dy-mx-auto", isDragging ? "dy-text-primary" : "dy-text-muted-foreground") }),
2412
2509
  /* @__PURE__ */ jsxs("div", {
2413
2510
  className: "dy-space-y-1",
2414
2511
  children: [/* @__PURE__ */ jsx("p", {
@@ -2427,7 +2524,10 @@ function CsvImporter({ slug, schema, onClose }) {
2427
2524
  })
2428
2525
  ]
2429
2526
  })
2430
- })
2527
+ }), fileError && /* @__PURE__ */ jsxs("div", {
2528
+ 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",
2529
+ children: [/* @__PURE__ */ jsx(AlertCircle, { className: "dy-h-4 dy-w-4 dy-shrink-0" }), fileError]
2530
+ })]
2431
2531
  }),
2432
2532
  step === "map" && /* @__PURE__ */ jsxs("div", {
2433
2533
  className: "dy-space-y-4",
@@ -2531,19 +2631,22 @@ function CsvImporter({ slug, schema, onClose }) {
2531
2631
  })]
2532
2632
  }),
2533
2633
  /* @__PURE__ */ jsx("div", {
2534
- className: "dy-h-[300px] dy-overflow-auto dy-border dy-border-border dy-rounded-xl",
2634
+ className: "dy-overflow-x-auto dy-overflow-y-auto dy-max-h-[60vh] dy-border dy-border-border dy-rounded-xl",
2535
2635
  children: /* @__PURE__ */ jsxs("table", {
2536
- className: "dy-w-full dy-min-w-max dy-text-left dy-border-collapse",
2537
- children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", {
2538
- className: "dy-border-b dy-border-border dy-bg-muted/40 dy-text-xs dy-font-semibold dy-text-muted-foreground",
2539
- children: [/* @__PURE__ */ jsx("th", {
2540
- className: "dy-p-3 dy-w-16",
2541
- children: "Row"
2542
- }), importableFields.filter((f) => Object.values(mapping).includes(f.name)).map((f) => /* @__PURE__ */ jsx("th", {
2543
- className: "dy-p-3",
2544
- children: f.label || f.name
2545
- }, f.name))]
2546
- }) }), /* @__PURE__ */ jsx("tbody", {
2636
+ className: "dy-min-w-max dy-w-full dy-text-left dy-border-collapse",
2637
+ children: [/* @__PURE__ */ jsx("thead", {
2638
+ className: "dy-sticky dy-top-0 dy-z-10",
2639
+ children: /* @__PURE__ */ jsxs("tr", {
2640
+ className: "dy-border-b dy-border-border dy-bg-muted/40 dy-text-xs dy-font-semibold dy-text-muted-foreground",
2641
+ children: [/* @__PURE__ */ jsx("th", {
2642
+ className: "dy-p-3 dy-w-16",
2643
+ children: "Row"
2644
+ }), importableFields.filter((f) => Object.values(mapping).includes(f.name)).map((f) => /* @__PURE__ */ jsx("th", {
2645
+ className: "dy-p-3",
2646
+ children: f.label || f.name
2647
+ }, f.name))]
2648
+ })
2649
+ }), /* @__PURE__ */ jsx("tbody", {
2547
2650
  className: "dy-text-sm",
2548
2651
  children: previewRows.map((rowResult) => /* @__PURE__ */ jsxs("tr", {
2549
2652
  className: "dy-border-b dy-border-border hover:dy-bg-muted/20",
@@ -2565,6 +2668,39 @@ function CsvImporter({ slug, schema, onClose }) {
2565
2668
  })]
2566
2669
  })
2567
2670
  }),
2671
+ totalErrors > 0 && totalErrors < validatedData.length && /* @__PURE__ */ jsxs("label", {
2672
+ 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",
2673
+ children: [/* @__PURE__ */ jsx("input", {
2674
+ type: "checkbox",
2675
+ className: "dy-mt-0.5 dy-accent-amber-500",
2676
+ checked: confirmedSkip,
2677
+ onChange: (e) => setConfirmedSkip(e.target.checked)
2678
+ }), /* @__PURE__ */ jsxs("span", {
2679
+ className: "dy-text-xs dy-text-amber-700 dark:dy-text-amber-400",
2680
+ children: [
2681
+ "I understand that ",
2682
+ /* @__PURE__ */ jsxs("strong", { children: [
2683
+ totalErrors,
2684
+ " row",
2685
+ totalErrors !== 1 ? "s" : ""
2686
+ ] }),
2687
+ " with validation errors will be skipped. Only the ",
2688
+ validatedData.length - totalErrors,
2689
+ " valid row",
2690
+ validatedData.length - totalErrors !== 1 ? "s" : "",
2691
+ " will be imported."
2692
+ ]
2693
+ })]
2694
+ }),
2695
+ totalErrors === validatedData.length && /* @__PURE__ */ jsxs("div", {
2696
+ 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",
2697
+ children: [
2698
+ /* @__PURE__ */ jsx(AlertCircle, { className: "dy-h-4 dy-w-4 dy-shrink-0" }),
2699
+ "All ",
2700
+ totalErrors,
2701
+ " rows have validation errors. Fix your CSV and re-upload before importing."
2702
+ ]
2703
+ }),
2568
2704
  /* @__PURE__ */ jsxs("div", {
2569
2705
  className: "dy-flex dy-justify-between dy-pt-4",
2570
2706
  children: [/* @__PURE__ */ jsx(Button, {
@@ -2572,14 +2708,17 @@ function CsvImporter({ slug, schema, onClose }) {
2572
2708
  onClick: () => setStep("map"),
2573
2709
  children: "Back"
2574
2710
  }), /* @__PURE__ */ jsxs(Button, {
2575
- onClick: startImport,
2711
+ onClick: () => startImport(),
2576
2712
  variant: "default",
2577
2713
  className: "dy-gap-2",
2714
+ disabled: totalErrors === validatedData.length || totalErrors > 0 && !confirmedSkip,
2578
2715
  children: [
2579
2716
  /* @__PURE__ */ jsx(Play, { className: "dy-h-4 dy-w-4" }),
2580
2717
  " Start Import (",
2581
2718
  validatedData.length - totalErrors,
2582
- " Rows)"
2719
+ " Row",
2720
+ validatedData.length - totalErrors !== 1 ? "s" : "",
2721
+ ")"
2583
2722
  ]
2584
2723
  })]
2585
2724
  })
@@ -2657,12 +2796,27 @@ function CsvImporter({ slug, schema, onClose }) {
2657
2796
  failedRows.length,
2658
2797
  " rows)"
2659
2798
  ]
2660
- }), /* @__PURE__ */ jsxs(Button, {
2661
- size: "sm",
2662
- variant: "outline",
2663
- className: "dy-h-8 dy-text-xs dy-gap-1.5",
2664
- onClick: downloadFailedCsv,
2665
- children: [/* @__PURE__ */ jsx(Download, { className: "dy-h-3.5 dy-w-3.5" }), " Download Errors CSV"]
2799
+ }), /* @__PURE__ */ jsxs("div", {
2800
+ className: "dy-flex dy-items-center dy-gap-2",
2801
+ children: [retryableRows.length > 0 && /* @__PURE__ */ jsxs(Button, {
2802
+ size: "sm",
2803
+ variant: "outline",
2804
+ className: "dy-h-8 dy-text-xs dy-gap-1.5",
2805
+ onClick: handleRetryFailed,
2806
+ children: [
2807
+ /* @__PURE__ */ jsx(RotateCcw, { className: "dy-h-3.5 dy-w-3.5" }),
2808
+ " Retry ",
2809
+ retryableRows.length,
2810
+ " Failed Row",
2811
+ retryableRows.length !== 1 ? "s" : ""
2812
+ ]
2813
+ }), /* @__PURE__ */ jsxs(Button, {
2814
+ size: "sm",
2815
+ variant: "outline",
2816
+ className: "dy-h-8 dy-text-xs dy-gap-1.5",
2817
+ onClick: downloadFailedCsv,
2818
+ children: [/* @__PURE__ */ jsx(Download, { className: "dy-h-3.5 dy-w-3.5" }), " Download Errors CSV"]
2819
+ })]
2666
2820
  })]
2667
2821
  }), /* @__PURE__ */ jsx(ScrollArea, {
2668
2822
  className: "dy-h-[180px] dy-border dy-border-border dy-rounded-xl",
@@ -2707,27 +2861,6 @@ function CsvImporter({ slug, schema, onClose }) {
2707
2861
  });
2708
2862
  }
2709
2863
  //#endregion
2710
- //#region src/components/ui/csv-importer-dialog.tsx
2711
- function CsvImporterDialog({ open, onOpenChange, slug, schema }) {
2712
- const title = schema.labels?.singular || schema.slug || slug;
2713
- return /* @__PURE__ */ jsx(Dialog, {
2714
- open,
2715
- onOpenChange,
2716
- children: /* @__PURE__ */ jsxs(DialogContent, {
2717
- className: "sm:dy-max-w-2xl",
2718
- children: [/* @__PURE__ */ jsxs(DialogHeader, { children: [/* @__PURE__ */ jsx(DialogTitle, { children: "Import CSV Data" }), /* @__PURE__ */ jsxs(DialogDescription, { children: [
2719
- "Import records into the ",
2720
- title,
2721
- " collection."
2722
- ] })] }), /* @__PURE__ */ jsx(CsvImporter, {
2723
- slug,
2724
- schema,
2725
- onClose: () => onOpenChange(false)
2726
- })]
2727
- })
2728
- });
2729
- }
2730
- //#endregion
2731
2864
  //#region src/components/ui/render-cell.tsx
2732
2865
  function RenderCell({ value, field, client, schemas }) {
2733
2866
  if (value === null || value === void 0) return /* @__PURE__ */ jsx("span", {
@@ -2943,14 +3076,14 @@ function MediaGrid({ items, baseUrl, onDelete, slug }) {
2943
3076
  }
2944
3077
  //#endregion
2945
3078
  //#region src/components/ui/sheet.tsx
2946
- var Sheet = DialogPrimitive.Root;
2947
- var SheetPortal = DialogPrimitive.Portal;
2948
- var SheetOverlay = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Overlay, {
3079
+ var Sheet = SheetPrimitive.Root;
3080
+ var SheetPortal = SheetPrimitive.Portal;
3081
+ var SheetOverlay = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(SheetPrimitive.Overlay, {
2949
3082
  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),
2950
3083
  ...props,
2951
3084
  ref
2952
3085
  }));
2953
- SheetOverlay.displayName = DialogPrimitive.Overlay.displayName;
3086
+ SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
2954
3087
  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", {
2955
3088
  variants: { side: {
2956
3089
  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",
@@ -2965,11 +3098,11 @@ var SheetContent = React$1.forwardRef(({ side = "right", className, children, ..
2965
3098
  return /* @__PURE__ */ jsx(SheetPortal, { children: /* @__PURE__ */ jsxs("div", {
2966
3099
  className: themeClassName,
2967
3100
  "data-theme": resolvedTheme,
2968
- children: [/* @__PURE__ */ jsx(SheetOverlay, {}), /* @__PURE__ */ jsxs(DialogPrimitive.Content, {
3101
+ children: [/* @__PURE__ */ jsx(SheetOverlay, {}), /* @__PURE__ */ jsxs(SheetPrimitive.Content, {
2969
3102
  ref,
2970
3103
  className: cn(sheetVariants({ side }), className),
2971
3104
  ...props,
2972
- children: [children, /* @__PURE__ */ jsxs(DialogPrimitive.Close, {
3105
+ children: [children, /* @__PURE__ */ jsxs(SheetPrimitive.Close, {
2973
3106
  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",
2974
3107
  children: [/* @__PURE__ */ jsx(X, { className: "dy-h-4 dy-w-4" }), /* @__PURE__ */ jsx("span", {
2975
3108
  className: "dy-sr-only",
@@ -2979,7 +3112,7 @@ var SheetContent = React$1.forwardRef(({ side = "right", className, children, ..
2979
3112
  })]
2980
3113
  }) });
2981
3114
  });
2982
- SheetContent.displayName = DialogPrimitive.Content.displayName;
3115
+ SheetContent.displayName = SheetPrimitive.Content.displayName;
2983
3116
  var SheetHeader = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
2984
3117
  className: cn("dy-flex dy-flex-col dy-space-y-2 dy-text-center sm:dy-text-left", className),
2985
3118
  ...props
@@ -2990,18 +3123,18 @@ var SheetFooter = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
2990
3123
  ...props
2991
3124
  });
2992
3125
  SheetFooter.displayName = "SheetFooter";
2993
- var SheetTitle = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Title, {
3126
+ var SheetTitle = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(SheetPrimitive.Title, {
2994
3127
  ref,
2995
3128
  className: cn("dy-text-lg dy-font-semibold dy-text-foreground", className),
2996
3129
  ...props
2997
3130
  }));
2998
- SheetTitle.displayName = DialogPrimitive.Title.displayName;
2999
- var SheetDescription = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Description, {
3131
+ SheetTitle.displayName = SheetPrimitive.Title.displayName;
3132
+ var SheetDescription = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(SheetPrimitive.Description, {
3000
3133
  ref,
3001
3134
  className: cn("dy-text-sm dy-text-muted-foreground", className),
3002
3135
  ...props
3003
3136
  }));
3004
- SheetDescription.displayName = DialogPrimitive.Description.displayName;
3137
+ SheetDescription.displayName = SheetPrimitive.Description.displayName;
3005
3138
  //#endregion
3006
3139
  //#region src/components/ui/toggle.tsx
3007
3140
  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", {
@@ -3032,6 +3165,59 @@ var Toggle = React$1.forwardRef(({ className, variant, size, ...props }, ref) =>
3032
3165
  }));
3033
3166
  Toggle.displayName = TogglePrimitive.Root.displayName;
3034
3167
  //#endregion
3168
+ //#region src/components/ui/dialog.tsx
3169
+ var Dialog = SheetPrimitive.Root;
3170
+ var DialogTrigger = SheetPrimitive.Trigger;
3171
+ var DialogPortal = SheetPrimitive.Portal;
3172
+ var DialogOverlay = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(SheetPrimitive.Overlay, {
3173
+ ref,
3174
+ 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),
3175
+ ...props
3176
+ }));
3177
+ DialogOverlay.displayName = SheetPrimitive.Overlay.displayName;
3178
+ var DialogContent = React$1.forwardRef(({ className, children, ...props }, ref) => {
3179
+ const { resolvedTheme, themeClassName } = useAdminTheme();
3180
+ return /* @__PURE__ */ jsx(DialogPortal, { children: /* @__PURE__ */ jsxs("div", {
3181
+ className: themeClassName,
3182
+ "data-theme": resolvedTheme,
3183
+ children: [/* @__PURE__ */ jsx(DialogOverlay, {}), /* @__PURE__ */ jsxs(SheetPrimitive.Content, {
3184
+ ref,
3185
+ 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),
3186
+ ...props,
3187
+ children: [children, /* @__PURE__ */ jsxs(SheetPrimitive.Close, {
3188
+ 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",
3189
+ children: [/* @__PURE__ */ jsx(X, { className: "dy-h-4 dy-w-4" }), /* @__PURE__ */ jsx("span", {
3190
+ className: "dy-sr-only",
3191
+ children: "Close"
3192
+ })]
3193
+ })]
3194
+ })]
3195
+ }) });
3196
+ });
3197
+ DialogContent.displayName = SheetPrimitive.Content.displayName;
3198
+ var DialogHeader = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
3199
+ className: cn("dy-flex dy-flex-col dy-space-y-1.5 dy-text-center sm:dy-text-left", className),
3200
+ ...props
3201
+ });
3202
+ DialogHeader.displayName = "DialogHeader";
3203
+ var DialogFooter = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
3204
+ className: cn("dy-flex dy-flex-col-reverse sm:dy-flex-row sm:dy-justify-end sm:dy-space-x-2", className),
3205
+ ...props
3206
+ });
3207
+ DialogFooter.displayName = "DialogFooter";
3208
+ var DialogTitle = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(SheetPrimitive.Title, {
3209
+ ref,
3210
+ className: cn("dy-text-lg dy-font-semibold dy-leading-none dy-tracking-tight", className),
3211
+ ...props
3212
+ }));
3213
+ DialogTitle.displayName = SheetPrimitive.Title.displayName;
3214
+ var DialogDescription = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(SheetPrimitive.Description, {
3215
+ ref,
3216
+ className: cn("dy-text-sm dy-text-muted-foreground", className),
3217
+ ...props
3218
+ }));
3219
+ DialogDescription.displayName = SheetPrimitive.Description.displayName;
3220
+ //#endregion
3035
3221
  //#region src/components/ui/tabs.tsx
3036
3222
  var Tabs = TabsPrimitive.Root;
3037
3223
  var TabsList = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(TabsPrimitive.List, {
@@ -6615,244 +6801,228 @@ function UrlField({ schema: _schema, field, disabled, context: _context }) {
6615
6801
  const [docsLoading, setDocsLoading] = React$1.useState(false);
6616
6802
  const parseValue = (val) => {
6617
6803
  if (!val) return {
6618
- mode: "external",
6619
- data: {
6620
- type: "custom",
6621
- url: "",
6622
- label: ""
6623
- }
6804
+ type: "custom",
6805
+ url: ""
6624
6806
  };
6625
6807
  if (typeof val === "string") return {
6626
- mode: "external",
6627
- data: {
6628
- type: "custom",
6629
- url: val,
6630
- label: ""
6631
- }
6808
+ type: "custom",
6809
+ url: val
6632
6810
  };
6633
- if (typeof val === "object" && val.type) return {
6634
- mode: val.type === "internal" ? "internal" : "external",
6635
- data: val
6811
+ if (typeof val === "object") return {
6812
+ type: val.type === "internal" ? "internal" : "custom",
6813
+ url: val.url || "",
6814
+ relationTo: val.relationTo,
6815
+ value: val.value,
6816
+ label: val.label
6636
6817
  };
6637
6818
  return {
6638
- mode: "external",
6639
- data: {
6640
- type: "custom",
6641
- url: String(val),
6642
- label: ""
6643
- }
6819
+ type: "custom",
6820
+ url: String(val)
6644
6821
  };
6645
6822
  };
6646
- const { mode: currentMode, data: currentData } = parseValue(field.value);
6647
- const [mode, setMode] = React$1.useState(currentMode);
6648
- const [urlValue, setUrlValue] = React$1.useState(currentData.url || "");
6823
+ const currentData = parseValue(field.value);
6824
+ const [urlValue, setUrlValue] = React$1.useState(currentData.url);
6649
6825
  const [labelValue, setLabelValue] = React$1.useState(currentData.label || "");
6650
6826
  const [collectionValue, setCollectionValue] = React$1.useState(currentData.relationTo || "");
6651
6827
  const [docValue, setDocValue] = React$1.useState(currentData.value || "");
6652
- const [resolvedUrlValue, setResolvedUrlValue] = React$1.useState(currentMode === "internal" ? currentData.url || "" : "");
6653
- const [docSearch, setDocSearch] = React$1.useState("");
6654
- const collections = schemas?.collections || [];
6828
+ const collections = React$1.useMemo(() => schemas?.collections || [], [schemas?.collections]);
6655
6829
  React$1.useEffect(() => {
6656
- if (collectionValue && client) {
6830
+ if (!client || !schemas?.collections) return;
6831
+ const eligibleCollections = collections.filter((c) => c.admin?.previewUrl);
6832
+ if (eligibleCollections.length === 0) return;
6833
+ let active = true;
6834
+ Promise.resolve().then(() => {
6835
+ if (!active) return;
6657
6836
  setDocsLoading(true);
6658
6837
  setDocuments([]);
6659
- client.collection(collectionValue).find({ limit: 50 }).exec().then((res) => {
6660
- setDocuments(res.docs || []);
6661
- }).catch((err) => {
6662
- console.error("Failed to load documents:", err);
6663
- setDocuments([]);
6664
- }).finally(() => {
6665
- setDocsLoading(false);
6666
- });
6667
- }
6668
- }, [collectionValue, client]);
6669
- const handleUpdate = (newMode, updates) => {
6670
- let newValue;
6671
- if (newMode === "external") newValue = {
6672
- type: "custom",
6673
- url: updates.url !== void 0 ? updates.url : urlValue,
6674
- label: updates.label !== void 0 ? updates.label : labelValue
6838
+ });
6839
+ Promise.all(eligibleCollections.map((col) => client.collection(col.slug).find({ limit: 50 }).exec().then((res) => ({
6840
+ collection: col,
6841
+ docs: res.docs || []
6842
+ })).catch((err) => {
6843
+ console.error(`Failed to load documents for ${col.slug}:`, err);
6844
+ return {
6845
+ collection: col,
6846
+ docs: []
6847
+ };
6848
+ }))).then((results) => {
6849
+ if (!active) return;
6850
+ setDocuments(results.flatMap((r) => r.docs.map((doc) => ({
6851
+ ...doc,
6852
+ __collectionSlug: r.collection.slug,
6853
+ __collectionLabel: r.collection.labels?.plural || r.collection.slug,
6854
+ __previewUrl: r.collection.admin?.previewUrl
6855
+ }))));
6856
+ }).catch((err) => {
6857
+ console.error("Failed to load documents:", err);
6858
+ }).finally(() => {
6859
+ if (active) setDocsLoading(false);
6860
+ });
6861
+ return () => {
6862
+ active = false;
6675
6863
  };
6676
- else newValue = {
6677
- type: "internal",
6678
- url: (updates.url !== void 0 ? updates.url : resolvedUrlValue) || void 0,
6679
- relationTo: updates.relationTo || collectionValue,
6680
- value: updates.value || docValue,
6681
- label: updates.label !== void 0 ? updates.label : labelValue
6864
+ }, [
6865
+ client,
6866
+ schemas,
6867
+ collections
6868
+ ]);
6869
+ const handleUpdate = (url, label, relationTo, docId) => {
6870
+ const isInternal = !!(relationTo && docId);
6871
+ const newValue = {
6872
+ type: isInternal ? "internal" : "custom",
6873
+ url: url || void 0,
6874
+ relationTo: isInternal ? relationTo : void 0,
6875
+ value: isInternal ? docId : void 0,
6876
+ label: label || void 0
6682
6877
  };
6683
6878
  field.onChange(newValue);
6684
6879
  };
6685
- const handleModeChange = (newMode) => {
6686
- setMode(newMode);
6687
- if (newMode === "external") {
6688
- setUrlValue(currentData.url || "");
6689
- setLabelValue(currentData.label || "");
6690
- setResolvedUrlValue("");
6691
- } else {
6692
- setCollectionValue(currentData.relationTo || "");
6693
- setDocValue(currentData.value || "");
6694
- setResolvedUrlValue(currentData.type === "internal" ? currentData.url || "" : "");
6695
- setLabelValue(currentData.label || "");
6696
- }
6697
- };
6698
- const handleExternalUrlChange = (value) => {
6880
+ const handleUrlInputChange = (value) => {
6699
6881
  setUrlValue(value);
6700
- handleUpdate("external", { url: value });
6882
+ setCollectionValue("");
6883
+ setDocValue("");
6884
+ handleUpdate(value, labelValue);
6701
6885
  };
6702
- const handleExternalLabelChange = (value) => {
6886
+ const handleLabelInputChange = (value) => {
6703
6887
  setLabelValue(value);
6704
- handleUpdate("external", { label: value });
6705
- };
6706
- const handleCollectionChange = (value) => {
6707
- setCollectionValue(value);
6708
- setDocValue("");
6709
- setResolvedUrlValue("");
6888
+ handleUpdate(urlValue, value, collectionValue, docValue);
6710
6889
  };
6711
- const handleDocumentSelect = (docId) => {
6712
- setDocValue(docId);
6890
+ const handleDocumentSelect = (doc) => {
6891
+ setDocValue(doc.id);
6892
+ setCollectionValue(doc.__collectionSlug);
6713
6893
  setOpenPopover(false);
6714
- const urlPattern = collections.find((c) => c.slug === collectionValue)?.admin?.urlPattern;
6715
- const doc = documents.find((d) => d.id === docId);
6716
- const resolvedUrl = urlPattern && doc ? interpolateUrlPattern(urlPattern, doc) : void 0;
6717
- setResolvedUrlValue(resolvedUrl || "");
6718
- handleUpdate("internal", {
6719
- value: docId,
6720
- url: resolvedUrl
6721
- });
6722
- };
6723
- const handleInternalLabelChange = (value) => {
6724
- setLabelValue(value);
6725
- handleUpdate("internal", { label: value });
6894
+ const urlPattern = doc.__previewUrl;
6895
+ let resolvedUrl = void 0;
6896
+ if (urlPattern) {
6897
+ if (typeof urlPattern === "function") resolvedUrl = urlPattern(doc, { locale: "en" });
6898
+ else if (typeof urlPattern === "string") if (urlPattern.includes("{{")) resolvedUrl = urlPattern.replace(/{{(.*?)}}/g, (_, key) => String(doc[key.trim()] || ""));
6899
+ else try {
6900
+ const context = {
6901
+ ...doc,
6902
+ siteUrl: window.location.origin
6903
+ };
6904
+ if (urlPattern.includes("+") || urlPattern.includes("?") || urlPattern.includes("==") || urlPattern.includes("siteUrl")) resolvedUrl = jexl.evalSync(urlPattern, context);
6905
+ else resolvedUrl = interpolateUrlPattern(urlPattern, doc);
6906
+ } catch {
6907
+ resolvedUrl = interpolateUrlPattern(urlPattern, doc);
6908
+ }
6909
+ }
6910
+ if (typeof resolvedUrl === "string" && resolvedUrl.startsWith("/")) resolvedUrl = `${window.location.origin}${resolvedUrl}`;
6911
+ const finalUrl = resolvedUrl || "";
6912
+ setUrlValue(finalUrl);
6913
+ handleUpdate(finalUrl, labelValue, doc.__collectionSlug, doc.id);
6726
6914
  };
6727
- const getCollectionDisplayField = (collectionSlug) => {
6915
+ const getCollectionDisplayField = React$1.useCallback((collectionSlug) => {
6728
6916
  return collections.find((c) => c.slug === collectionSlug)?.admin?.useAsTitle || "title";
6729
- };
6730
- const getDocumentDisplay = (doc, collectionSlug) => {
6917
+ }, [collections]);
6918
+ const getDocumentDisplay = React$1.useCallback((doc, collectionSlug) => {
6731
6919
  return doc[getCollectionDisplayField(collectionSlug)] || doc.name || doc.slug || doc.id;
6732
- };
6733
- const selectedDoc = documents.find((d) => d.id === docValue);
6920
+ }, [getCollectionDisplayField]);
6921
+ const filteredDocs = React$1.useMemo(() => {
6922
+ if (!urlValue) return documents;
6923
+ const query = urlValue.toLowerCase();
6924
+ return documents.filter((doc) => {
6925
+ const display = getDocumentDisplay(doc, doc.__collectionSlug).toLowerCase();
6926
+ const slug = String(doc.slug || "").toLowerCase();
6927
+ return display.includes(query) || slug.includes(query);
6928
+ });
6929
+ }, [
6930
+ documents,
6931
+ urlValue,
6932
+ getDocumentDisplay
6933
+ ]);
6934
+ const selectedDoc = documents.find((d) => d.id === docValue && d.__collectionSlug === collectionValue);
6734
6935
  const selectedCollectionLabel = collections.find((c) => c.slug === collectionValue)?.labels?.singular || collectionValue;
6735
- return /* @__PURE__ */ jsx("div", {
6936
+ return /* @__PURE__ */ jsxs("div", {
6736
6937
  className: "dy-space-y-3",
6737
- children: /* @__PURE__ */ jsxs(Tabs, {
6738
- value: mode,
6739
- onValueChange: (val) => handleModeChange(val),
6740
- children: [
6741
- /* @__PURE__ */ jsxs(TabsList, {
6742
- className: "dy-w-full dy-grid dy-grid-cols-2 dy-bg-muted dy-p-1 dy-rounded-lg dy-h-auto",
6743
- children: [/* @__PURE__ */ jsx(TabsTrigger, {
6744
- value: "internal",
6745
- disabled,
6746
- className: "dy-rounded-md",
6747
- children: "Internal Link"
6748
- }), /* @__PURE__ */ jsx(TabsTrigger, {
6749
- value: "external",
6750
- disabled,
6751
- className: "dy-rounded-md",
6752
- children: "External Link"
6753
- })]
6754
- }),
6755
- /* @__PURE__ */ jsxs(TabsContent, {
6756
- value: "external",
6757
- className: "dy-space-y-3 dy-mt-3",
6758
- children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
6759
- className: "dy-text-xs dy-font-semibold dy-text-muted-foreground dy-uppercase dy-tracking-wider dy-block dy-mb-2",
6760
- children: "URL"
6761
- }), /* @__PURE__ */ jsx(Input, {
6762
- type: "url",
6763
- value: urlValue,
6764
- onChange: (e) => handleExternalUrlChange(e.target.value),
6765
- placeholder: "https://example.com",
6766
- disabled,
6767
- className: "dy-border-border/50"
6768
- })] }), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
6769
- className: "dy-text-xs dy-font-semibold dy-text-muted-foreground dy-uppercase dy-tracking-wider dy-block dy-mb-2",
6770
- children: "Link Label (Optional)"
6771
- }), /* @__PURE__ */ jsx(Input, {
6938
+ children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs(Popover, {
6939
+ open: openPopover && !disabled,
6940
+ onOpenChange: setOpenPopover,
6941
+ children: [/* @__PURE__ */ jsx(PopoverTrigger, {
6942
+ asChild: true,
6943
+ children: /* @__PURE__ */ jsx("div", {
6944
+ className: "dy-w-full",
6945
+ children: /* @__PURE__ */ jsx(Input, {
6772
6946
  type: "text",
6773
- value: labelValue,
6774
- onChange: (e) => handleExternalLabelChange(e.target.value),
6775
- placeholder: "e.g., Learn More",
6947
+ value: urlValue,
6948
+ onChange: (e) => handleUrlInputChange(e.target.value),
6949
+ onFocus: () => setOpenPopover(true),
6950
+ placeholder: "Type or paste a URL, or search pages...",
6776
6951
  disabled,
6777
- className: "dy-border-border/50"
6778
- })] })]
6779
- }),
6780
- /* @__PURE__ */ jsxs(TabsContent, {
6781
- value: "internal",
6782
- className: "dy-space-y-3 dy-mt-3",
6783
- children: [
6784
- /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
6785
- className: "dy-text-xs dy-font-semibold dy-text-muted-foreground dy-uppercase dy-tracking-wider dy-block dy-mb-2",
6786
- children: "Collection"
6787
- }), /* @__PURE__ */ jsxs(Select, {
6788
- value: collectionValue,
6789
- onValueChange: handleCollectionChange,
6790
- disabled,
6791
- children: [/* @__PURE__ */ jsx(SelectTrigger, {
6792
- className: "dy-bg-background dy-border-border/50",
6793
- children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Select a collection..." })
6794
- }), /* @__PURE__ */ jsx(SelectContent, { children: collections.map((col) => /* @__PURE__ */ jsx(SelectItem, {
6795
- value: col.slug,
6796
- children: col.labels?.plural || col.slug
6797
- }, col.slug)) })]
6798
- })] }),
6799
- collectionValue && /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
6800
- className: "dy-text-xs dy-font-semibold dy-text-muted-foreground dy-uppercase dy-tracking-wider dy-block dy-mb-2",
6801
- children: "Document"
6802
- }), /* @__PURE__ */ jsxs(Popover, {
6803
- open: disabled ? false : openPopover,
6804
- onOpenChange: setOpenPopover,
6805
- children: [/* @__PURE__ */ jsx(PopoverTrigger, {
6806
- asChild: true,
6807
- children: /* @__PURE__ */ jsxs(Button, {
6808
- variant: "outline",
6809
- role: "combobox",
6810
- "aria-expanded": openPopover,
6811
- disabled,
6812
- className: "dy-w-full dy-justify-between dy-font-normal dy-bg-background dy-border-border/50 dy-h-11",
6813
- children: [docsLoading ? "Loading..." : selectedDoc ? /* @__PURE__ */ jsx("span", {
6814
- className: "dy-truncate",
6815
- children: getDocumentDisplay(selectedDoc, collectionValue)
6816
- }) : /* @__PURE__ */ jsx("span", {
6817
- className: "dy-text-muted-foreground",
6818
- children: "Select a document..."
6819
- }), /* @__PURE__ */ jsx(ChevronsUpDown, { className: "dy-ml-2 dy-h-4 dy-w-4 dy-shrink-0 dy-opacity-50" })]
6820
- })
6821
- }), /* @__PURE__ */ jsx(PopoverContent, {
6822
- className: "dy-w-full dy-p-0",
6823
- align: "start",
6824
- children: /* @__PURE__ */ jsxs(Command$1, { children: [/* @__PURE__ */ jsx(CommandInput, {
6825
- placeholder: `Search ${selectedCollectionLabel}...`,
6826
- value: docSearch,
6827
- onValueChange: setDocSearch
6828
- }), /* @__PURE__ */ jsxs(CommandList, { children: [/* @__PURE__ */ jsx(CommandEmpty, { children: docsLoading ? "Searching..." : "No documents found." }), /* @__PURE__ */ jsx(CommandGroup, { children: documents.filter((doc) => {
6829
- if (!docSearch) return true;
6830
- return getDocumentDisplay(doc, collectionValue).toLowerCase().includes(docSearch.toLowerCase());
6831
- }).map((doc) => /* @__PURE__ */ jsxs(CommandItem, {
6832
- value: doc.id,
6833
- onSelect: () => handleDocumentSelect(doc.id),
6834
- children: [/* @__PURE__ */ jsx(Check, { className: cn("dy-mr-2 dy-h-4 dy-w-4", docValue === doc.id ? "dy-opacity-100" : "dy-opacity-0") }), /* @__PURE__ */ jsx("span", {
6952
+ className: "dy-bg-background dy-border-border/50 dy-h-11 dy-w-full"
6953
+ })
6954
+ })
6955
+ }), /* @__PURE__ */ jsx(PopoverContent, {
6956
+ className: "dy-p-0 dy-w-[var(--radix-popover-trigger-width)] dy-max-h-60 dy-overflow-y-auto",
6957
+ align: "start",
6958
+ onOpenAutoFocus: (e) => e.preventDefault(),
6959
+ children: /* @__PURE__ */ jsx(Command$1, {
6960
+ className: "dy-border-none",
6961
+ shouldFilter: false,
6962
+ children: /* @__PURE__ */ jsxs(CommandList, { children: [
6963
+ /* @__PURE__ */ jsx(CommandEmpty, {
6964
+ className: "dy-py-2.5 dy-px-3 dy-text-xs dy-text-muted-foreground",
6965
+ children: docsLoading ? "Loading pages..." : "No matching pages found. Press Enter to use as custom link."
6966
+ }),
6967
+ urlValue && /* @__PURE__ */ jsx(CommandGroup, {
6968
+ heading: "Custom Link",
6969
+ children: /* @__PURE__ */ jsxs(CommandItem, {
6970
+ value: `custom-${urlValue}`,
6971
+ onSelect: () => {
6972
+ handleUrlInputChange(urlValue);
6973
+ setOpenPopover(false);
6974
+ },
6975
+ className: "dy-text-primary",
6976
+ children: [
6977
+ "Use link \"",
6978
+ urlValue,
6979
+ "\""
6980
+ ]
6981
+ })
6982
+ }),
6983
+ (() => {
6984
+ const grouped = {};
6985
+ filteredDocs.forEach((doc) => {
6986
+ const key = doc.__collectionLabel;
6987
+ if (!grouped[key]) grouped[key] = [];
6988
+ grouped[key].push(doc);
6989
+ });
6990
+ return Object.entries(grouped).map(([groupName, docs]) => /* @__PURE__ */ jsx(CommandGroup, {
6991
+ heading: groupName,
6992
+ children: docs.map((doc) => /* @__PURE__ */ jsxs(CommandItem, {
6993
+ value: `${doc.__collectionSlug}-${doc.id}-${getDocumentDisplay(doc, doc.__collectionSlug)}`,
6994
+ onSelect: () => handleDocumentSelect(doc),
6995
+ children: [/* @__PURE__ */ jsx(Check, { className: cn("dy-mr-2 dy-h-4 dy-w-4", docValue === doc.id && collectionValue === doc.__collectionSlug ? "dy-opacity-100" : "dy-opacity-0") }), /* @__PURE__ */ jsx("span", {
6835
6996
  className: "dy-flex-1",
6836
- children: getDocumentDisplay(doc, collectionValue)
6997
+ children: getDocumentDisplay(doc, doc.__collectionSlug)
6837
6998
  })]
6838
- }, doc.id)) })] })] })
6839
- })]
6840
- })] }),
6841
- /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
6842
- className: "dy-text-xs dy-font-semibold dy-text-muted-foreground dy-uppercase dy-tracking-wider dy-block dy-mb-2",
6843
- children: "Link Label (Optional)"
6844
- }), /* @__PURE__ */ jsx(Input, {
6845
- type: "text",
6846
- value: labelValue,
6847
- onChange: (e) => handleInternalLabelChange(e.target.value),
6848
- placeholder: "e.g., Read More",
6849
- disabled,
6850
- className: "dy-bg-background dy-border-border/50"
6851
- })] })
6852
- ]
6999
+ }, `${doc.__collectionSlug}-${doc.id}`))
7000
+ }, groupName));
7001
+ })()
7002
+ ] })
6853
7003
  })
6854
- ]
6855
- })
7004
+ })]
7005
+ }), selectedDoc && /* @__PURE__ */ jsxs("p", {
7006
+ className: "dy-text-[11px] dy-text-muted-foreground dy-mt-1.5",
7007
+ children: ["Linked to internal page: ", /* @__PURE__ */ jsxs("span", {
7008
+ className: "dy-font-medium dy-text-foreground",
7009
+ children: [
7010
+ selectedCollectionLabel,
7011
+ ": ",
7012
+ getDocumentDisplay(selectedDoc, collectionValue)
7013
+ ]
7014
+ })]
7015
+ })] }), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
7016
+ className: "dy-text-xs dy-font-semibold dy-text-muted-foreground dy-uppercase dy-tracking-wider dy-block dy-mb-2",
7017
+ children: "Link Label (Optional)"
7018
+ }), /* @__PURE__ */ jsx(Input, {
7019
+ type: "text",
7020
+ value: labelValue,
7021
+ onChange: (e) => handleLabelInputChange(e.target.value),
7022
+ placeholder: "e.g., Learn More",
7023
+ disabled,
7024
+ className: "dy-bg-background dy-border-border/50"
7025
+ })] })]
6856
7026
  });
6857
7027
  }
6858
7028
  //#endregion
@@ -9706,7 +9876,7 @@ function CollectionListPage({ slug }) {
9706
9876
  const allAvailableColumns = React$1.useMemo(() => {
9707
9877
  if (!schema) return [];
9708
9878
  return [
9709
- ...schema.fields.filter((f) => f.name !== "password" && !f.admin?.hidden && f.type !== "row" && f.type !== "join").map((field) => field.name),
9879
+ ...schema.fields.filter((f) => f.name && f.name !== "password" && !f.admin?.hidden && f.type !== "row" && f.type !== "join").map((field) => field.name),
9710
9880
  "id",
9711
9881
  "createdAt",
9712
9882
  "updatedAt"
@@ -9714,7 +9884,7 @@ function CollectionListPage({ slug }) {
9714
9884
  }, [schema]);
9715
9885
  const defaultListColumns = React$1.useMemo(() => {
9716
9886
  if (!schema) return [];
9717
- const allDisplayFields = schema.fields.filter((f) => f.name !== "password" && !f.admin?.hidden && f.type !== "row" && f.type !== "join");
9887
+ const allDisplayFields = schema.fields.filter((f) => f.name && f.name !== "password" && !f.admin?.hidden && f.type !== "row" && f.type !== "join");
9718
9888
  const configuredColumns = Array.isArray(schema.admin?.defaultColumns) ? schema.admin.defaultColumns : [];
9719
9889
  return configuredColumns.length > 0 ? configuredColumns : allDisplayFields.slice(0, 3).map((field) => field.name);
9720
9890
  }, [schema]);
@@ -9913,7 +10083,7 @@ function CollectionListPage({ slug }) {
9913
10083
  key: "id",
9914
10084
  label: "ID"
9915
10085
  },
9916
- ...schema.fields.filter((f) => f.name !== "password" && !f.admin?.hidden && f.type !== "row" && f.type !== "join").map((f) => ({
10086
+ ...schema.fields.filter((f) => f.name !== "password" && !f.admin?.hidden && f.type !== "row" && f.type !== "join").filter((f) => !!f.name).map((f) => ({
9917
10087
  key: f.name,
9918
10088
  label: f.label || f.name
9919
10089
  })),
@@ -9988,7 +10158,7 @@ function CollectionListPage({ slug }) {
9988
10158
  }
9989
10159
  const columns = React$1.useMemo(() => {
9990
10160
  if (!schema) return [];
9991
- const allDisplayFields = schema.fields.filter((f) => f.name !== "password" && !f.admin?.hidden && f.type !== "row" && f.type !== "join");
10161
+ const allDisplayFields = schema.fields.filter((f) => f.name && f.name !== "password" && !f.admin?.hidden && f.type !== "row" && f.type !== "join");
9992
10162
  const fieldByName = new Map(allDisplayFields.map((field) => [field.name, field]));
9993
10163
  const configuredColumns = localPreference.columns.filter((col) => col.visible).map((col) => col.name);
9994
10164
  const visibleColumnNames = configuredColumns.length > 0 ? configuredColumns : allDisplayFields.slice(0, 3).map((field) => field.name);
@@ -10009,8 +10179,25 @@ function CollectionListPage({ slug }) {
10009
10179
  }
10010
10180
  return true;
10011
10181
  };
10182
+ const getPreviewUrl = (item) => {
10183
+ if (!schema?.admin?.previewUrl) return null;
10184
+ let previewUrl = typeof schema.admin.previewUrl === "function" ? schema.admin.previewUrl(item, { locale: "en" }) : schema.admin.previewUrl;
10185
+ if (typeof previewUrl === "string" && previewUrl.includes("{{")) previewUrl = previewUrl.replace(/{{(.*?)}}/g, (_, key) => String(item[key.trim()] || ""));
10186
+ else if (typeof previewUrl === "string") try {
10187
+ const context = {
10188
+ ...item,
10189
+ siteUrl: window.location.origin
10190
+ };
10191
+ if (previewUrl.includes("+") || previewUrl.includes("?") || previewUrl.includes("==") || previewUrl.includes("siteUrl")) previewUrl = jexl.evalSync(previewUrl, context);
10192
+ } catch (e) {
10193
+ console.warn("Preview URL eval failed:", e);
10194
+ }
10195
+ if (typeof previewUrl === "string" && previewUrl.startsWith("/")) previewUrl = `${window.location.origin}${previewUrl}`;
10196
+ return typeof previewUrl === "string" ? previewUrl : null;
10197
+ };
10012
10198
  const renderLinkedCell = (item, cell) => {
10013
10199
  const canDelete = canDeleteRow(item);
10200
+ const previewUrl = getPreviewUrl(item);
10014
10201
  return /* @__PURE__ */ jsxs("div", {
10015
10202
  className: "dy-flex dy-flex-col dy-gap-1",
10016
10203
  children: [/* @__PURE__ */ jsx(Link, {
@@ -10020,6 +10207,16 @@ function CollectionListPage({ slug }) {
10020
10207
  }), /* @__PURE__ */ jsxs("div", {
10021
10208
  className: "dy-flex dy-items-center dy-gap-2",
10022
10209
  children: [
10210
+ previewUrl && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("a", {
10211
+ href: previewUrl,
10212
+ target: "_blank",
10213
+ rel: "noopener noreferrer",
10214
+ className: "dy-text-[11px] dy-text-muted-foreground hover:dy-text-foreground dy-underline-offset-2 hover:dy-underline dy-transition-colors dy-duration-150",
10215
+ children: "View"
10216
+ }), /* @__PURE__ */ jsx("span", {
10217
+ className: "dy-text-muted-foreground/40 dy-text-[11px]",
10218
+ children: "|"
10219
+ })] }),
10023
10220
  /* @__PURE__ */ jsx(Link, {
10024
10221
  to: `/collections/${slug}/edit/${String(item.id)}`,
10025
10222
  className: "dy-text-[11px] dy-text-muted-foreground hover:dy-text-foreground dy-underline-offset-2 hover:dy-underline dy-transition-colors dy-duration-150",
@@ -10044,6 +10241,22 @@ function CollectionListPage({ slug }) {
10044
10241
  accessorKey: field.name,
10045
10242
  header: field.label || field.name,
10046
10243
  cell: ({ row }) => {
10244
+ if (field.name === "url") {
10245
+ if (schema?.admin?.previewUrl) {
10246
+ const resolved = getPreviewUrl(row.original);
10247
+ if (resolved) return /* @__PURE__ */ jsx("a", {
10248
+ href: resolved,
10249
+ target: "_blank",
10250
+ rel: "noopener noreferrer",
10251
+ className: "dy-text-xs dy-text-primary hover:dy-underline dy-underline-offset-2 dy-font-medium",
10252
+ children: resolved
10253
+ });
10254
+ }
10255
+ return /* @__PURE__ */ jsx("span", {
10256
+ className: "dy-text-muted-foreground",
10257
+ children: "-"
10258
+ });
10259
+ }
10047
10260
  const cell = /* @__PURE__ */ jsx(RenderCell, {
10048
10261
  value: row.getValue(field.name),
10049
10262
  field,
@@ -10238,6 +10451,38 @@ function CollectionListPage({ slug }) {
10238
10451
  })
10239
10452
  ]
10240
10453
  });
10454
+ if (isImportOpen && schema) {
10455
+ const collectionTitle = schema.labels?.plural || schema.slug;
10456
+ return /* @__PURE__ */ jsxs("div", {
10457
+ className: "dy-space-y-6 dy-animate-in lg:dy-space-y-8",
10458
+ children: [
10459
+ /* @__PURE__ */ jsx("div", {
10460
+ className: "dy-flex dy-items-center dy-gap-3",
10461
+ children: /* @__PURE__ */ jsxs(Button, {
10462
+ variant: "ghost",
10463
+ size: "sm",
10464
+ className: "dy-h-8 dy-px-2 dy-gap-1.5 dy-text-xs dy-text-muted-foreground hover:dy-text-foreground",
10465
+ onClick: () => setIsImportOpen(false),
10466
+ children: [
10467
+ /* @__PURE__ */ jsx(ArrowLeft, { className: "dy-h-3.5 dy-w-3.5" }),
10468
+ "Back to ",
10469
+ collectionTitle
10470
+ ]
10471
+ })
10472
+ }),
10473
+ /* @__PURE__ */ jsx(PageHeader, {
10474
+ title: `Import CSV — ${collectionTitle}`,
10475
+ description: `Upload a CSV file to bulk-import records into the ${collectionTitle} collection.`,
10476
+ icon: FileUp
10477
+ }),
10478
+ /* @__PURE__ */ jsx(CsvImporter, {
10479
+ slug,
10480
+ schema,
10481
+ onClose: () => setIsImportOpen(false)
10482
+ })
10483
+ ]
10484
+ });
10485
+ }
10241
10486
  return /* @__PURE__ */ jsxs("div", {
10242
10487
  className: "dy-space-y-6 dy-animate-in lg:dy-space-y-8",
10243
10488
  children: [
@@ -10511,12 +10756,6 @@ function CollectionListPage({ slug }) {
10511
10756
  componentProps: collectionComponentProps
10512
10757
  })
10513
10758
  ]
10514
- }),
10515
- /* @__PURE__ */ jsx(CsvImporterDialog, {
10516
- open: isImportOpen,
10517
- onOpenChange: setIsImportOpen,
10518
- slug,
10519
- schema
10520
10759
  })
10521
10760
  ]
10522
10761
  });
@@ -12601,9 +12840,8 @@ function GlobalEditorPage() {
12601
12840
  });
12602
12841
  }
12603
12842
  //#endregion
12604
- //#region src/pages/setup/setup-prompt.tsx
12843
+ //#region src/pages/setup/utils.ts
12605
12844
  var GUIDE_URL = "https://www.dyrected.com/guide";
12606
- var DOCS_URL = "https://docs.dyrected.com";
12607
12845
  function normalizeTechStack(techStack) {
12608
12846
  if (!techStack) return void 0;
12609
12847
  if (techStack === "next") return "nextjs";
@@ -12619,154 +12857,209 @@ function buildGuideUrl(config) {
12619
12857
  if (config.baseUrl) url.searchParams.set("endpoint", config.baseUrl);
12620
12858
  return url.toString();
12621
12859
  }
12860
+ function buildPrompt(config) {
12861
+ const { siteId, apiKey, baseUrl } = config;
12862
+ if (!(siteId && apiKey && baseUrl)) return GENERATE_CMS_PROMPT;
12863
+ const placeholder = `Ask me for the following in one message:
12864
+
12865
+ - Site ID
12866
+ - Site API key
12867
+ - Base URL
12868
+
12869
+ Wait for my reply.`;
12870
+ const replacement = `Use the following credentials:
12871
+
12872
+ - Site ID: ${siteId}
12873
+ - Site API key: ${apiKey}
12874
+ - Base URL: ${baseUrl}`;
12875
+ return GENERATE_CMS_PROMPT.replace(placeholder, replacement);
12876
+ }
12877
+ //#endregion
12878
+ //#region src/pages/setup/setup-prompt.tsx
12879
+ var DOCS_URL = "https://docs.dyrected.com";
12880
+ var steps = [
12881
+ "Paste the prompt into your AI builder — the same one that owns the website code.",
12882
+ "Review the content list the AI sends back. Correct anything missing or wrong, then say \"approved\".",
12883
+ "Your Dyrected credentials are already in the prompt. Give them to the AI when it asks at Stage 4.",
12884
+ "Test one real edit in Dyrected. If the change appears on the website, invite the client."
12885
+ ];
12886
+ var stepsNoCredentials = [
12887
+ "Paste the prompt into your AI builder — the same one that owns the website code.",
12888
+ "Review the content list the AI sends back. Correct anything missing or wrong, then say \"approved\".",
12889
+ "When the AI reaches Stage 4, it will ask for your Site ID, Site API key, and Base URL.",
12890
+ "Test one real edit in Dyrected. If the change appears on the website, invite the client."
12891
+ ];
12622
12892
  function SetupPromptUI({ config }) {
12893
+ const [copied, setCopied] = useState(false);
12623
12894
  const guideUrl = buildGuideUrl(config);
12624
- const stack = normalizeTechStack(config.defaultTechStack);
12895
+ const hasCredentials = !!(config.siteId && config.apiKey && config.baseUrl);
12896
+ const guideSteps = hasCredentials ? steps : stepsNoCredentials;
12897
+ const promptText = buildPrompt(config);
12898
+ async function copyPrompt() {
12899
+ await navigator.clipboard.writeText(promptText);
12900
+ setCopied(true);
12901
+ window.setTimeout(() => setCopied(false), 1800);
12902
+ }
12903
+ const currentVersion = "2.5.49";
12904
+ const [latestVersion, setLatestVersion] = useState(() => {
12905
+ if (typeof window === "undefined") return null;
12906
+ return localStorage.getItem("dyrected_latest_release");
12907
+ });
12908
+ useEffect(() => {
12909
+ if (!latestVersion) fetch("https://registry.npmjs.org/@dyrected/core/latest").then((r) => r.json()).then((data) => {
12910
+ if (data?.version) {
12911
+ setLatestVersion(data.version);
12912
+ localStorage.setItem("dyrected_latest_release", data.version);
12913
+ localStorage.setItem("dyrected_latest_release_timestamp", String(Date.now()));
12914
+ }
12915
+ }).catch(() => {});
12916
+ }, [latestVersion]);
12917
+ const hasUpdate = latestVersion && latestVersion !== currentVersion && (() => {
12918
+ const lParts = latestVersion.split(".").map(Number);
12919
+ const cParts = currentVersion.split(".").map(Number);
12920
+ for (let i = 0; i < 3; i++) {
12921
+ const l = lParts[i] || 0;
12922
+ const c = cParts[i] || 0;
12923
+ if (l > c) return true;
12924
+ if (l < c) return false;
12925
+ }
12926
+ return false;
12927
+ })();
12625
12928
  return /* @__PURE__ */ jsxs("div", {
12626
- className: "dy-mx-auto dy-max-w-5xl dy-py-6 lg:dy-py-10",
12627
- children: [/* @__PURE__ */ jsxs("section", {
12628
- className: "dy-relative dy-overflow-hidden dy-rounded-[28px] dy-border dy-border-border dy-bg-card dy-text-card-foreground dy-shadow-2xl",
12629
- children: [
12630
- /* @__PURE__ */ jsx("div", {
12631
- className: "dy-absolute dy-inset-y-0 dy-right-0 dy-w-1/3 dy-bg-primary",
12632
- "aria-hidden": "true"
12633
- }),
12634
- /* @__PURE__ */ jsx("div", {
12635
- className: "dy-absolute dy-inset-y-0 dy-right-[12%] dy-w-40 dy-skew-x-[-12deg] dy-bg-card",
12636
- "aria-hidden": "true"
12637
- }),
12638
- /* @__PURE__ */ jsxs("div", {
12639
- 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",
12929
+ className: "dy-mx-auto dy-max-w-3xl dy-space-y-10 dy-px-4 dy-py-8",
12930
+ children: [
12931
+ /* @__PURE__ */ jsxs("div", {
12932
+ className: "dy-rounded-xl dy-border dy-border-border dy-bg-muted/30 dy-p-5 dy-space-y-3",
12933
+ children: [/* @__PURE__ */ jsx("h2", {
12934
+ className: "dy-text-sm dy-font-semibold dy-text-foreground",
12935
+ children: "System Info & Updates"
12936
+ }), /* @__PURE__ */ jsxs("div", {
12937
+ className: "dy-flex dy-flex-col sm:dy-flex-row sm:dy-items-center dy-justify-between dy-gap-4 dy-text-xs",
12640
12938
  children: [/* @__PURE__ */ jsxs("div", {
12641
- className: "dy-flex dy-flex-col dy-justify-between dy-gap-12",
12642
- children: [/* @__PURE__ */ jsxs("div", {
12643
- className: "dy-space-y-6",
12644
- children: [/* @__PURE__ */ jsxs("div", {
12645
- 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",
12646
- children: [/* @__PURE__ */ jsx(Sparkles, { className: "dy-h-3.5 dy-w-3.5 dy-text-primary" }), "Dyrected setup"]
12647
- }), /* @__PURE__ */ jsxs("div", {
12648
- className: "dy-max-w-2xl dy-space-y-4",
12649
- children: [/* @__PURE__ */ jsx("h1", {
12650
- 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",
12651
- children: "Let the guide do the heavy lifting."
12652
- }), /* @__PURE__ */ jsx("p", {
12653
- className: "dy-max-w-xl dy-text-base dy-leading-7 dy-text-muted-foreground sm:dy-text-lg",
12654
- children: "Get a guided path tailored to your project, whether you are building with an AI coding tool or wiring up the SDK yourself."
12655
- })]
12939
+ className: "dy-space-y-1",
12940
+ children: [/* @__PURE__ */ jsxs("p", {
12941
+ className: "dy-text-muted-foreground",
12942
+ children: ["Current version: ", /* @__PURE__ */ jsxs("span", {
12943
+ className: "dy-font-mono dy-font-semibold dy-text-foreground",
12944
+ children: ["v", currentVersion]
12656
12945
  })]
12657
- }), /* @__PURE__ */ jsxs("div", {
12658
- className: "dy-flex dy-flex-col dy-gap-3 sm:dy-flex-row sm:dy-items-center",
12659
- children: [/* @__PURE__ */ jsx(Button, {
12660
- asChild: true,
12661
- size: "lg",
12662
- className: "dy-h-12 dy-bg-primary dy-px-6 dy-text-primary-foreground hover:dy-bg-primary/90",
12663
- children: /* @__PURE__ */ jsxs("a", {
12664
- href: guideUrl,
12665
- target: "_blank",
12666
- rel: "noopener noreferrer",
12667
- children: [
12668
- /* @__PURE__ */ jsx(Compass, { className: "dy-h-4 dy-w-4" }),
12669
- "Open guided setup",
12670
- /* @__PURE__ */ jsx(ArrowUpRight, { className: "dy-h-4 dy-w-4" })
12671
- ]
12672
- })
12673
- }), /* @__PURE__ */ jsx(Button, {
12674
- asChild: true,
12675
- size: "lg",
12676
- variant: "ghost",
12677
- className: "dy-h-12 dy-text-card-foreground hover:dy-bg-muted hover:dy-text-card-foreground",
12678
- children: /* @__PURE__ */ jsxs("a", {
12679
- href: DOCS_URL,
12680
- target: "_blank",
12681
- rel: "noopener noreferrer",
12682
- children: [/* @__PURE__ */ jsx(BookOpen, { className: "dy-h-4 dy-w-4" }), "Browse developer docs"]
12683
- })
12946
+ }), latestVersion && /* @__PURE__ */ jsxs("p", {
12947
+ className: "dy-text-muted-foreground",
12948
+ children: ["Latest release: ", /* @__PURE__ */ jsxs("span", {
12949
+ className: "dy-font-mono dy-font-semibold dy-text-foreground",
12950
+ children: ["v", latestVersion]
12684
12951
  })]
12685
12952
  })]
12686
- }), /* @__PURE__ */ jsx("div", {
12687
- className: "dy-relative dy-flex dy-items-end lg:dy-justify-end",
12688
- children: /* @__PURE__ */ jsxs("div", {
12689
- 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",
12690
- children: [/* @__PURE__ */ jsxs("div", {
12691
- className: "dy-mb-5 dy-flex dy-items-center dy-justify-between",
12692
- children: [/* @__PURE__ */ jsx("span", {
12693
- className: "dy-text-xs dy-font-bold dy-uppercase dy-tracking-[0.12em] dy-text-muted-foreground",
12694
- children: "Context included"
12695
- }), /* @__PURE__ */ jsx(Code2, { className: "dy-h-4 dy-w-4 dy-text-primary" })]
12696
- }), /* @__PURE__ */ jsxs("dl", {
12697
- className: "dy-space-y-4 dy-text-sm",
12698
- children: [/* @__PURE__ */ jsxs("div", {
12699
- className: "dy-flex dy-items-center dy-justify-between dy-gap-4",
12700
- children: [/* @__PURE__ */ jsx("dt", {
12701
- className: "dy-text-muted-foreground",
12702
- children: "Tech stack"
12703
- }), /* @__PURE__ */ jsx("dd", {
12704
- className: "dy-font-mono dy-font-semibold dy-text-foreground",
12705
- children: stack ?? "Choose in guide"
12706
- })]
12707
- }), /* @__PURE__ */ jsxs("div", {
12708
- className: "dy-flex dy-items-center dy-justify-between dy-gap-4",
12709
- children: [/* @__PURE__ */ jsx("dt", {
12710
- className: "dy-text-muted-foreground",
12711
- children: "Site"
12712
- }), /* @__PURE__ */ jsx("dd", {
12713
- className: "dy-max-w-[180px] dy-truncate dy-font-mono dy-font-semibold dy-text-foreground",
12714
- children: config.siteId || "Not provided"
12715
- })]
12716
- })]
12953
+ }), hasUpdate ? /* @__PURE__ */ jsxs("div", {
12954
+ className: "dy-flex dy-flex-col dy-items-start sm:dy-items-end dy-gap-1.5",
12955
+ children: [/* @__PURE__ */ jsxs("div", {
12956
+ className: "dy-flex dy-items-center dy-gap-2",
12957
+ children: [/* @__PURE__ */ jsx("span", {
12958
+ className: "dy-inline-flex dy-items-center dy-gap-1 dy-rounded-full dy-bg-primary/10 dy-px-2.5 dy-py-0.5 dy-text-[11px] dy-font-medium dy-text-primary",
12959
+ children: "Update available!"
12960
+ }), /* @__PURE__ */ jsxs("a", {
12961
+ href: "https://github.com/Dyrected/dyrected/blob/main/CHANGELOG.md",
12962
+ target: "_blank",
12963
+ rel: "noopener noreferrer",
12964
+ className: "dy-inline-flex dy-items-center dy-gap-1 dy-text-[11px] dy-text-primary dy-underline-offset-4 hover:dy-underline",
12965
+ children: ["View changelog", /* @__PURE__ */ jsx(ArrowUpRight, { className: "dy-h-3 dy-w-3" })]
12717
12966
  })]
12718
- })
12967
+ }), /* @__PURE__ */ jsxs("p", {
12968
+ className: "dy-text-muted-foreground",
12969
+ children: [
12970
+ "Run ",
12971
+ /* @__PURE__ */ jsx("code", {
12972
+ className: "dy-bg-black/5 dark:dy-bg-white/5 dy-px-1 dy-py-0.5 dy-rounded dy-font-mono",
12973
+ children: "npx dyrected upgrade"
12974
+ }),
12975
+ " to update."
12976
+ ]
12977
+ })]
12978
+ }) : /* @__PURE__ */ jsx("span", {
12979
+ className: "dy-inline-flex dy-items-center dy-gap-1 dy-rounded-full dy-bg-green-500/10 dy-px-2.5 dy-py-0.5 dy-text-[11px] dy-font-medium dy-text-green-600 dark:dy-text-green-400",
12980
+ children: "Up to date"
12719
12981
  })]
12720
- })
12721
- ]
12722
- }), /* @__PURE__ */ jsxs("div", {
12723
- className: "dy-grid dy-gap-4 dy-px-1 dy-pt-5 md:dy-grid-cols-2",
12724
- children: [/* @__PURE__ */ jsxs("a", {
12725
- href: guideUrl,
12726
- target: "_blank",
12727
- rel: "noopener noreferrer",
12728
- 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",
12982
+ })]
12983
+ }),
12984
+ /* @__PURE__ */ jsxs("div", {
12985
+ className: "dy-space-y-2",
12986
+ children: [/* @__PURE__ */ jsx("h1", {
12987
+ className: "dy-text-2xl dy-font-semibold dy-tracking-tight dy-text-foreground",
12988
+ children: "Set up Dyrected"
12989
+ }), /* @__PURE__ */ jsx("p", {
12990
+ className: "dy-text-sm dy-leading-6 dy-text-muted-foreground",
12991
+ children: "Copy the prompt below and paste it into your AI builder. It handles the setup in stages."
12992
+ })]
12993
+ }),
12994
+ /* @__PURE__ */ jsx("ol", {
12995
+ className: "dy-space-y-3",
12996
+ children: guideSteps.map((step, index) => /* @__PURE__ */ jsxs("li", {
12997
+ className: "dy-flex dy-gap-3 dy-text-sm",
12998
+ children: [/* @__PURE__ */ jsx("span", {
12999
+ 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",
13000
+ children: index + 1
13001
+ }), /* @__PURE__ */ jsx("span", {
13002
+ className: "dy-leading-6 dy-text-muted-foreground",
13003
+ children: step
13004
+ })]
13005
+ }, index))
13006
+ }),
13007
+ /* @__PURE__ */ jsxs("div", {
13008
+ className: "dy-overflow-hidden dy-rounded-xl dy-border dy-border-border dy-bg-card",
12729
13009
  children: [
12730
- /* @__PURE__ */ jsx("div", {
12731
- className: "dy-rounded-lg dy-bg-primary/15 dy-p-2.5 dy-text-foreground",
12732
- children: /* @__PURE__ */ jsx(Sparkles, { className: "dy-h-5 dy-w-5" })
12733
- }),
12734
13010
  /* @__PURE__ */ jsxs("div", {
12735
- className: "dy-min-w-0 dy-flex-1",
12736
- children: [/* @__PURE__ */ jsx("h2", {
12737
- className: "dy-font-semibold dy-text-card-foreground",
12738
- children: "Building with AI?"
12739
- }), /* @__PURE__ */ jsx("p", {
12740
- className: "dy-mt-1 dy-text-sm dy-leading-6 dy-text-muted-foreground",
12741
- children: "The guide gives your coding agent the right context and walks you through each decision."
13011
+ className: "dy-flex dy-items-center dy-justify-between dy-gap-4 dy-border-b dy-border-border dy-px-4 dy-py-3",
13012
+ children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("p", {
13013
+ className: "dy-text-xs dy-font-semibold dy-uppercase dy-tracking-[0.12em] dy-text-muted-foreground",
13014
+ children: "Dyrected setup prompt"
13015
+ }), hasCredentials && /* @__PURE__ */ jsxs("p", {
13016
+ className: "dy-mt-0.5 dy-text-xs dy-text-muted-foreground",
13017
+ children: ["Credentials pre-filled for ", config.siteName || config.siteId]
13018
+ })] }), /* @__PURE__ */ jsxs("button", {
13019
+ type: "button",
13020
+ onClick: copyPrompt,
13021
+ 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",
13022
+ 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"]
12742
13023
  })]
12743
13024
  }),
12744
- /* @__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" })
13025
+ /* @__PURE__ */ jsx("pre", {
13026
+ className: "dy-max-h-64 dy-overflow-y-auto dy-p-4 dy-text-[11px] dy-leading-5 dy-text-muted-foreground",
13027
+ children: /* @__PURE__ */ jsx("code", { children: promptText })
13028
+ }),
13029
+ /* @__PURE__ */ jsx("div", {
13030
+ className: "dy-border-t dy-border-border dy-px-4 dy-py-3",
13031
+ children: /* @__PURE__ */ jsxs("p", {
13032
+ className: "dy-text-xs dy-text-muted-foreground",
13033
+ 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."]
13034
+ })
13035
+ })
12745
13036
  ]
12746
- }), /* @__PURE__ */ jsxs("a", {
12747
- href: DOCS_URL,
12748
- target: "_blank",
12749
- rel: "noopener noreferrer",
12750
- 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",
13037
+ }),
13038
+ /* @__PURE__ */ jsxs("div", {
13039
+ className: "dy-flex dy-flex-col dy-gap-2 sm:dy-flex-row",
12751
13040
  children: [
12752
- /* @__PURE__ */ jsx("div", {
12753
- className: "dy-rounded-lg dy-bg-muted dy-p-2.5 dy-text-foreground",
12754
- children: /* @__PURE__ */ jsx(BookOpen, { className: "dy-h-5 dy-w-5" })
13041
+ /* @__PURE__ */ jsxs("a", {
13042
+ href: guideUrl,
13043
+ target: "_blank",
13044
+ rel: "noopener noreferrer",
13045
+ 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",
13046
+ children: ["Full guide", /* @__PURE__ */ jsx(ArrowUpRight, { className: "dy-h-3.5 dy-w-3.5" })]
12755
13047
  }),
12756
- /* @__PURE__ */ jsxs("div", {
12757
- className: "dy-min-w-0 dy-flex-1",
12758
- children: [/* @__PURE__ */ jsx("h2", {
12759
- className: "dy-font-semibold dy-text-card-foreground",
12760
- children: "Need implementation detail?"
12761
- }), /* @__PURE__ */ jsx("p", {
12762
- className: "dy-mt-1 dy-text-sm dy-leading-6 dy-text-muted-foreground",
12763
- children: "Use the docs for SDK APIs, framework integrations, configuration, and production reference."
12764
- })]
13048
+ /* @__PURE__ */ jsx("span", {
13049
+ className: "dy-hidden dy-text-muted-foreground sm:dy-inline",
13050
+ "aria-hidden": "true",
13051
+ children: "·"
12765
13052
  }),
12766
- /* @__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" })
13053
+ /* @__PURE__ */ jsxs("a", {
13054
+ href: DOCS_URL,
13055
+ target: "_blank",
13056
+ rel: "noopener noreferrer",
13057
+ 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",
13058
+ children: ["Developer docs", /* @__PURE__ */ jsx(ArrowUpRight, { className: "dy-h-3.5 dy-w-3.5" })]
13059
+ })
12767
13060
  ]
12768
- })]
12769
- })]
13061
+ })
13062
+ ]
12770
13063
  });
12771
13064
  }
12772
13065
  //#endregion
@@ -13410,8 +13703,7 @@ function buildProviderStartUrl(baseUrl, providerId) {
13410
13703
  }
13411
13704
  function resolveAdminAuthCollection(collections, collectionSlug) {
13412
13705
  if (!collections) return void 0;
13413
- if (collectionSlug) return collections.find((collection) => collection.slug === collectionSlug);
13414
- return collections.find((collection) => collection.slug === "__admins") ?? collections.find((collection) => collection.auth);
13706
+ return collections.find((collection) => collection.slug === "__admins") ?? collections.find((collection) => collection.slug === collectionSlug) ?? collections.find((collection) => collection.auth);
13415
13707
  }
13416
13708
  function readToken(value) {
13417
13709
  if (!value || typeof value !== "object" || !("token" in value)) return null;