@dyrected/admin 2.5.36 → 2.5.39

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, 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, ExternalLink, Eye, EyeOff, FileDown, FileIcon, FileText, Filter, Globe, Grid, 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, MoreHorizontal, PanelLeftClose, PanelLeftOpen, Pencil, Plus, Quote, RotateCcw, Save, Scissors, Search, Settings, Settings2, Share2, Shield, Smartphone, Sparkles, Strikethrough, 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, 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, MoreHorizontal, PanelLeftClose, PanelLeftOpen, Pencil, Play, Plus, Quote, RotateCcw, Save, Scissors, Search, Settings, Settings2, Share2, Shield, Smartphone, Sparkles, Strikethrough, 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";
@@ -14,12 +14,18 @@ import { cva } from "class-variance-authority";
14
14
  import { Toaster, toast } from "sonner";
15
15
  import * as PopoverPrimitive from "@radix-ui/react-popover";
16
16
  import * as SelectPrimitive from "@radix-ui/react-select";
17
- import { flexRender, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
17
+ import { flexRender, getCoreRowModel, getFilteredRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
18
18
  import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
19
+ import { DndContext, KeyboardSensor, PointerSensor, closestCenter, useSensor, useSensors } from "@dnd-kit/core";
20
+ import { SortableContext, arrayMove, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
21
+ import { CSS } from "@dnd-kit/utilities";
22
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
23
+ import Papa from "papaparse";
24
+ import * as ProgressPrimitive from "@radix-ui/react-progress";
25
+ import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
19
26
  import jexl from "jexl";
20
27
  import { DataSheetGrid, checkboxColumn, keyColumn, textColumn } from "react-datasheet-grid";
21
28
  import "react-datasheet-grid/dist/style.css";
22
- import * as SheetPrimitive from "@radix-ui/react-dialog";
23
29
  import { EditorContent, useEditor } from "@tiptap/react";
24
30
  import StarterKit from "@tiptap/starter-kit";
25
31
  import TextAlign from "@tiptap/extension-text-align";
@@ -29,7 +35,6 @@ import Underline$1 from "@tiptap/extension-underline";
29
35
  import { TableKit } from "@tiptap/extension-table";
30
36
  import * as TogglePrimitive from "@radix-ui/react-toggle";
31
37
  import * as TabsPrimitive from "@radix-ui/react-tabs";
32
- import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area";
33
38
  import ReactCrop, { centerCrop, makeAspectCrop } from "react-image-crop";
34
39
  import "react-image-crop/dist/ReactCrop.css";
35
40
  import { useDropzone } from "react-dropzone";
@@ -42,11 +47,7 @@ import * as z from "zod";
42
47
  import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
43
48
  import { format } from "date-fns";
44
49
  import { DayPicker, getDefaultClassNames } from "react-day-picker";
45
- import { DndContext, KeyboardSensor, PointerSensor, closestCenter, useSensor, useSensors } from "@dnd-kit/core";
46
- import { SortableContext, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
47
- import { CSS } from "@dnd-kit/utilities";
48
50
  import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio";
49
- import * as ProgressPrimitive from "@radix-ui/react-progress";
50
51
  import * as SeparatorPrimitive from "@radix-ui/react-separator";
51
52
  import { Blurhash } from "react-blurhash";
52
53
  //#region src/providers/dyrected-provider.tsx
@@ -1796,7 +1797,7 @@ function usePreferences(key, defaultValue) {
1796
1797
  //#endregion
1797
1798
  //#region src/components/ui/data-table.tsx
1798
1799
  var EMPTY_VISIBILITY = {};
1799
- function DataTable({ columns, data, searchKey, rowSelection: externalRowSelection, onRowSelectionChange, bulkActions, toolbarActions, persistenceKey, initialColumnVisibility }) {
1800
+ function DataTable({ columns, data, searchKey, rowSelection: externalRowSelection, onRowSelectionChange, bulkActions, toolbarActions, persistenceKey, initialColumnVisibility, hideViewButton = false }) {
1800
1801
  const [sorting, setSorting] = React$1.useState([]);
1801
1802
  const [columnFilters, setColumnFilters] = React$1.useState([]);
1802
1803
  const [columnVisibility, setColumnVisibility] = usePreferences(persistenceKey ? `visibility_${persistenceKey}` : "temp_visibility", initialColumnVisibility ?? EMPTY_VISIBILITY);
@@ -1809,7 +1810,6 @@ function DataTable({ columns, data, searchKey, rowSelection: externalRowSelectio
1809
1810
  onSortingChange: setSorting,
1810
1811
  onColumnFiltersChange: setColumnFilters,
1811
1812
  getCoreRowModel: getCoreRowModel(),
1812
- getPaginationRowModel: getPaginationRowModel(),
1813
1813
  getSortedRowModel: getSortedRowModel(),
1814
1814
  getFilteredRowModel: getFilteredRowModel(),
1815
1815
  onColumnVisibilityChange: setColumnVisibility,
@@ -1823,111 +1823,65 @@ function DataTable({ columns, data, searchKey, rowSelection: externalRowSelectio
1823
1823
  });
1824
1824
  return /* @__PURE__ */ jsxs("div", {
1825
1825
  className: "dy-w-full dy-space-y-4",
1826
- children: [
1827
- /* @__PURE__ */ jsxs("div", {
1828
- className: "dy-flex dy-flex-col dy-gap-3 sm:dy-flex-row sm:dy-items-center sm:dy-gap-4",
1829
- children: [
1830
- /* @__PURE__ */ jsxs("div", {
1831
- className: `dy-order-1 dy-grid dy-w-full dy-gap-2 sm:dy-order-3 sm:dy-ml-auto sm:dy-flex sm:dy-w-auto sm:dy-items-center ${toolbarActions ? "dy-grid-cols-3" : "dy-grid-cols-1"}`,
1832
- children: [toolbarActions, /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
1833
- asChild: true,
1834
- children: /* @__PURE__ */ jsxs(Button, {
1835
- variant: "outline",
1836
- size: "sm",
1837
- className: "dy-flex dy-h-9 dy-w-full dy-gap-2 sm:dy-h-8 sm:dy-w-auto",
1838
- children: [/* @__PURE__ */ jsx(Settings2, { className: "dy-h-4 dy-w-4" }), "View"]
1839
- })
1840
- }), /* @__PURE__ */ jsx(DropdownMenuContent, {
1841
- align: "end",
1842
- children: table.getAllColumns().filter((column) => column.getCanHide()).map((column) => {
1843
- return /* @__PURE__ */ jsx(DropdownMenuCheckboxItem, {
1844
- className: "dy-capitalize",
1845
- checked: column.getIsVisible(),
1846
- onCheckedChange: (value) => column.toggleVisibility(!!value),
1847
- children: column.id
1848
- }, column.id);
1849
- })
1850
- })] })]
1851
- }),
1852
- searchKey && /* @__PURE__ */ jsx(Input, {
1853
- placeholder: `Search ${searchKey}...`,
1854
- value: table.getColumn(searchKey)?.getFilterValue() ?? "",
1855
- onChange: (event) => table.getColumn(searchKey)?.setFilterValue(event.target.value),
1856
- className: "dy-order-2 dy-h-9 dy-w-full sm:dy-order-1 sm:dy-max-w-sm"
1857
- }),
1858
- bulkActions && table.getFilteredSelectedRowModel().rows.length > 0 && /* @__PURE__ */ jsx("div", {
1859
- className: "dy-order-3 dy-flex dy-w-full dy-items-center dy-gap-2 dy-animate-in dy-slide-in-from-left-2 sm:dy-order-2 sm:dy-w-auto",
1860
- children: bulkActions(table.getFilteredSelectedRowModel().rows.map((r) => r.original.id))
1861
- })
1862
- ]
1863
- }),
1864
- /* @__PURE__ */ jsx("div", {
1865
- className: "dy-overflow-x-auto dy-rounded-md dy-border dy-border-border/40",
1866
- children: /* @__PURE__ */ jsxs(Table$1, {
1867
- className: "dy-min-w-[720px]",
1868
- children: [/* @__PURE__ */ jsx(TableHeader, { children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx(TableRow, { children: headerGroup.headers.map((header) => {
1869
- return /* @__PURE__ */ jsx(TableHead, { children: header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext()) }, header.id);
1870
- }) }, headerGroup.id)) }), /* @__PURE__ */ jsx(TableBody, {
1871
- className: "dy-border-t dy-border-border/40",
1872
- children: table.getRowModel().rows?.length ? table.getRowModel().rows.map((row) => /* @__PURE__ */ jsx(TableRow, {
1873
- "data-state": row.getIsSelected() && "selected",
1874
- className: "dy-border-none even:dy-bg-primary/[0.03] hover:dy-bg-primary/[0.06] dy-transition-colors dy-duration-200",
1875
- children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx(TableCell, {
1876
- className: "dy-py-4 dy-px-4 dy-border-none first:dy-pl-4 last:dy-pr-4",
1877
- children: flexRender(cell.column.columnDef.cell, cell.getContext())
1878
- }, cell.id))
1879
- }, row.id)) : /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, {
1880
- colSpan: columns.length,
1881
- className: "dy-h-24 dy-text-center",
1882
- children: "No results."
1883
- }) })
1884
- })]
1885
- })
1886
- }),
1887
- /* @__PURE__ */ jsxs("div", {
1888
- className: "dy-flex dy-flex-col dy-gap-3 dy-px-2 sm:dy-flex-row sm:dy-items-center sm:dy-justify-between",
1889
- children: [/* @__PURE__ */ jsxs("div", {
1890
- className: "dy-flex-1 dy-text-xs dy-text-muted-foreground sm:dy-text-sm",
1891
- children: [
1892
- table.getFilteredSelectedRowModel().rows.length,
1893
- " of",
1894
- " ",
1895
- table.getFilteredRowModel().rows.length,
1896
- " row(s) selected."
1897
- ]
1898
- }), /* @__PURE__ */ jsxs("div", {
1899
- className: "dy-flex dy-items-center dy-justify-between dy-gap-2 sm:dy-justify-end",
1900
- children: [
1901
- /* @__PURE__ */ jsx(Button, {
1902
- variant: "outline",
1903
- size: "sm",
1904
- className: "dy-h-9 dy-w-9 dy-p-0",
1905
- onClick: () => table.previousPage(),
1906
- disabled: !table.getCanPreviousPage(),
1907
- children: /* @__PURE__ */ jsx(ChevronLeft, { className: "dy-h-4 dy-w-4" })
1908
- }),
1909
- /* @__PURE__ */ jsxs("span", {
1910
- className: "dy-min-w-0 dy-text-center dy-text-xs dy-font-medium sm:dy-text-sm",
1911
- children: [
1912
- "Page ",
1913
- table.getState().pagination.pageIndex + 1,
1914
- " of",
1915
- " ",
1916
- table.getPageCount()
1917
- ]
1918
- }),
1919
- /* @__PURE__ */ jsx(Button, {
1826
+ children: [/* @__PURE__ */ jsxs("div", {
1827
+ className: "dy-flex dy-flex-col dy-gap-3 sm:dy-flex-row sm:dy-items-center sm:dy-gap-4",
1828
+ children: [
1829
+ /* @__PURE__ */ jsxs("div", {
1830
+ className: `dy-order-1 dy-grid dy-w-full dy-gap-2 sm:dy-order-3 sm:dy-ml-auto sm:dy-flex sm:dy-w-auto sm:dy-items-center ${toolbarActions ? hideViewButton ? "dy-grid-cols-2" : "dy-grid-cols-3" : hideViewButton ? "dy-grid-cols-1" : "dy-grid-cols-2"}`,
1831
+ children: [toolbarActions, !hideViewButton && /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
1832
+ asChild: true,
1833
+ children: /* @__PURE__ */ jsxs(Button, {
1920
1834
  variant: "outline",
1921
1835
  size: "sm",
1922
- className: "dy-h-9 dy-w-9 dy-p-0",
1923
- onClick: () => table.nextPage(),
1924
- disabled: !table.getCanNextPage(),
1925
- children: /* @__PURE__ */ jsx(ChevronRight, { className: "dy-h-4 dy-w-4" })
1836
+ className: "dy-flex dy-h-9 dy-w-full dy-gap-2 sm:dy-h-8 sm:dy-w-auto",
1837
+ children: [/* @__PURE__ */ jsx(Settings2, { className: "dy-h-4 dy-w-4" }), "View"]
1926
1838
  })
1927
- ]
1839
+ }), /* @__PURE__ */ jsx(DropdownMenuContent, {
1840
+ align: "end",
1841
+ children: table.getAllColumns().filter((column) => column.getCanHide()).map((column) => {
1842
+ return /* @__PURE__ */ jsx(DropdownMenuCheckboxItem, {
1843
+ className: "dy-capitalize",
1844
+ checked: column.getIsVisible(),
1845
+ onCheckedChange: (value) => column.toggleVisibility(!!value),
1846
+ children: column.id
1847
+ }, column.id);
1848
+ })
1849
+ })] })]
1850
+ }),
1851
+ searchKey && /* @__PURE__ */ jsx(Input, {
1852
+ placeholder: `Search ${searchKey}...`,
1853
+ value: table.getColumn(searchKey)?.getFilterValue() ?? "",
1854
+ onChange: (event) => table.getColumn(searchKey)?.setFilterValue(event.target.value),
1855
+ className: "dy-order-2 dy-h-9 dy-w-full sm:dy-order-1 sm:dy-max-w-sm"
1856
+ }),
1857
+ bulkActions && table.getFilteredSelectedRowModel().rows.length > 0 && /* @__PURE__ */ jsx("div", {
1858
+ className: "dy-order-3 dy-flex dy-w-full dy-items-center dy-gap-2 dy-animate-in dy-slide-in-from-left-2 sm:dy-order-2 sm:dy-w-auto",
1859
+ children: bulkActions(table.getFilteredSelectedRowModel().rows.map((r) => r.original.id))
1860
+ })
1861
+ ]
1862
+ }), /* @__PURE__ */ jsx("div", {
1863
+ className: "dy-overflow-x-auto dy-rounded-md dy-border dy-border-border/40",
1864
+ children: /* @__PURE__ */ jsxs(Table$1, {
1865
+ className: "dy-min-w-[720px]",
1866
+ children: [/* @__PURE__ */ jsx(TableHeader, { children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx(TableRow, { children: headerGroup.headers.map((header) => {
1867
+ return /* @__PURE__ */ jsx(TableHead, { children: header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext()) }, header.id);
1868
+ }) }, headerGroup.id)) }), /* @__PURE__ */ jsx(TableBody, {
1869
+ className: "dy-border-t dy-border-border/40",
1870
+ children: table.getRowModel().rows?.length ? table.getRowModel().rows.map((row) => /* @__PURE__ */ jsx(TableRow, {
1871
+ "data-state": row.getIsSelected() && "selected",
1872
+ className: "dy-border-none even:dy-bg-primary/[0.03] hover:dy-bg-primary/[0.06] dy-transition-colors dy-duration-200",
1873
+ children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx(TableCell, {
1874
+ className: "dy-py-4 dy-px-4 dy-border-none first:dy-pl-4 last:dy-pr-4",
1875
+ children: flexRender(cell.column.columnDef.cell, cell.getContext())
1876
+ }, cell.id))
1877
+ }, row.id)) : /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, {
1878
+ colSpan: columns.length,
1879
+ className: "dy-h-24 dy-text-center",
1880
+ children: "No results."
1881
+ }) })
1928
1882
  })]
1929
1883
  })
1930
- ]
1884
+ })]
1931
1885
  });
1932
1886
  }
1933
1887
  //#endregion
@@ -1943,140 +1897,791 @@ var Checkbox = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__
1943
1897
  }));
1944
1898
  Checkbox.displayName = CheckboxPrimitive.Root.displayName;
1945
1899
  //#endregion
1946
- //#region src/components/ui/render-cell.tsx
1947
- function RenderCell({ value, field, client, schemas }) {
1948
- if (value === null || value === void 0) return /* @__PURE__ */ jsx("span", {
1949
- className: "dy-text-muted-foreground",
1950
- children: "-"
1951
- });
1952
- if (field.type === "boolean" || typeof value === "boolean") return /* @__PURE__ */ jsx(Badge, {
1953
- variant: value ? "default" : "secondary",
1954
- children: value ? "Yes" : "No"
1955
- });
1956
- if (field.type === "date") {
1957
- const date = new Date(value);
1958
- return /* @__PURE__ */ jsxs("div", {
1959
- className: "dy-flex dy-items-center dy-gap-1.5 dy-text-muted-foreground",
1960
- children: [/* @__PURE__ */ jsx(Calendar, { className: "dy-h-3 dy-w-3" }), /* @__PURE__ */ jsx("span", {
1961
- className: "dy-text-xs",
1962
- children: date.toLocaleDateString()
1900
+ //#region src/components/ui/dialog.tsx
1901
+ var Dialog = DialogPrimitive.Root;
1902
+ var DialogTrigger = DialogPrimitive.Trigger;
1903
+ var DialogPortal = DialogPrimitive.Portal;
1904
+ var DialogOverlay = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Overlay, {
1905
+ ref,
1906
+ 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),
1907
+ ...props
1908
+ }));
1909
+ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
1910
+ var DialogContent = React$1.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsx(DialogPortal, { children: /* @__PURE__ */ jsxs("div", {
1911
+ className: "dy-admin-ui",
1912
+ children: [/* @__PURE__ */ jsx(DialogOverlay, {}), /* @__PURE__ */ jsxs(DialogPrimitive.Content, {
1913
+ ref,
1914
+ 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),
1915
+ ...props,
1916
+ children: [children, /* @__PURE__ */ jsxs(DialogPrimitive.Close, {
1917
+ 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",
1918
+ children: [/* @__PURE__ */ jsx(X, { className: "dy-h-4 dy-w-4" }), /* @__PURE__ */ jsx("span", {
1919
+ className: "dy-sr-only",
1920
+ children: "Close"
1963
1921
  })]
1964
- });
1965
- }
1966
- const relationTo = field.relationTo || field.collection;
1967
- if (field.type === "image" || field.type === "relationship" && isUploadCollection(relationTo, schemas)) {
1968
- if (!value) return /* @__PURE__ */ jsx("span", {
1969
- className: "dy-text-muted-foreground",
1970
- children: "-"
1971
- });
1972
- const url = getMediaUrl(value, client?.getBaseUrl() || "");
1973
- if (!url) return /* @__PURE__ */ jsx("span", {
1974
- className: "dy-text-muted-foreground",
1975
- children: "-"
1976
- });
1977
- return /* @__PURE__ */ jsx("div", {
1978
- className: "dy-h-8 dy-w-8 dy-rounded dy-overflow-hidden dy-border dy-bg-muted dy-shadow-sm",
1979
- children: /* @__PURE__ */ jsx("img", {
1980
- src: url,
1981
- className: "dy-h-full dy-w-full dy-object-cover",
1982
- alt: ""
1983
- })
1984
- });
1985
- }
1986
- if (field.type === "relationship" && typeof value === "object") {
1987
- const relTo = field.relationTo || field.collection;
1988
- const displayValue = value[(schemas?.collections?.find((c) => c?.slug === relTo))?.admin?.useAsTitle || "title"] || value.name || value.id || "Unknown";
1989
- return /* @__PURE__ */ jsx("div", {
1990
- className: "dy-flex dy-items-center dy-gap-2",
1991
- children: /* @__PURE__ */ jsx(Badge, {
1992
- variant: "outline",
1993
- className: "dy-font-normal dy-border-primary/20 dy-bg-primary/5 dy-text-primary",
1994
- children: String(displayValue)
1995
- })
1996
- });
1997
- }
1998
- if (Array.isArray(value)) return /* @__PURE__ */ jsxs("div", {
1999
- className: "dy-flex dy-flex-wrap dy-gap-1",
2000
- children: [value.slice(0, 2).map((item, i) => /* @__PURE__ */ jsx(Badge, {
2001
- variant: "outline",
2002
- className: "dy-text-[10px] dy-px-1.5 dy-h-5",
2003
- children: typeof item === "object" ? item.title || item.name || item.id : String(item)
2004
- }, i)), value.length > 2 && /* @__PURE__ */ jsxs("span", {
2005
- className: "dy-text-[10px] dy-text-muted-foreground",
2006
- children: [
2007
- "+",
2008
- value.length - 2,
2009
- " more"
2010
- ]
2011
1922
  })]
2012
- });
2013
- if (typeof value === "object" && !Array.isArray(value)) {
2014
- const entries = Object.entries(value).filter(([_, v]) => typeof v !== "object" && v !== null && v !== void 0).slice(0, 3);
2015
- if (entries.length > 0) return /* @__PURE__ */ jsxs("span", {
2016
- className: "dy-text-[11px] dy-text-muted-foreground dy-font-medium dy-leading-tight",
2017
- children: [entries.map(([k, v]) => `${k}: ${String(v)}`).join(", "), Object.keys(value).length > 3 ? "..." : ""]
2018
- });
2019
- return /* @__PURE__ */ jsxs("span", {
2020
- className: "dy-text-[11px] dy-text-muted-foreground dy-font-mono dy-bg-muted/30 dy-px-1 dy-rounded",
2021
- children: [JSON.stringify(value).slice(0, 30), JSON.stringify(value).length > 30 ? "..." : ""]
2022
- });
2023
- }
2024
- return /* @__PURE__ */ jsx("span", {
2025
- className: "dy-text-sm dy-font-medium",
2026
- children: typeof value === "object" ? JSON.stringify(value).slice(0, 50) : String(value)
2027
- });
2028
- }
2029
- function isUploadCollection(slug, schemas) {
2030
- if (!slug) return false;
2031
- return !!(schemas?.collections?.find((c) => c?.slug === slug))?.upload;
2032
- }
1923
+ })]
1924
+ }) }));
1925
+ DialogContent.displayName = DialogPrimitive.Content.displayName;
1926
+ var DialogHeader = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
1927
+ className: cn("dy-flex dy-flex-col dy-space-y-1.5 dy-text-center sm:dy-text-left", className),
1928
+ ...props
1929
+ });
1930
+ DialogHeader.displayName = "DialogHeader";
1931
+ var DialogFooter = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
1932
+ className: cn("dy-flex dy-flex-col-reverse sm:dy-flex-row sm:dy-justify-end sm:dy-space-x-2", className),
1933
+ ...props
1934
+ });
1935
+ DialogFooter.displayName = "DialogFooter";
1936
+ var DialogTitle = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Title, {
1937
+ ref,
1938
+ className: cn("dy-text-lg dy-font-semibold dy-leading-none dy-tracking-tight", className),
1939
+ ...props
1940
+ }));
1941
+ DialogTitle.displayName = DialogPrimitive.Title.displayName;
1942
+ var DialogDescription = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Description, {
1943
+ ref,
1944
+ className: cn("dy-text-sm dy-text-muted-foreground", className),
1945
+ ...props
1946
+ }));
1947
+ DialogDescription.displayName = DialogPrimitive.Description.displayName;
2033
1948
  //#endregion
2034
- //#region src/components/ui/page-header.tsx
2035
- function PageHeader({ title, description, icon: Icon, children }) {
2036
- return /* @__PURE__ */ jsxs("div", {
2037
- className: "dy-mb-6 dy-flex dy-flex-col dy-gap-4 sm:dy-mb-8 sm:dy-flex-row sm:dy-items-end sm:dy-justify-between",
2038
- children: [/* @__PURE__ */ jsxs("div", {
2039
- className: "dy-min-w-0",
2040
- children: [/* @__PURE__ */ jsxs("div", {
2041
- className: "dy-mb-1 dy-flex dy-items-start dy-gap-3",
2042
- children: [Icon && /* @__PURE__ */ jsx(Icon, { className: "dy-mt-1 dy-h-4 dy-w-4 dy-flex-shrink-0 dy-text-primary/60" }), /* @__PURE__ */ jsx("h1", {
2043
- className: "dy-min-w-0 dy-break-words dy-text-xl dy-font-bold dy-tracking-tight dy-text-foreground sm:dy-text-2xl",
2044
- children: title
2045
- })]
2046
- }), description && /* @__PURE__ */ jsx("p", {
2047
- className: "dy-max-w-2xl dy-text-[11px] dy-font-medium dy-leading-5 dy-text-muted-foreground/60",
2048
- children: description
2049
- })]
2050
- }), /* @__PURE__ */ jsx("div", {
2051
- className: "dy-flex dy-w-full dy-flex-col dy-gap-2 sm:dy-w-auto sm:dy-flex-row sm:dy-items-center sm:dy-gap-3",
1949
+ //#region src/components/ui/progress.tsx
1950
+ var Progress = React$1.forwardRef(({ className, value, ...props }, ref) => /* @__PURE__ */ jsx(ProgressPrimitive.Root, {
1951
+ ref,
1952
+ className: cn("dy-relative dy-h-4 dy-w-full dy-overflow-hidden dy-rounded-full dy-bg-secondary", className),
1953
+ ...props,
1954
+ children: /* @__PURE__ */ jsx(ProgressPrimitive.Indicator, {
1955
+ className: "dy-h-full dy-w-full dy-flex-1 dy-bg-primary dy-transition-all",
1956
+ style: { transform: `translateX(-${100 - (value || 0)}%)` }
1957
+ })
1958
+ }));
1959
+ Progress.displayName = ProgressPrimitive.Root.displayName;
1960
+ //#endregion
1961
+ //#region src/components/ui/scroll-area.tsx
1962
+ var ScrollArea = React$1.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(ScrollAreaPrimitive.Root, {
1963
+ ref,
1964
+ className: cn("dy-relative dy-overflow-hidden", className),
1965
+ ...props,
1966
+ children: [
1967
+ /* @__PURE__ */ jsx(ScrollAreaPrimitive.Viewport, {
1968
+ className: "dy-h-full dy-w-full dy-rounded-[inherit]",
2052
1969
  children
2053
- })]
2054
- });
2055
- }
1970
+ }),
1971
+ /* @__PURE__ */ jsx(ScrollBar, {}),
1972
+ /* @__PURE__ */ jsx(ScrollAreaPrimitive.Corner, {})
1973
+ ]
1974
+ }));
1975
+ ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
1976
+ var ScrollBar = React$1.forwardRef(({ className, orientation = "vertical", ...props }, ref) => /* @__PURE__ */ jsx(ScrollAreaPrimitive.ScrollAreaScrollbar, {
1977
+ ref,
1978
+ orientation,
1979
+ className: cn("dy-flex dy-touch-none dy-select-none dy-transition-colors", orientation === "vertical" && "dy-h-full dy-w-2.5 dy-border-l dy-border-l-transparent dy-p-[1px]", orientation === "horizontal" && "dy-h-2.5 dy-flex-col dy-border-t dy-border-t-transparent dy-p-[1px]", className),
1980
+ ...props,
1981
+ children: /* @__PURE__ */ jsx(ScrollAreaPrimitive.ScrollAreaThumb, { className: "dy-relative dy-flex-1 dy-rounded-full dy-bg-border" })
1982
+ }));
1983
+ ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
2056
1984
  //#endregion
2057
- //#region src/components/ui/pagination.tsx
2058
- function Pagination({ page, totalPages, total, hasPrevPage, hasNextPage, onPageChange, className }) {
2059
- if (totalPages <= 1) return null;
2060
- return /* @__PURE__ */ jsxs("div", {
2061
- className: `dy-flex dy-flex-col dy-gap-3 dy-border-t dy-border-border/40 dy-px-4 dy-py-4 sm:dy-flex-row sm:dy-items-center sm:dy-justify-between ${className}`,
2062
- children: [/* @__PURE__ */ jsxs("p", {
2063
- className: "dy-text-xs dy-leading-5 dy-text-muted-foreground",
2064
- children: [
2065
- "Showing page ",
2066
- /* @__PURE__ */ jsx("strong", { children: page }),
2067
- " of ",
2068
- /* @__PURE__ */ jsx("strong", { children: totalPages }),
2069
- total != null && /* @__PURE__ */ jsxs(Fragment, { children: [
2070
- " - ",
2071
- total,
2072
- " total entries"
2073
- ] })
2074
- ]
2075
- }), /* @__PURE__ */ jsxs("div", {
2076
- className: "dy-flex dy-items-center dy-justify-between dy-gap-2 sm:dy-justify-end",
2077
- children: [/* @__PURE__ */ jsx(Button, {
2078
- variant: "outline",
2079
- size: "sm",
1985
+ //#region src/components/ui/csv-importer.tsx
1986
+ function CsvImporter({ slug, schema, onClose }) {
1987
+ const { client } = useDyrected();
1988
+ const queryClient = useQueryClient();
1989
+ const [step, setStep] = React$1.useState("upload");
1990
+ const [csvHeaders, setCsvHeaders] = React$1.useState([]);
1991
+ const [parsedRows, setParsedRows] = React$1.useState([]);
1992
+ const [mapping, setMapping] = React$1.useState({});
1993
+ const [validatedData, setValidatedData] = React$1.useState([]);
1994
+ const [totalRows, setTotalRows] = React$1.useState(0);
1995
+ const [processedCount, setProcessedCount] = React$1.useState(0);
1996
+ const [successCount, setSuccessCount] = React$1.useState(0);
1997
+ const [failedRows, setFailedRows] = React$1.useState([]);
1998
+ const importableFields = React$1.useMemo(() => {
1999
+ return schema.fields.filter((f) => f.name !== "id" && f.name !== "createdAt" && f.name !== "updatedAt" && f.type !== "row" && f.type !== "join" && !f.admin?.hidden);
2000
+ }, [schema]);
2001
+ const requiredFields = React$1.useMemo(() => {
2002
+ return importableFields.filter((f) => f.required);
2003
+ }, [importableFields]);
2004
+ const handleFileChange = (e) => {
2005
+ const selectedFile = e.target.files?.[0];
2006
+ if (!selectedFile) return;
2007
+ Papa.parse(selectedFile, {
2008
+ header: true,
2009
+ skipEmptyLines: "greedy",
2010
+ complete: (results) => {
2011
+ const headers = results.meta.fields || [];
2012
+ setCsvHeaders(headers);
2013
+ setParsedRows(results.data);
2014
+ const initialMapping = {};
2015
+ headers.forEach((header) => {
2016
+ const match = importableFields.find((f) => f.name.toLowerCase() === header.toLowerCase() || f.label?.toLowerCase() === header.toLowerCase());
2017
+ initialMapping[header] = match ? match.name : "__ignore__";
2018
+ });
2019
+ setMapping(initialMapping);
2020
+ setStep("map");
2021
+ },
2022
+ error: (error) => {
2023
+ console.error("PapaParse error:", error);
2024
+ }
2025
+ });
2026
+ };
2027
+ const handleMapChange = (header, fieldName) => {
2028
+ setMapping((prev) => ({
2029
+ ...prev,
2030
+ [header]: fieldName
2031
+ }));
2032
+ };
2033
+ const isMapValid = React$1.useMemo(() => {
2034
+ const mappedFields = Object.values(mapping);
2035
+ return requiredFields.every((f) => mappedFields.includes(f.name));
2036
+ }, [requiredFields, mapping]);
2037
+ const coerceAndValidate = (val, field) => {
2038
+ if (val === void 0 || val === null || String(val).trim() === "") {
2039
+ if (field.required) return {
2040
+ value: null,
2041
+ error: `${field.label || field.name} is required`
2042
+ };
2043
+ return { value: null };
2044
+ }
2045
+ const strVal = String(val).trim();
2046
+ switch (field.type) {
2047
+ case "number": {
2048
+ const num = Number(strVal.replace(/[$€£¥₹₦₩₪₺₽฿,]/g, ""));
2049
+ if (isNaN(num)) return {
2050
+ value: null,
2051
+ error: "Must be a valid number"
2052
+ };
2053
+ return { value: num };
2054
+ }
2055
+ case "boolean": {
2056
+ const lower = strVal.toLowerCase();
2057
+ if (lower === "true" || lower === "yes" || lower === "1") return { value: true };
2058
+ if (lower === "false" || lower === "no" || lower === "0") return { value: false };
2059
+ return {
2060
+ value: null,
2061
+ error: "Must be a boolean (true/false, yes/no, 1/0)"
2062
+ };
2063
+ }
2064
+ case "date":
2065
+ case "datetime": {
2066
+ const date = new Date(strVal);
2067
+ if (isNaN(date.getTime())) return {
2068
+ value: null,
2069
+ error: "Must be a valid date"
2070
+ };
2071
+ return { value: date.toISOString() };
2072
+ }
2073
+ case "image": try {
2074
+ new URL(strVal);
2075
+ return { value: strVal };
2076
+ } catch {
2077
+ return {
2078
+ value: null,
2079
+ error: "Must be a valid image URL"
2080
+ };
2081
+ }
2082
+ case "url": try {
2083
+ new URL(strVal);
2084
+ return { value: {
2085
+ type: "custom",
2086
+ url: strVal,
2087
+ label: ""
2088
+ } };
2089
+ } catch {
2090
+ return {
2091
+ value: null,
2092
+ error: "Must be a valid URL (e.g. https://example.com)"
2093
+ };
2094
+ }
2095
+ case "select":
2096
+ case "radio":
2097
+ if (field.options && Array.isArray(field.options) && field.options.length > 0) {
2098
+ const found = field.options.find((opt) => {
2099
+ const optVal = typeof opt === "string" ? opt : opt.value;
2100
+ return String(optVal).toLowerCase() === strVal.toLowerCase();
2101
+ });
2102
+ if (!found) return {
2103
+ value: null,
2104
+ error: `Must be one of: ${field.options.map((opt) => typeof opt === "string" ? opt : opt.label || opt.value).join(", ")}`
2105
+ };
2106
+ return { value: typeof found === "string" ? found : found.value };
2107
+ }
2108
+ return { value: strVal };
2109
+ default: return { value: strVal };
2110
+ }
2111
+ };
2112
+ const handleProceedToPreview = () => {
2113
+ setValidatedData(parsedRows.map((row, idx) => {
2114
+ const coercedRow = {};
2115
+ const errors = {};
2116
+ let isValid = true;
2117
+ Object.entries(mapping).forEach(([csvHeader, targetField]) => {
2118
+ if (!targetField || targetField === "__ignore__") return;
2119
+ const fieldSchema = importableFields.find((f) => f.name === targetField);
2120
+ if (!fieldSchema) return;
2121
+ const rawValue = row[csvHeader];
2122
+ const { value, error } = coerceAndValidate(rawValue, fieldSchema);
2123
+ if (error) {
2124
+ errors[targetField] = error;
2125
+ isValid = false;
2126
+ } else coercedRow[targetField] = value;
2127
+ });
2128
+ requiredFields.forEach((field) => {
2129
+ if (!Object.prototype.hasOwnProperty.call(coercedRow, field.name) || coercedRow[field.name] === null) {
2130
+ errors[field.name] = `${field.label || field.name} is required`;
2131
+ isValid = false;
2132
+ }
2133
+ });
2134
+ return {
2135
+ rowNumber: idx + 1,
2136
+ data: coercedRow,
2137
+ errors,
2138
+ isValid
2139
+ };
2140
+ }));
2141
+ setStep("preview");
2142
+ };
2143
+ const startImport = async () => {
2144
+ setStep("importing");
2145
+ setTotalRows(validatedData.length);
2146
+ setProcessedCount(0);
2147
+ setSuccessCount(0);
2148
+ setFailedRows([]);
2149
+ let success = 0;
2150
+ const failures = [];
2151
+ for (let i = 0; i < validatedData.length; i++) {
2152
+ const rowResult = validatedData[i];
2153
+ setProcessedCount(i + 1);
2154
+ if (!rowResult.isValid) {
2155
+ failures.push({
2156
+ row: rowResult.rowNumber,
2157
+ data: rowResult.data,
2158
+ error: `Validation error: ${Object.values(rowResult.errors).join(", ")}`
2159
+ });
2160
+ setFailedRows([...failures]);
2161
+ continue;
2162
+ }
2163
+ try {
2164
+ const data = { ...rowResult.data };
2165
+ for (const field of importableFields) {
2166
+ if (field.type !== "image") continue;
2167
+ const url = data[field.name];
2168
+ if (!url || typeof url !== "string") continue;
2169
+ const mediaCollection = field.relationTo || "media";
2170
+ const response = await fetch(url);
2171
+ if (!response.ok) throw new Error(`Failed to fetch image: ${url}`);
2172
+ const blob = await response.blob();
2173
+ const filename = url.split("/").pop()?.split("?")[0] || "image";
2174
+ const file = new File([blob], filename, { type: blob.type });
2175
+ const media = await client.collection(mediaCollection).upload(file);
2176
+ data[field.name] = media.id;
2177
+ }
2178
+ await client.collection(slug).create(data);
2179
+ success++;
2180
+ setSuccessCount(success);
2181
+ } catch (error) {
2182
+ failures.push({
2183
+ row: rowResult.rowNumber,
2184
+ data: rowResult.data,
2185
+ error: error.message || "Failed to create entry"
2186
+ });
2187
+ setFailedRows([...failures]);
2188
+ }
2189
+ }
2190
+ queryClient.invalidateQueries({ queryKey: ["collection", slug] });
2191
+ setStep("complete");
2192
+ };
2193
+ const downloadFailedCsv = () => {
2194
+ if (failedRows.length === 0) return;
2195
+ const headers = [...csvHeaders, "Import Error"];
2196
+ const rows = failedRows.map((f) => {
2197
+ return {
2198
+ ...parsedRows[f.row - 1],
2199
+ "Import Error": f.error
2200
+ };
2201
+ });
2202
+ const csvContent = Papa.unparse({
2203
+ fields: headers,
2204
+ data: rows
2205
+ });
2206
+ const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
2207
+ const url = URL.createObjectURL(blob);
2208
+ const link = document.createElement("a");
2209
+ link.setAttribute("href", url);
2210
+ link.setAttribute("download", `${slug}_failed_imports.csv`);
2211
+ document.body.appendChild(link);
2212
+ link.click();
2213
+ document.body.removeChild(link);
2214
+ };
2215
+ const previewRows = React$1.useMemo(() => {
2216
+ return validatedData.slice(0, 100);
2217
+ }, [validatedData]);
2218
+ const totalErrors = React$1.useMemo(() => {
2219
+ return validatedData.filter((r) => !r.isValid).length;
2220
+ }, [validatedData]);
2221
+ return /* @__PURE__ */ jsxs("div", {
2222
+ className: "dy-space-y-6",
2223
+ children: [
2224
+ step === "upload" && /* @__PURE__ */ jsx("div", {
2225
+ className: "dy-space-y-4",
2226
+ children: /* @__PURE__ */ jsx("div", {
2227
+ 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",
2228
+ children: /* @__PURE__ */ jsxs("label", {
2229
+ className: "dy-cursor-pointer dy-block dy-space-y-4 dy-px-6",
2230
+ children: [
2231
+ /* @__PURE__ */ jsx(Upload, { className: "dy-h-10 dy-w-10 dy-mx-auto dy-text-muted-foreground" }),
2232
+ /* @__PURE__ */ jsxs("div", {
2233
+ className: "dy-space-y-1",
2234
+ children: [/* @__PURE__ */ jsx("p", {
2235
+ className: "dy-text-sm dy-font-semibold",
2236
+ children: "Click to upload or drag & drop"
2237
+ }), /* @__PURE__ */ jsx("p", {
2238
+ className: "dy-text-xs dy-text-muted-foreground",
2239
+ children: "CSV files only"
2240
+ })]
2241
+ }),
2242
+ /* @__PURE__ */ jsx("input", {
2243
+ type: "file",
2244
+ accept: ".csv",
2245
+ className: "dy-hidden",
2246
+ onChange: handleFileChange
2247
+ })
2248
+ ]
2249
+ })
2250
+ })
2251
+ }),
2252
+ step === "map" && /* @__PURE__ */ jsxs("div", {
2253
+ className: "dy-space-y-4",
2254
+ children: [
2255
+ /* @__PURE__ */ jsx("div", {
2256
+ className: "dy-p-4 dy-bg-muted/40 dy-border dy-border-border dy-rounded-xl dy-text-sm dy-text-muted-foreground",
2257
+ children: "Map the columns in your CSV file to the corresponding fields of the collection. All required fields must be mapped to proceed."
2258
+ }),
2259
+ /* @__PURE__ */ jsx("div", {
2260
+ className: "dy-h-[300px] dy-overflow-y-auto dy-border dy-border-border dy-rounded-xl",
2261
+ children: /* @__PURE__ */ jsx("div", {
2262
+ className: "dy-divide-y dy-divide-border",
2263
+ children: csvHeaders.map((header) => {
2264
+ const mappedVal = mapping[header] ?? "__ignore__";
2265
+ return /* @__PURE__ */ jsxs("div", {
2266
+ className: "dy-flex dy-items-center dy-justify-between dy-p-3.5",
2267
+ children: [/* @__PURE__ */ jsxs("div", {
2268
+ className: "dy-space-y-1 dy-flex-1 dy-pr-4",
2269
+ children: [/* @__PURE__ */ jsx("p", {
2270
+ className: "dy-text-sm dy-font-semibold dy-text-foreground",
2271
+ children: header
2272
+ }), parsedRows[0] && /* @__PURE__ */ jsxs("p", {
2273
+ className: "dy-text-xs dy-text-muted-foreground dy-truncate",
2274
+ children: ["Sample: ", parsedRows[0][header]]
2275
+ })]
2276
+ }), /* @__PURE__ */ jsx("div", {
2277
+ className: "dy-w-[200px]",
2278
+ children: /* @__PURE__ */ jsxs(Select, {
2279
+ value: mappedVal,
2280
+ onValueChange: (val) => handleMapChange(header, val),
2281
+ children: [/* @__PURE__ */ jsx(SelectTrigger, {
2282
+ className: "dy-w-full",
2283
+ children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Ignore field" })
2284
+ }), /* @__PURE__ */ jsxs(SelectContent, { children: [/* @__PURE__ */ jsx(SelectItem, {
2285
+ value: "__ignore__",
2286
+ children: "Ignore field"
2287
+ }), importableFields.map((field) => /* @__PURE__ */ jsxs(SelectItem, {
2288
+ value: field.name,
2289
+ children: [field.required ? "* " : "", field.label || field.name]
2290
+ }, field.name))] })]
2291
+ })
2292
+ })]
2293
+ }, header);
2294
+ })
2295
+ })
2296
+ }),
2297
+ requiredFields.length > 0 && /* @__PURE__ */ jsxs("div", {
2298
+ className: "dy-space-y-2",
2299
+ children: [/* @__PURE__ */ jsx("p", {
2300
+ className: "dy-text-xs dy-font-semibold dy-text-muted-foreground",
2301
+ children: "Required Fields:"
2302
+ }), /* @__PURE__ */ jsx("div", {
2303
+ className: "dy-flex dy-flex-wrap dy-gap-2",
2304
+ children: requiredFields.map((f) => {
2305
+ const isMapped = Object.values(mapping).includes(f.name);
2306
+ return /* @__PURE__ */ jsxs(Badge, {
2307
+ variant: isMapped ? "secondary" : "destructive",
2308
+ className: "dy-rounded-md",
2309
+ children: [isMapped ? "✓ " : "✗ ", f.label || f.name]
2310
+ }, f.name);
2311
+ })
2312
+ })]
2313
+ }),
2314
+ /* @__PURE__ */ jsxs("div", {
2315
+ className: "dy-flex dy-justify-between dy-pt-4",
2316
+ children: [/* @__PURE__ */ jsx(Button, {
2317
+ variant: "outline",
2318
+ onClick: () => setStep("upload"),
2319
+ children: "Back"
2320
+ }), /* @__PURE__ */ jsxs(Button, {
2321
+ onClick: handleProceedToPreview,
2322
+ disabled: !isMapValid,
2323
+ children: ["Preview Validation ", /* @__PURE__ */ jsx(ArrowRight, { className: "dy-h-4 dy-w-4 dy-ml-2" })]
2324
+ })]
2325
+ })
2326
+ ]
2327
+ }),
2328
+ step === "preview" && /* @__PURE__ */ jsxs("div", {
2329
+ className: "dy-space-y-4",
2330
+ children: [
2331
+ /* @__PURE__ */ jsxs("div", {
2332
+ className: "dy-flex dy-items-center dy-justify-between",
2333
+ children: [/* @__PURE__ */ jsxs("div", {
2334
+ className: "dy-space-y-1",
2335
+ children: [/* @__PURE__ */ jsx("h3", {
2336
+ className: "dy-text-sm dy-font-semibold",
2337
+ children: "Validation Preview"
2338
+ }), /* @__PURE__ */ jsxs("p", {
2339
+ className: "dy-text-xs dy-text-muted-foreground",
2340
+ children: ["Showing the first 100 rows. ", totalErrors > 0 ? `${totalErrors} rows have errors and will be skipped.` : "All rows are valid."]
2341
+ })]
2342
+ }), totalErrors > 0 && /* @__PURE__ */ jsxs(Badge, {
2343
+ variant: "destructive",
2344
+ className: "dy-rounded-md dy-gap-1.5",
2345
+ children: [
2346
+ /* @__PURE__ */ jsx(AlertTriangle, { className: "dy-h-3.5 dy-w-3.5" }),
2347
+ " ",
2348
+ totalErrors,
2349
+ " Invalid"
2350
+ ]
2351
+ })]
2352
+ }),
2353
+ /* @__PURE__ */ jsx("div", {
2354
+ className: "dy-h-[300px] dy-overflow-auto dy-border dy-border-border dy-rounded-xl",
2355
+ children: /* @__PURE__ */ jsxs("table", {
2356
+ className: "dy-w-full dy-min-w-max dy-text-left dy-border-collapse",
2357
+ children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", {
2358
+ className: "dy-border-b dy-border-border dy-bg-muted/40 dy-text-xs dy-font-semibold dy-text-muted-foreground",
2359
+ children: [/* @__PURE__ */ jsx("th", {
2360
+ className: "dy-p-3 dy-w-16",
2361
+ children: "Row"
2362
+ }), importableFields.filter((f) => Object.values(mapping).includes(f.name)).map((f) => /* @__PURE__ */ jsx("th", {
2363
+ className: "dy-p-3",
2364
+ children: f.label || f.name
2365
+ }, f.name))]
2366
+ }) }), /* @__PURE__ */ jsx("tbody", {
2367
+ className: "dy-text-sm",
2368
+ children: previewRows.map((rowResult) => /* @__PURE__ */ jsxs("tr", {
2369
+ className: "dy-border-b dy-border-border hover:dy-bg-muted/20",
2370
+ children: [/* @__PURE__ */ jsx("td", {
2371
+ className: "dy-p-3 dy-font-medium dy-text-muted-foreground",
2372
+ children: rowResult.rowNumber
2373
+ }), importableFields.filter((f) => Object.values(mapping).includes(f.name)).map((f) => {
2374
+ const val = rowResult.data[f.name];
2375
+ const error = rowResult.errors[f.name];
2376
+ return /* @__PURE__ */ jsx("td", {
2377
+ className: "dy-p-3",
2378
+ children: error ? /* @__PURE__ */ jsxs("span", {
2379
+ className: "dy-flex dy-items-center dy-gap-1.5 dy-text-destructive dy-font-medium",
2380
+ children: [/* @__PURE__ */ jsx(AlertCircle, { className: "dy-h-3.5 dy-w-3.5" }), error]
2381
+ }) : String(val ?? "")
2382
+ }, f.name);
2383
+ })]
2384
+ }, rowResult.rowNumber))
2385
+ })]
2386
+ })
2387
+ }),
2388
+ /* @__PURE__ */ jsxs("div", {
2389
+ className: "dy-flex dy-justify-between dy-pt-4",
2390
+ children: [/* @__PURE__ */ jsx(Button, {
2391
+ variant: "outline",
2392
+ onClick: () => setStep("map"),
2393
+ children: "Back"
2394
+ }), /* @__PURE__ */ jsxs(Button, {
2395
+ onClick: startImport,
2396
+ variant: "default",
2397
+ className: "dy-gap-2",
2398
+ children: [
2399
+ /* @__PURE__ */ jsx(Play, { className: "dy-h-4 dy-w-4" }),
2400
+ " Start Import (",
2401
+ validatedData.length - totalErrors,
2402
+ " Rows)"
2403
+ ]
2404
+ })]
2405
+ })
2406
+ ]
2407
+ }),
2408
+ step === "importing" && /* @__PURE__ */ jsxs("div", {
2409
+ className: "dy-space-y-6 dy-py-6",
2410
+ children: [/* @__PURE__ */ jsxs("div", {
2411
+ className: "dy-space-y-2",
2412
+ children: [/* @__PURE__ */ jsxs("div", {
2413
+ className: "dy-flex dy-items-center dy-justify-between dy-text-sm dy-font-semibold",
2414
+ children: [/* @__PURE__ */ jsx("span", { children: "Importing rows..." }), /* @__PURE__ */ jsxs("span", { children: [
2415
+ processedCount,
2416
+ " / ",
2417
+ totalRows
2418
+ ] })]
2419
+ }), /* @__PURE__ */ jsx(Progress, {
2420
+ value: processedCount / totalRows * 100,
2421
+ className: "dy-h-2"
2422
+ })]
2423
+ }), /* @__PURE__ */ jsxs("div", {
2424
+ className: "dy-grid dy-grid-cols-2 dy-gap-4",
2425
+ children: [/* @__PURE__ */ jsxs("div", {
2426
+ className: "dy-p-4 dy-bg-muted/30 dy-border dy-border-border dy-rounded-xl dy-text-center",
2427
+ children: [/* @__PURE__ */ jsx("p", {
2428
+ className: "dy-text-2xl dy-font-bold dy-text-primary",
2429
+ children: successCount
2430
+ }), /* @__PURE__ */ jsx("p", {
2431
+ className: "dy-text-xs dy-text-muted-foreground",
2432
+ children: "Successful"
2433
+ })]
2434
+ }), /* @__PURE__ */ jsxs("div", {
2435
+ className: "dy-p-4 dy-bg-muted/30 dy-border dy-border-border dy-rounded-xl dy-text-center",
2436
+ children: [/* @__PURE__ */ jsx("p", {
2437
+ className: "dy-text-2xl dy-font-bold dy-text-destructive",
2438
+ children: failedRows.length
2439
+ }), /* @__PURE__ */ jsx("p", {
2440
+ className: "dy-text-xs dy-text-muted-foreground",
2441
+ children: "Failed/Skipped"
2442
+ })]
2443
+ })]
2444
+ })]
2445
+ }),
2446
+ step === "complete" && /* @__PURE__ */ jsxs("div", {
2447
+ className: "dy-space-y-6",
2448
+ children: [
2449
+ /* @__PURE__ */ jsxs("div", {
2450
+ className: "dy-flex dy-flex-col dy-items-center dy-text-center dy-space-y-3 dy-py-6",
2451
+ children: [
2452
+ /* @__PURE__ */ jsx(CheckCircle2, { className: "dy-h-12 dy-w-12 dy-text-green-500" }),
2453
+ /* @__PURE__ */ jsx("h3", {
2454
+ className: "dy-text-lg dy-font-semibold",
2455
+ children: "Import Process Completed"
2456
+ }),
2457
+ /* @__PURE__ */ jsxs("p", {
2458
+ className: "dy-text-sm dy-text-muted-foreground dy-max-w-md",
2459
+ children: [
2460
+ "Successfully imported ",
2461
+ successCount,
2462
+ " rows. ",
2463
+ failedRows.length,
2464
+ " rows failed or were skipped due to validation/server errors."
2465
+ ]
2466
+ })
2467
+ ]
2468
+ }),
2469
+ failedRows.length > 0 && /* @__PURE__ */ jsxs("div", {
2470
+ className: "dy-space-y-3",
2471
+ children: [/* @__PURE__ */ jsxs("div", {
2472
+ className: "dy-flex dy-items-center dy-justify-between",
2473
+ children: [/* @__PURE__ */ jsxs("p", {
2474
+ className: "dy-text-xs dy-font-bold dy-text-destructive uppercase tracking-wide",
2475
+ children: [
2476
+ "Failure Logs (",
2477
+ failedRows.length,
2478
+ " rows)"
2479
+ ]
2480
+ }), /* @__PURE__ */ jsxs(Button, {
2481
+ size: "sm",
2482
+ variant: "outline",
2483
+ className: "dy-h-8 dy-text-xs dy-gap-1.5",
2484
+ onClick: downloadFailedCsv,
2485
+ children: [/* @__PURE__ */ jsx(Download, { className: "dy-h-3.5 dy-w-3.5" }), " Download Errors CSV"]
2486
+ })]
2487
+ }), /* @__PURE__ */ jsx(ScrollArea, {
2488
+ className: "dy-h-[180px] dy-border dy-border-border dy-rounded-xl",
2489
+ children: /* @__PURE__ */ jsx("div", {
2490
+ className: "dy-divide-y dy-divide-border",
2491
+ children: failedRows.map((f, idx) => /* @__PURE__ */ jsxs("div", {
2492
+ className: "dy-flex dy-items-start dy-gap-3 dy-p-3 dy-text-xs",
2493
+ children: [/* @__PURE__ */ jsxs(Badge, {
2494
+ variant: "destructive",
2495
+ className: "dy-rounded dy-shrink-0",
2496
+ children: ["Row ", f.row]
2497
+ }), /* @__PURE__ */ jsx("p", {
2498
+ className: "dy-text-muted-foreground font-medium dy-pt-0.5",
2499
+ children: f.error
2500
+ })]
2501
+ }, idx))
2502
+ })
2503
+ })]
2504
+ }),
2505
+ /* @__PURE__ */ jsxs("div", {
2506
+ className: "dy-flex dy-justify-end dy-gap-2 dy-pt-4",
2507
+ children: [/* @__PURE__ */ jsxs(Button, {
2508
+ variant: "outline",
2509
+ className: "dy-gap-2",
2510
+ onClick: () => {
2511
+ setCsvHeaders([]);
2512
+ setParsedRows([]);
2513
+ setMapping({});
2514
+ setValidatedData([]);
2515
+ setFailedRows([]);
2516
+ setStep("upload");
2517
+ },
2518
+ children: [/* @__PURE__ */ jsx(RotateCcw, { className: "dy-h-4 dy-w-4" }), " Import Another File"]
2519
+ }), /* @__PURE__ */ jsx(Button, {
2520
+ onClick: onClose,
2521
+ children: "Close Importer"
2522
+ })]
2523
+ })
2524
+ ]
2525
+ })
2526
+ ]
2527
+ });
2528
+ }
2529
+ //#endregion
2530
+ //#region src/components/ui/csv-importer-dialog.tsx
2531
+ function CsvImporterDialog({ open, onOpenChange, slug, schema }) {
2532
+ const title = schema.labels?.singular || schema.slug || slug;
2533
+ return /* @__PURE__ */ jsx(Dialog, {
2534
+ open,
2535
+ onOpenChange,
2536
+ children: /* @__PURE__ */ jsxs(DialogContent, {
2537
+ className: "sm:dy-max-w-2xl",
2538
+ children: [/* @__PURE__ */ jsxs(DialogHeader, { children: [/* @__PURE__ */ jsx(DialogTitle, { children: "Import CSV Data" }), /* @__PURE__ */ jsxs(DialogDescription, { children: [
2539
+ "Import records into the ",
2540
+ title,
2541
+ " collection."
2542
+ ] })] }), /* @__PURE__ */ jsx(CsvImporter, {
2543
+ slug,
2544
+ schema,
2545
+ onClose: () => onOpenChange(false)
2546
+ })]
2547
+ })
2548
+ });
2549
+ }
2550
+ //#endregion
2551
+ //#region src/components/ui/render-cell.tsx
2552
+ function RenderCell({ value, field, client, schemas }) {
2553
+ if (value === null || value === void 0) return /* @__PURE__ */ jsx("span", {
2554
+ className: "dy-text-muted-foreground",
2555
+ children: "-"
2556
+ });
2557
+ if (field.type === "boolean" || typeof value === "boolean") return /* @__PURE__ */ jsx(Badge, {
2558
+ variant: value ? "default" : "secondary",
2559
+ children: value ? "Yes" : "No"
2560
+ });
2561
+ if (field.type === "date") {
2562
+ const date = new Date(value);
2563
+ return /* @__PURE__ */ jsxs("div", {
2564
+ className: "dy-flex dy-items-center dy-gap-1.5 dy-text-muted-foreground",
2565
+ children: [/* @__PURE__ */ jsx(Calendar, { className: "dy-h-3 dy-w-3" }), /* @__PURE__ */ jsx("span", {
2566
+ className: "dy-text-xs",
2567
+ children: date.toLocaleDateString()
2568
+ })]
2569
+ });
2570
+ }
2571
+ const relationTo = field.relationTo || field.collection;
2572
+ if (field.type === "image" || field.type === "relationship" && isUploadCollection(relationTo, schemas)) {
2573
+ if (!value) return /* @__PURE__ */ jsx("span", {
2574
+ className: "dy-text-muted-foreground",
2575
+ children: "-"
2576
+ });
2577
+ const url = getMediaUrl(value, client?.getBaseUrl() || "");
2578
+ if (!url) return /* @__PURE__ */ jsx("span", {
2579
+ className: "dy-text-muted-foreground",
2580
+ children: "-"
2581
+ });
2582
+ return /* @__PURE__ */ jsx("div", {
2583
+ className: "dy-h-8 dy-w-8 dy-rounded dy-overflow-hidden dy-border dy-bg-muted dy-shadow-sm",
2584
+ children: /* @__PURE__ */ jsx("img", {
2585
+ src: url,
2586
+ className: "dy-h-full dy-w-full dy-object-cover",
2587
+ alt: ""
2588
+ })
2589
+ });
2590
+ }
2591
+ if (field.type === "relationship" && typeof value === "object") {
2592
+ const relTo = field.relationTo || field.collection;
2593
+ const displayValue = value[(schemas?.collections?.find((c) => c?.slug === relTo))?.admin?.useAsTitle || "title"] || value.name || value.id || "Unknown";
2594
+ return /* @__PURE__ */ jsx("div", {
2595
+ className: "dy-flex dy-items-center dy-gap-2",
2596
+ children: /* @__PURE__ */ jsx(Badge, {
2597
+ variant: "outline",
2598
+ className: "dy-font-normal dy-border-primary/20 dy-bg-primary/5 dy-text-primary",
2599
+ children: String(displayValue)
2600
+ })
2601
+ });
2602
+ }
2603
+ if (Array.isArray(value)) return /* @__PURE__ */ jsxs("div", {
2604
+ className: "dy-flex dy-flex-wrap dy-gap-1",
2605
+ children: [value.slice(0, 2).map((item, i) => /* @__PURE__ */ jsx(Badge, {
2606
+ variant: "outline",
2607
+ className: "dy-text-[10px] dy-px-1.5 dy-h-5",
2608
+ children: typeof item === "object" ? item.title || item.name || item.id : String(item)
2609
+ }, i)), value.length > 2 && /* @__PURE__ */ jsxs("span", {
2610
+ className: "dy-text-[10px] dy-text-muted-foreground",
2611
+ children: [
2612
+ "+",
2613
+ value.length - 2,
2614
+ " more"
2615
+ ]
2616
+ })]
2617
+ });
2618
+ if (typeof value === "object" && !Array.isArray(value)) {
2619
+ const entries = Object.entries(value).filter(([_, v]) => typeof v !== "object" && v !== null && v !== void 0).slice(0, 3);
2620
+ if (entries.length > 0) return /* @__PURE__ */ jsxs("span", {
2621
+ className: "dy-text-[11px] dy-text-muted-foreground dy-font-medium dy-leading-tight",
2622
+ children: [entries.map(([k, v]) => `${k}: ${String(v)}`).join(", "), Object.keys(value).length > 3 ? "..." : ""]
2623
+ });
2624
+ return /* @__PURE__ */ jsxs("span", {
2625
+ className: "dy-text-[11px] dy-text-muted-foreground dy-font-mono dy-bg-muted/30 dy-px-1 dy-rounded",
2626
+ children: [JSON.stringify(value).slice(0, 30), JSON.stringify(value).length > 30 ? "..." : ""]
2627
+ });
2628
+ }
2629
+ return /* @__PURE__ */ jsx("span", {
2630
+ className: "dy-text-sm dy-font-medium",
2631
+ children: typeof value === "object" ? JSON.stringify(value).slice(0, 50) : String(value)
2632
+ });
2633
+ }
2634
+ function isUploadCollection(slug, schemas) {
2635
+ if (!slug) return false;
2636
+ return !!(schemas?.collections?.find((c) => c?.slug === slug))?.upload;
2637
+ }
2638
+ //#endregion
2639
+ //#region src/components/ui/page-header.tsx
2640
+ function PageHeader({ title, description, icon: Icon, children }) {
2641
+ return /* @__PURE__ */ jsxs("div", {
2642
+ className: "dy-mb-6 dy-flex dy-flex-col dy-gap-4 sm:dy-mb-8 sm:dy-flex-row sm:dy-items-end sm:dy-justify-between",
2643
+ children: [/* @__PURE__ */ jsxs("div", {
2644
+ className: "dy-min-w-0",
2645
+ children: [/* @__PURE__ */ jsxs("div", {
2646
+ className: "dy-mb-1 dy-flex dy-items-start dy-gap-3",
2647
+ children: [Icon && /* @__PURE__ */ jsx(Icon, { className: "dy-mt-1 dy-h-4 dy-w-4 dy-flex-shrink-0 dy-text-primary/60" }), /* @__PURE__ */ jsx("h1", {
2648
+ className: "dy-min-w-0 dy-break-words dy-text-xl dy-font-bold dy-tracking-tight dy-text-foreground sm:dy-text-2xl",
2649
+ children: title
2650
+ })]
2651
+ }), description && /* @__PURE__ */ jsx("p", {
2652
+ className: "dy-max-w-2xl dy-text-[11px] dy-font-medium dy-leading-5 dy-text-muted-foreground/60",
2653
+ children: description
2654
+ })]
2655
+ }), /* @__PURE__ */ jsx("div", {
2656
+ className: "dy-flex dy-w-full dy-flex-col dy-gap-2 sm:dy-w-auto sm:dy-flex-row sm:dy-items-center sm:dy-gap-3",
2657
+ children
2658
+ })]
2659
+ });
2660
+ }
2661
+ //#endregion
2662
+ //#region src/components/ui/pagination.tsx
2663
+ function Pagination({ page, totalPages, total, hasPrevPage, hasNextPage, onPageChange, className }) {
2664
+ if (totalPages <= 1) return null;
2665
+ return /* @__PURE__ */ jsxs("div", {
2666
+ className: `dy-flex dy-flex-col dy-gap-3 dy-border-t dy-border-border/40 dy-px-4 dy-py-4 sm:dy-flex-row sm:dy-items-center sm:dy-justify-between ${className}`,
2667
+ children: [/* @__PURE__ */ jsxs("p", {
2668
+ className: "dy-text-xs dy-leading-5 dy-text-muted-foreground",
2669
+ children: [
2670
+ "Showing page ",
2671
+ /* @__PURE__ */ jsx("strong", { children: page }),
2672
+ " of ",
2673
+ /* @__PURE__ */ jsx("strong", { children: totalPages }),
2674
+ total != null && /* @__PURE__ */ jsxs(Fragment, { children: [
2675
+ " - ",
2676
+ total,
2677
+ " total entries"
2678
+ ] })
2679
+ ]
2680
+ }), /* @__PURE__ */ jsxs("div", {
2681
+ className: "dy-flex dy-items-center dy-justify-between dy-gap-2 sm:dy-justify-end",
2682
+ children: [/* @__PURE__ */ jsx(Button, {
2683
+ variant: "outline",
2684
+ size: "sm",
2080
2685
  className: "dy-h-9 dy-w-9 dy-p-0",
2081
2686
  disabled: !hasPrevPage,
2082
2687
  onClick: () => onPageChange(Math.max(1, page - 1)),
@@ -2158,14 +2763,14 @@ function MediaGrid({ items, baseUrl, onDelete, slug }) {
2158
2763
  }
2159
2764
  //#endregion
2160
2765
  //#region src/components/ui/sheet.tsx
2161
- var Sheet = SheetPrimitive.Root;
2162
- var SheetPortal = SheetPrimitive.Portal;
2163
- var SheetOverlay = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(SheetPrimitive.Overlay, {
2766
+ var Sheet = DialogPrimitive.Root;
2767
+ var SheetPortal = DialogPrimitive.Portal;
2768
+ var SheetOverlay = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Overlay, {
2164
2769
  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),
2165
2770
  ...props,
2166
2771
  ref
2167
2772
  }));
2168
- SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
2773
+ SheetOverlay.displayName = DialogPrimitive.Overlay.displayName;
2169
2774
  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", {
2170
2775
  variants: { side: {
2171
2776
  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",
@@ -2177,11 +2782,11 @@ var sheetVariants = cva("dy-fixed dy-z-50 dy-gap-4 dy-bg-background dy-p-6 dy-sh
2177
2782
  });
2178
2783
  var SheetContent = React$1.forwardRef(({ side = "right", className, children, ...props }, ref) => /* @__PURE__ */ jsx(SheetPortal, { children: /* @__PURE__ */ jsxs("div", {
2179
2784
  className: "dy-admin-ui",
2180
- children: [/* @__PURE__ */ jsx(SheetOverlay, {}), /* @__PURE__ */ jsxs(SheetPrimitive.Content, {
2785
+ children: [/* @__PURE__ */ jsx(SheetOverlay, {}), /* @__PURE__ */ jsxs(DialogPrimitive.Content, {
2181
2786
  ref,
2182
2787
  className: cn(sheetVariants({ side }), className),
2183
2788
  ...props,
2184
- children: [children, /* @__PURE__ */ jsxs(SheetPrimitive.Close, {
2789
+ children: [children, /* @__PURE__ */ jsxs(DialogPrimitive.Close, {
2185
2790
  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",
2186
2791
  children: [/* @__PURE__ */ jsx(X, { className: "dy-h-4 dy-w-4" }), /* @__PURE__ */ jsx("span", {
2187
2792
  className: "dy-sr-only",
@@ -2190,7 +2795,7 @@ var SheetContent = React$1.forwardRef(({ side = "right", className, children, ..
2190
2795
  })]
2191
2796
  })]
2192
2797
  }) }));
2193
- SheetContent.displayName = SheetPrimitive.Content.displayName;
2798
+ SheetContent.displayName = DialogPrimitive.Content.displayName;
2194
2799
  var SheetHeader = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
2195
2800
  className: cn("dy-flex dy-flex-col dy-space-y-2 dy-text-center sm:dy-text-left", className),
2196
2801
  ...props
@@ -2201,18 +2806,18 @@ var SheetFooter = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
2201
2806
  ...props
2202
2807
  });
2203
2808
  SheetFooter.displayName = "SheetFooter";
2204
- var SheetTitle = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(SheetPrimitive.Title, {
2809
+ var SheetTitle = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Title, {
2205
2810
  ref,
2206
2811
  className: cn("dy-text-lg dy-font-semibold dy-text-foreground", className),
2207
2812
  ...props
2208
2813
  }));
2209
- SheetTitle.displayName = SheetPrimitive.Title.displayName;
2210
- var SheetDescription = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(SheetPrimitive.Description, {
2814
+ SheetTitle.displayName = DialogPrimitive.Title.displayName;
2815
+ var SheetDescription = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Description, {
2211
2816
  ref,
2212
2817
  className: cn("dy-text-sm dy-text-muted-foreground", className),
2213
2818
  ...props
2214
2819
  }));
2215
- SheetDescription.displayName = SheetPrimitive.Description.displayName;
2820
+ SheetDescription.displayName = DialogPrimitive.Description.displayName;
2216
2821
  //#endregion
2217
2822
  //#region src/components/ui/toggle.tsx
2218
2823
  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", {
@@ -2243,55 +2848,6 @@ var Toggle = React$1.forwardRef(({ className, variant, size, ...props }, ref) =>
2243
2848
  }));
2244
2849
  Toggle.displayName = TogglePrimitive.Root.displayName;
2245
2850
  //#endregion
2246
- //#region src/components/ui/dialog.tsx
2247
- var Dialog = SheetPrimitive.Root;
2248
- var DialogTrigger = SheetPrimitive.Trigger;
2249
- var DialogPortal = SheetPrimitive.Portal;
2250
- var DialogOverlay = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(SheetPrimitive.Overlay, {
2251
- ref,
2252
- 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),
2253
- ...props
2254
- }));
2255
- DialogOverlay.displayName = SheetPrimitive.Overlay.displayName;
2256
- var DialogContent = React$1.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsx(DialogPortal, { children: /* @__PURE__ */ jsxs("div", {
2257
- className: "dy-admin-ui",
2258
- children: [/* @__PURE__ */ jsx(DialogOverlay, {}), /* @__PURE__ */ jsxs(SheetPrimitive.Content, {
2259
- ref,
2260
- 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),
2261
- ...props,
2262
- children: [children, /* @__PURE__ */ jsxs(SheetPrimitive.Close, {
2263
- 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",
2264
- children: [/* @__PURE__ */ jsx(X, { className: "dy-h-4 dy-w-4" }), /* @__PURE__ */ jsx("span", {
2265
- className: "dy-sr-only",
2266
- children: "Close"
2267
- })]
2268
- })]
2269
- })]
2270
- }) }));
2271
- DialogContent.displayName = SheetPrimitive.Content.displayName;
2272
- var DialogHeader = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
2273
- className: cn("dy-flex dy-flex-col dy-space-y-1.5 dy-text-center sm:dy-text-left", className),
2274
- ...props
2275
- });
2276
- DialogHeader.displayName = "DialogHeader";
2277
- var DialogFooter = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
2278
- className: cn("dy-flex dy-flex-col-reverse sm:dy-flex-row sm:dy-justify-end sm:dy-space-x-2", className),
2279
- ...props
2280
- });
2281
- DialogFooter.displayName = "DialogFooter";
2282
- var DialogTitle = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(SheetPrimitive.Title, {
2283
- ref,
2284
- className: cn("dy-text-lg dy-font-semibold dy-leading-none dy-tracking-tight", className),
2285
- ...props
2286
- }));
2287
- DialogTitle.displayName = SheetPrimitive.Title.displayName;
2288
- var DialogDescription = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(SheetPrimitive.Description, {
2289
- ref,
2290
- className: cn("dy-text-sm dy-text-muted-foreground", className),
2291
- ...props
2292
- }));
2293
- DialogDescription.displayName = SheetPrimitive.Description.displayName;
2294
- //#endregion
2295
2851
  //#region src/components/ui/tabs.tsx
2296
2852
  var Tabs = TabsPrimitive.Root;
2297
2853
  var TabsList = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(TabsPrimitive.List, {
@@ -2313,30 +2869,6 @@ var TabsContent = React$1.forwardRef(({ className, ...props }, ref) => /* @__PUR
2313
2869
  }));
2314
2870
  TabsContent.displayName = TabsPrimitive.Content.displayName;
2315
2871
  //#endregion
2316
- //#region src/components/ui/scroll-area.tsx
2317
- var ScrollArea = React$1.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(ScrollAreaPrimitive.Root, {
2318
- ref,
2319
- className: cn("dy-relative dy-overflow-hidden", className),
2320
- ...props,
2321
- children: [
2322
- /* @__PURE__ */ jsx(ScrollAreaPrimitive.Viewport, {
2323
- className: "dy-h-full dy-w-full dy-rounded-[inherit]",
2324
- children
2325
- }),
2326
- /* @__PURE__ */ jsx(ScrollBar, {}),
2327
- /* @__PURE__ */ jsx(ScrollAreaPrimitive.Corner, {})
2328
- ]
2329
- }));
2330
- ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
2331
- var ScrollBar = React$1.forwardRef(({ className, orientation = "vertical", ...props }, ref) => /* @__PURE__ */ jsx(ScrollAreaPrimitive.ScrollAreaScrollbar, {
2332
- ref,
2333
- orientation,
2334
- className: cn("dy-flex dy-touch-none dy-select-none dy-transition-colors", orientation === "vertical" && "dy-h-full dy-w-2.5 dy-border-l dy-border-l-transparent dy-p-[1px]", orientation === "horizontal" && "dy-h-2.5 dy-flex-col dy-border-t dy-border-t-transparent dy-p-[1px]", className),
2335
- ...props,
2336
- children: /* @__PURE__ */ jsx(ScrollAreaPrimitive.ScrollAreaThumb, { className: "dy-relative dy-flex-1 dy-rounded-full dy-bg-border" })
2337
- }));
2338
- ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
2339
- //#endregion
2340
2872
  //#region src/components/media/media-library-dialog.tsx
2341
2873
  function MediaLibraryDialog({ collection, isOpen, onOpenChange, selectedValues, onSelect, multiple, onConfirm }) {
2342
2874
  const { client, schemas } = useDyrected();
@@ -7815,8 +8347,8 @@ function FormEngine({ collection, fields, defaultValues = {}, onSubmit, onChange
7815
8347
  const tabbedFields = visibleFields.filter((f) => !!f.admin?.tab);
7816
8348
  let fieldsContent;
7817
8349
  const renderFieldColumn = (field) => /* @__PURE__ */ jsx("div", {
7818
- className: "dy-min-w-0 dy-px-3",
7819
- style: { width: field.admin?.width || "100%" },
8350
+ className: "dy-min-w-0 dy-px-3 dy-w-full sm:dy-w-[var(--field-w)]",
8351
+ style: { "--field-w": field.admin?.width || "100%" },
7820
8352
  children: /* @__PURE__ */ jsx(FormFieldRenderer, {
7821
8353
  schema: field,
7822
8354
  basePath: "",
@@ -8222,7 +8754,7 @@ function RelationshipPicker({ value, onChange, label, relationTo, multiple, disa
8222
8754
  }
8223
8755
  //#endregion
8224
8756
  //#region src/components/ui/spreadsheet-editor.tsx
8225
- function SelectCellDisplay({ field, value, collection, siblingValues }) {
8757
+ function useFieldOptions(field, collection, siblingValues) {
8226
8758
  const { client } = useDyrected();
8227
8759
  const isDynamic = !!(field.options && typeof field.options === "object" && "_dynamic" in field.options);
8228
8760
  const { data: dynamicOptions } = useQuery({
@@ -8234,7 +8766,7 @@ function SelectCellDisplay({ field, value, collection, siblingValues }) {
8234
8766
  ],
8235
8767
  queryFn: async () => {
8236
8768
  const q = new URLSearchParams();
8237
- if (siblingValues) Object.entries(siblingValues).forEach(([k, v]) => {
8769
+ Object.entries(siblingValues).forEach(([k, v]) => {
8238
8770
  if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") q.append(k, String(v));
8239
8771
  });
8240
8772
  const url = `${client?.getBaseUrl() || ""}/api/dyrected/options/${collection}/${field.name}?${q.toString()}`;
@@ -8250,7 +8782,7 @@ function SelectCellDisplay({ field, value, collection, siblingValues }) {
8250
8782
  },
8251
8783
  enabled: !!client && isDynamic && !!collection && !!field.name
8252
8784
  });
8253
- const matched = React$1.useMemo(() => {
8785
+ return React$1.useMemo(() => {
8254
8786
  const rawOptions = isDynamic ? dynamicOptions || [] : field.options;
8255
8787
  return (Array.isArray(rawOptions) ? rawOptions : []).map((opt) => {
8256
8788
  if (typeof opt === "object" && opt !== null) return {
@@ -8266,54 +8798,14 @@ function SelectCellDisplay({ field, value, collection, siblingValues }) {
8266
8798
  isDynamic,
8267
8799
  dynamicOptions,
8268
8800
  field.options
8269
- ]).find((opt) => opt.value === String(value ?? ""));
8801
+ ]);
8802
+ }
8803
+ function SelectCellDisplay({ field, value, collection, siblingValues }) {
8804
+ const matched = useFieldOptions(field, collection, siblingValues).find((opt) => opt.value === String(value ?? ""));
8270
8805
  return /* @__PURE__ */ jsx("span", { children: matched ? matched.label : value !== null && value !== void 0 ? String(value) : "" });
8271
8806
  }
8272
8807
  function SelectPopoverEditor({ field, value, collection, siblingValues, onCommit }) {
8273
- const { client } = useDyrected();
8274
- const isDynamic = !!(field.options && typeof field.options === "object" && "_dynamic" in field.options);
8275
- const { data: dynamicOptions } = useQuery({
8276
- queryKey: [
8277
- "options",
8278
- collection,
8279
- field.name,
8280
- siblingValues
8281
- ],
8282
- queryFn: async () => {
8283
- const q = new URLSearchParams();
8284
- if (siblingValues) Object.entries(siblingValues).forEach(([k, v]) => {
8285
- if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") q.append(k, String(v));
8286
- });
8287
- const url = `${client?.getBaseUrl() || ""}/api/dyrected/options/${collection}/${field.name}?${q.toString()}`;
8288
- const authHeaders = {};
8289
- const token = typeof window !== "undefined" ? localStorage.getItem("dyrected_token") : null;
8290
- if (token) authHeaders["Authorization"] = `Bearer ${token}`;
8291
- const res = await fetch(url, { headers: {
8292
- "Content-Type": "application/json",
8293
- ...authHeaders
8294
- } });
8295
- if (!res.ok) throw new Error("Failed to fetch options");
8296
- return res.json();
8297
- },
8298
- enabled: !!client && isDynamic && !!collection && !!field.name
8299
- });
8300
- const options = React$1.useMemo(() => {
8301
- const rawOptions = isDynamic ? dynamicOptions || [] : field.options;
8302
- return (Array.isArray(rawOptions) ? rawOptions : []).map((opt) => {
8303
- if (typeof opt === "object" && opt !== null) return {
8304
- label: opt.label,
8305
- value: String(opt.value ?? "")
8306
- };
8307
- return {
8308
- label: String(opt),
8309
- value: String(opt)
8310
- };
8311
- });
8312
- }, [
8313
- isDynamic,
8314
- dynamicOptions,
8315
- field.options
8316
- ]);
8808
+ const options = useFieldOptions(field, collection, siblingValues);
8317
8809
  return /* @__PURE__ */ jsxs("div", {
8318
8810
  className: "dy-max-h-60 dy-overflow-y-auto dy-flex dy-flex-col dy-divide-y dy-divide-border dy-bg-popover dy-rounded-md",
8319
8811
  children: [options.map((opt) => /* @__PURE__ */ jsx("button", {
@@ -8327,6 +8819,163 @@ function SelectPopoverEditor({ field, value, collection, siblingValues, onCommit
8327
8819
  })]
8328
8820
  });
8329
8821
  }
8822
+ function MultiSelectCellDisplay({ field, value, collection, siblingValues }) {
8823
+ const options = useFieldOptions(field, collection, siblingValues);
8824
+ const labels = (Array.isArray(value) ? value.map(String) : []).map((v) => options.find((o) => o.value === v)?.label ?? v);
8825
+ return /* @__PURE__ */ jsx("span", { children: labels.length > 0 ? labels.join(", ") : "" });
8826
+ }
8827
+ function MultiSelectPopoverEditor({ field, value, collection, siblingValues, onToggle }) {
8828
+ const options = useFieldOptions(field, collection, siblingValues);
8829
+ const selected = Array.isArray(value) ? value.map(String) : [];
8830
+ return /* @__PURE__ */ jsxs("div", {
8831
+ className: "dy-max-h-60 dy-overflow-y-auto dy-flex dy-flex-col dy-divide-y dy-divide-border dy-bg-popover dy-rounded-md",
8832
+ children: [options.map((opt) => {
8833
+ const checked = selected.includes(opt.value);
8834
+ return /* @__PURE__ */ jsxs("button", {
8835
+ type: "button",
8836
+ className: cn("dy-w-full dy-text-left dy-px-3 dy-py-2 dy-text-xs hover:dy-bg-accent hover:dy-text-accent-foreground dy-transition-colors dy-flex dy-items-center dy-gap-2", checked && "dy-bg-accent/50"),
8837
+ onClick: () => onToggle(checked ? selected.filter((v) => v !== opt.value) : [...selected, opt.value]),
8838
+ children: [/* @__PURE__ */ jsx("span", {
8839
+ className: cn("dy-w-3.5 dy-h-3.5 dy-rounded-sm dy-border dy-border-border dy-flex-shrink-0 dy-flex dy-items-center dy-justify-center", checked && "dy-bg-primary dy-border-primary"),
8840
+ children: checked && /* @__PURE__ */ jsx("span", {
8841
+ className: "dy-text-primary-foreground dy-text-[10px] dy-leading-none",
8842
+ children: "✓"
8843
+ })
8844
+ }), opt.label]
8845
+ }, opt.value);
8846
+ }), options.length === 0 && /* @__PURE__ */ jsx("div", {
8847
+ className: "dy-text-xs dy-text-muted-foreground dy-p-3 dy-text-center",
8848
+ children: "No options available"
8849
+ })]
8850
+ });
8851
+ }
8852
+ function SubFieldRow({ subField, value, onChange, collection }) {
8853
+ const { user, schemas } = useDyrected();
8854
+ const handleChange = React$1.useCallback((eventOrValue) => {
8855
+ if (eventOrValue != null && typeof eventOrValue === "object" && "target" in eventOrValue) {
8856
+ const e = eventOrValue;
8857
+ if (e.target.type === "checkbox") onChange(e.target.checked);
8858
+ else if (e.target.value === "") onChange(null);
8859
+ else if (subField.type === "number") onChange(Number(e.target.value));
8860
+ else onChange(e.target.value);
8861
+ } else onChange(eventOrValue);
8862
+ }, [onChange, subField.type]);
8863
+ const syntheticField = React$1.useMemo(() => ({
8864
+ value: value ?? (subField.type === "boolean" ? false : ""),
8865
+ onChange: handleChange,
8866
+ name: subField.name ?? "",
8867
+ ref: { current: null }
8868
+ }), [
8869
+ value,
8870
+ handleChange,
8871
+ subField.name,
8872
+ subField.type
8873
+ ]);
8874
+ return /* @__PURE__ */ jsxs("div", {
8875
+ className: "dy-flex dy-flex-col dy-gap-0.5",
8876
+ children: [/* @__PURE__ */ jsx("label", {
8877
+ className: "dy-text-[10px] dy-text-muted-foreground",
8878
+ children: subField.label || subField.name
8879
+ }), /* @__PURE__ */ jsx(FieldRenderer, {
8880
+ schema: subField,
8881
+ field: syntheticField,
8882
+ collection,
8883
+ context: {
8884
+ user: user ?? null,
8885
+ schemas,
8886
+ siblingData: {}
8887
+ }
8888
+ })]
8889
+ });
8890
+ }
8891
+ function ObjectPopoverEditor({ field, value, onChange, collection }) {
8892
+ const subFields = field.fields || [];
8893
+ const obj = typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
8894
+ return /* @__PURE__ */ jsxs("div", {
8895
+ className: "dy-flex dy-flex-col dy-gap-3 dy-max-h-[400px] dy-overflow-y-auto",
8896
+ children: [
8897
+ /* @__PURE__ */ jsx("div", {
8898
+ className: "dy-text-xs dy-font-semibold dy-text-muted-foreground",
8899
+ children: field.label || field.name
8900
+ }),
8901
+ subFields.filter((sf) => ![
8902
+ "object",
8903
+ "array",
8904
+ "blocks",
8905
+ "join",
8906
+ "row",
8907
+ "richText"
8908
+ ].includes(sf.type)).map((sf) => /* @__PURE__ */ jsx(SubFieldRow, {
8909
+ subField: sf,
8910
+ value: obj[sf.name],
8911
+ onChange: (val) => onChange({
8912
+ ...obj,
8913
+ [sf.name]: val
8914
+ }),
8915
+ collection
8916
+ }, sf.name)),
8917
+ subFields.length === 0 && /* @__PURE__ */ jsx("div", {
8918
+ className: "dy-text-xs dy-text-muted-foreground",
8919
+ children: "No fields defined"
8920
+ })
8921
+ ]
8922
+ });
8923
+ }
8924
+ function ArrayPopoverEditor({ field, value, onChange, collection }) {
8925
+ const subFields = field.fields || [];
8926
+ const items = Array.isArray(value) ? value : [];
8927
+ const updateItem = (idx, key, val) => onChange(items.map((item, i) => i === idx ? {
8928
+ ...item,
8929
+ [key]: val
8930
+ } : item));
8931
+ return /* @__PURE__ */ jsxs("div", {
8932
+ className: "dy-flex dy-flex-col dy-gap-3 dy-max-h-[400px] dy-overflow-y-auto",
8933
+ children: [
8934
+ /* @__PURE__ */ jsxs("div", {
8935
+ className: "dy-text-xs dy-font-semibold dy-text-muted-foreground",
8936
+ children: [
8937
+ field.label || field.name,
8938
+ " (",
8939
+ items.length,
8940
+ " items)"
8941
+ ]
8942
+ }),
8943
+ items.map((item, idx) => /* @__PURE__ */ jsxs("div", {
8944
+ className: "dy-border dy-rounded-md dy-p-2 dy-flex dy-flex-col dy-gap-2",
8945
+ children: [/* @__PURE__ */ jsxs("div", {
8946
+ className: "dy-flex dy-items-center dy-justify-between",
8947
+ children: [/* @__PURE__ */ jsxs("span", {
8948
+ className: "dy-text-[10px] dy-font-medium dy-text-muted-foreground",
8949
+ children: ["Item ", idx + 1]
8950
+ }), /* @__PURE__ */ jsx("button", {
8951
+ type: "button",
8952
+ className: "dy-text-[10px] dy-text-destructive hover:dy-underline",
8953
+ onClick: () => onChange(items.filter((_, i) => i !== idx)),
8954
+ children: "Remove"
8955
+ })]
8956
+ }), subFields.filter((sf) => ![
8957
+ "object",
8958
+ "array",
8959
+ "blocks",
8960
+ "join",
8961
+ "row",
8962
+ "richText"
8963
+ ].includes(sf.type)).map((sf) => /* @__PURE__ */ jsx(SubFieldRow, {
8964
+ subField: sf,
8965
+ value: item[sf.name],
8966
+ onChange: (val) => updateItem(idx, sf.name, val),
8967
+ collection
8968
+ }, sf.name))]
8969
+ }, idx)),
8970
+ /* @__PURE__ */ jsx("button", {
8971
+ type: "button",
8972
+ className: "dy-w-full dy-border dy-border-dashed dy-rounded dy-py-1.5 dy-text-xs dy-text-muted-foreground hover:dy-bg-muted/30 dy-transition-colors",
8973
+ onClick: () => onChange([...items, {}]),
8974
+ children: "+ Add item"
8975
+ })
8976
+ ]
8977
+ });
8978
+ }
8330
8979
  function SpreadsheetEditor({ slug, schema, data, onSave, isSaving }) {
8331
8980
  const { client } = useDyrected();
8332
8981
  const [drafts, setDrafts] = React$1.useState({});
@@ -8417,7 +9066,7 @@ function SpreadsheetEditor({ slug, schema, data, onSave, isSaving }) {
8417
9066
  };
8418
9067
  const columns = React$1.useMemo(() => {
8419
9068
  const cols = [];
8420
- schema.fields.filter((f) => f.name !== "password" && !f.admin?.hidden && f.type !== "row" && f.type !== "join").forEach((field) => {
9069
+ schema.fields.filter((f) => f.name !== "password" && !f.admin?.hidden && f.type !== "row" && f.type !== "join" && f.type !== "blocks").forEach((field) => {
8421
9070
  const isReadOnly = !!field.admin?.readOnly;
8422
9071
  if (field.type === "text" || field.type === "email" || field.type === "url") {
8423
9072
  cols.push({
@@ -8435,6 +9084,34 @@ function SpreadsheetEditor({ slug, schema, data, onSave, isSaving }) {
8435
9084
  });
8436
9085
  return;
8437
9086
  }
9087
+ if (field.type === "number") {
9088
+ cols.push({
9089
+ ...keyColumn(field.name, {
9090
+ component: ({ rowData: value, setRowData: onChange, focus }) => {
9091
+ return /* @__PURE__ */ jsx("input", {
9092
+ type: "number",
9093
+ className: "dy-w-full dy-h-full dy-bg-transparent dy-outline-none dy-px-2 dy-text-xs dy-text-foreground [appearance:textfield] [&::-webkit-outer-spin-button]:dy-appearance-none [&::-webkit-inner-spin-button]:dy-appearance-none",
9094
+ value: value != null ? String(value) : "",
9095
+ onChange: (e) => {
9096
+ const raw = e.target.value;
9097
+ onChange(raw === "" ? null : Number(raw));
9098
+ },
9099
+ disabled: isReadOnly,
9100
+ autoFocus: focus
9101
+ });
9102
+ },
9103
+ deleteValue: () => null,
9104
+ copyValue: ({ rowData: value }) => String(value ?? ""),
9105
+ pasteValue: ({ value }) => {
9106
+ const n = Number(value);
9107
+ return isNaN(n) ? null : n;
9108
+ }
9109
+ }),
9110
+ title: field.label || field.name,
9111
+ disabled: isReadOnly
9112
+ });
9113
+ return;
9114
+ }
8438
9115
  if (field.type === "date" || field.type === "datetime" || field.type === "time") {
8439
9116
  const inputType = field.type === "date" ? "date" : field.type === "datetime" ? "datetime-local" : "time";
8440
9117
  cols.push({
@@ -8478,10 +9155,18 @@ function SpreadsheetEditor({ slug, schema, data, onSave, isSaving }) {
8478
9155
  if (!rowData) return null;
8479
9156
  const value = rowData[field.name];
8480
9157
  const isSelectOrRadio = field.type === "select" || field.type === "radio";
9158
+ const isMultiSelect = field.type === "multiSelect";
9159
+ const isArray = field.type === "array";
9160
+ const isObject = field.type === "object";
8481
9161
  let display = "";
8482
- if (!isSelectOrRadio && value !== null && value !== void 0) if (typeof value === "object") display = value.title || value.name || value.filename || JSON.stringify(value);
9162
+ if (!isSelectOrRadio && !isMultiSelect && !isArray && !isObject && value !== null && value !== void 0) if (typeof value === "object") display = value.title || value.name || value.filename || JSON.stringify(value);
8483
9163
  else display = String(value);
8484
9164
  if (field.type === "richText" && display) display = display.replace(/<[^>]*>/g, "");
9165
+ if (isArray) display = Array.isArray(value) ? `${value.length} item${value.length !== 1 ? "s" : ""}` : "0 items";
9166
+ if (isObject && typeof value === "object" && value !== null) {
9167
+ const keys = Object.keys(value).filter((k) => value[k] != null);
9168
+ display = keys.length > 0 ? keys.slice(0, 2).map((k) => `${k}: ${String(value[k]).slice(0, 15)}`).join(", ") : "{ empty }";
9169
+ }
8485
9170
  return /* @__PURE__ */ jsxs("div", {
8486
9171
  tabIndex: -1,
8487
9172
  className: "dy-w-full dy-h-full dy-px-2 dy-py-1 dy-truncate dy-cursor-pointer hover:dy-bg-muted/30 dy-flex dy-items-center dy-gap-2 dy-outline-none",
@@ -8508,6 +9193,14 @@ function SpreadsheetEditor({ slug, schema, data, onSave, isSaving }) {
8508
9193
  collection: slug,
8509
9194
  siblingValues: rowData
8510
9195
  })
9196
+ }) : isMultiSelect ? /* @__PURE__ */ jsx("span", {
9197
+ className: "dy-text-xs dy-text-foreground",
9198
+ children: /* @__PURE__ */ jsx(MultiSelectCellDisplay, {
9199
+ field,
9200
+ value,
9201
+ collection: slug,
9202
+ siblingValues: rowData
9203
+ })
8511
9204
  }) : /* @__PURE__ */ jsx("span", {
8512
9205
  className: "dy-text-xs dy-text-muted-foreground",
8513
9206
  children: display || `Click to edit`
@@ -8627,10 +9320,15 @@ function SpreadsheetEditor({ slug, schema, data, onSave, isSaving }) {
8627
9320
  if (!open) handlePopoverClose();
8628
9321
  },
8629
9322
  children: activePopover && activePopover.field.type !== "richText" && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(PopoverAnchor, { virtualRef: { current: activePopover.element } }), /* @__PURE__ */ jsxs(PopoverContent, {
8630
- className: "dy-z-[100] dy-p-3 dy-w-80 dy-bg-popover dy-border dy-rounded-md dy-shadow-md",
9323
+ className: cn("dy-z-[100] dy-p-3 dy-bg-popover dy-border dy-rounded-md dy-shadow-md", activePopover.field.type === "object" || activePopover.field.type === "array" ? "dy-w-96" : "dy-w-80"),
8631
9324
  onInteractOutside: () => {
8632
9325
  const { rowIdx, field } = activePopover;
8633
- if (field.type !== "textarea" && field.type !== "json") return;
9326
+ if (![
9327
+ "textarea",
9328
+ "json",
9329
+ "object",
9330
+ "array"
9331
+ ].includes(field.type)) return;
8634
9332
  const row = gridData[rowIdx];
8635
9333
  if (row) setDrafts((prev) => ({
8636
9334
  ...prev,
@@ -8709,12 +9407,34 @@ function SpreadsheetEditor({ slug, schema, data, onSave, isSaving }) {
8709
9407
  (activePopover.field.type === "select" || activePopover.field.type === "radio") && /* @__PURE__ */ jsx(SelectPopoverEditor, {
8710
9408
  field: activePopover.field,
8711
9409
  value: localDraft,
8712
- collection: slug,
8713
- siblingValues: gridData[activePopover.rowIdx] || {},
8714
- onCommit: (val) => {
8715
- setLocalDraft(val);
8716
- commitValue(val);
8717
- }
9410
+ collection: slug,
9411
+ siblingValues: gridData[activePopover.rowIdx] || {},
9412
+ onCommit: (val) => {
9413
+ setLocalDraft(val);
9414
+ commitValue(val);
9415
+ }
9416
+ }),
9417
+ activePopover.field.type === "multiSelect" && /* @__PURE__ */ jsx(MultiSelectPopoverEditor, {
9418
+ field: activePopover.field,
9419
+ value: Array.isArray(localDraft) ? localDraft : [],
9420
+ collection: slug,
9421
+ siblingValues: gridData[activePopover.rowIdx] || {},
9422
+ onToggle: (val) => {
9423
+ setLocalDraft(val);
9424
+ commitValue(val);
9425
+ }
9426
+ }),
9427
+ activePopover.field.type === "object" && /* @__PURE__ */ jsx(ObjectPopoverEditor, {
9428
+ field: activePopover.field,
9429
+ value: localDraft,
9430
+ onChange: (val) => setLocalDraft(val),
9431
+ collection: slug
9432
+ }),
9433
+ activePopover.field.type === "array" && /* @__PURE__ */ jsx(ArrayPopoverEditor, {
9434
+ field: activePopover.field,
9435
+ value: localDraft,
9436
+ onChange: (val) => setLocalDraft(val),
9437
+ collection: slug
8718
9438
  })
8719
9439
  ]
8720
9440
  })] })
@@ -8724,13 +9444,41 @@ function SpreadsheetEditor({ slug, schema, data, onSave, isSaving }) {
8724
9444
  }
8725
9445
  //#endregion
8726
9446
  //#region src/pages/collections/list-page.tsx
9447
+ function SortableColumnItem({ id, label, visible, onToggleVisible }) {
9448
+ const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id });
9449
+ return /* @__PURE__ */ jsxs("div", {
9450
+ ref: setNodeRef,
9451
+ style: {
9452
+ transform: CSS.Transform.toString(transform),
9453
+ transition,
9454
+ zIndex: isDragging ? 10 : 1
9455
+ },
9456
+ className: cn("dy-flex dy-items-center dy-gap-2.5 dy-p-2 dy-bg-background dy-border dy-border-border/60 dy-rounded-lg dy-shadow-sm dy-transition-all", isDragging && "dy-opacity-50 dy-border-primary"),
9457
+ children: [
9458
+ /* @__PURE__ */ jsx("div", {
9459
+ ...attributes,
9460
+ ...listeners,
9461
+ className: "dy-cursor-grab active:dy-cursor-grabbing dy-text-muted-foreground/60 hover:dy-text-foreground dy-p-0.5",
9462
+ children: /* @__PURE__ */ jsx(GripVertical, { className: "dy-h-3.5 dy-w-3.5" })
9463
+ }),
9464
+ /* @__PURE__ */ jsx(Checkbox, {
9465
+ checked: visible,
9466
+ onCheckedChange: (val) => onToggleVisible(!!val)
9467
+ }),
9468
+ /* @__PURE__ */ jsx("div", {
9469
+ className: "dy-flex-1 dy-text-xs dy-font-medium dy-text-foreground",
9470
+ children: label
9471
+ })
9472
+ ]
9473
+ });
9474
+ }
8727
9475
  function CollectionListPage({ slug }) {
8728
9476
  const { client, components, user } = useDyrected();
8729
9477
  const queryClient = useQueryClient();
8730
9478
  const [page, setPage] = React$1.useState(1);
8731
9479
  const [rowSelection, setRowSelection] = React$1.useState({});
9480
+ const [isImportOpen, setIsImportOpen] = React$1.useState(false);
8732
9481
  const [searchParams, setSearchParams] = useSearchParams();
8733
- const [viewMode, setViewMode] = React$1.useState("list");
8734
9482
  const whereParam = searchParams.get("where");
8735
9483
  const rules = React$1.useMemo(() => {
8736
9484
  if (!whereParam) return [];
@@ -8760,6 +9508,145 @@ function CollectionListPage({ slug }) {
8760
9508
  enabled: !!client
8761
9509
  });
8762
9510
  const schema = schemas?.collections.find((c) => c.slug === slug);
9511
+ const allAvailableColumns = React$1.useMemo(() => {
9512
+ if (!schema) return [];
9513
+ return [
9514
+ ...schema.fields.filter((f) => f.name !== "password" && !f.admin?.hidden && f.type !== "row" && f.type !== "join").map((field) => field.name),
9515
+ "id",
9516
+ "createdAt",
9517
+ "updatedAt"
9518
+ ].filter((name, index, self) => self.indexOf(name) === index);
9519
+ }, [schema]);
9520
+ const defaultListColumns = React$1.useMemo(() => {
9521
+ if (!schema) return [];
9522
+ const allDisplayFields = schema.fields.filter((f) => f.name !== "password" && !f.admin?.hidden && f.type !== "row" && f.type !== "join");
9523
+ const configuredColumns = Array.isArray(schema.admin?.defaultColumns) ? schema.admin.defaultColumns : [];
9524
+ return configuredColumns.length > 0 ? configuredColumns : allDisplayFields.slice(0, 3).map((field) => field.name);
9525
+ }, [schema]);
9526
+ const defaultListPreference = React$1.useMemo(() => {
9527
+ const visibleSet = new Set(defaultListColumns);
9528
+ return {
9529
+ viewMode: "list",
9530
+ columns: allAvailableColumns.map((name) => ({
9531
+ name,
9532
+ visible: visibleSet.has(name)
9533
+ }))
9534
+ };
9535
+ }, [allAvailableColumns, defaultListColumns]);
9536
+ const prefKey = `layout:collections:${slug}:list`;
9537
+ const { data: rawPreference } = useQuery({
9538
+ queryKey: ["preferences", prefKey],
9539
+ queryFn: async () => {
9540
+ if (!client) return null;
9541
+ return (await client.getPreference(prefKey)).value;
9542
+ },
9543
+ enabled: !!client,
9544
+ staleTime: 5e3,
9545
+ refetchOnWindowFocus: true
9546
+ });
9547
+ const reconciledPreference = React$1.useMemo(() => {
9548
+ if (!rawPreference) return defaultListPreference;
9549
+ let parsedViewMode = "list";
9550
+ let parsedColumns = [];
9551
+ if (Array.isArray(rawPreference)) {
9552
+ parsedViewMode = "list";
9553
+ parsedColumns = rawPreference.map((item) => {
9554
+ if (typeof item === "string") return {
9555
+ name: item,
9556
+ visible: true
9557
+ };
9558
+ if (item && typeof item === "object" && "name" in item) {
9559
+ const obj = item;
9560
+ return {
9561
+ name: obj.name,
9562
+ visible: obj.visible !== false
9563
+ };
9564
+ }
9565
+ return null;
9566
+ }).filter((x) => x !== null);
9567
+ } else if (rawPreference && typeof rawPreference === "object") {
9568
+ const rawPrefObj = rawPreference;
9569
+ parsedViewMode = rawPrefObj.viewMode === "spreadsheet" ? "spreadsheet" : "list";
9570
+ if (Array.isArray(rawPrefObj.columns)) parsedColumns = rawPrefObj.columns.map((item) => {
9571
+ if (typeof item === "string") return {
9572
+ name: item,
9573
+ visible: true
9574
+ };
9575
+ if (item && typeof item === "object" && "name" in item) {
9576
+ const obj = item;
9577
+ return {
9578
+ name: obj.name,
9579
+ visible: obj.visible !== false
9580
+ };
9581
+ }
9582
+ return null;
9583
+ }).filter((x) => x !== null);
9584
+ }
9585
+ const validColumns = parsedColumns.filter((col) => allAvailableColumns.includes(col.name));
9586
+ const validNames = validColumns.map((col) => col.name);
9587
+ const missingColumns = allAvailableColumns.filter((name) => !validNames.includes(name)).map((name) => ({
9588
+ name,
9589
+ visible: false
9590
+ }));
9591
+ return {
9592
+ viewMode: parsedViewMode,
9593
+ columns: [...validColumns, ...missingColumns]
9594
+ };
9595
+ }, [
9596
+ rawPreference,
9597
+ defaultListPreference,
9598
+ allAvailableColumns
9599
+ ]);
9600
+ const [localPreference, setLocalPreference] = React$1.useState(reconciledPreference);
9601
+ const syncPreference = React$1.useState(() => {
9602
+ return (next) => {
9603
+ setLocalPreference((prev) => {
9604
+ const isViewModeSame = prev.viewMode === next.viewMode;
9605
+ const isColsSame = prev.columns.length === next.columns.length && prev.columns.every((col, i) => col.name === next.columns[i].name && col.visible === next.columns[i].visible);
9606
+ return isViewModeSame && isColsSame ? prev : next;
9607
+ });
9608
+ };
9609
+ })[0];
9610
+ React$1.useEffect(() => {
9611
+ syncPreference(reconciledPreference);
9612
+ }, [reconciledPreference, syncPreference]);
9613
+ const savePreferenceMutation = useMutation({
9614
+ mutationFn: async ({ scope, value }) => {
9615
+ if (!client) throw new Error("Client not available");
9616
+ await client.setPreference(prefKey, value, { scope });
9617
+ },
9618
+ onSuccess: () => {
9619
+ queryClient.invalidateQueries({ queryKey: ["preferences", prefKey] });
9620
+ }
9621
+ });
9622
+ const resetPreferenceMutation = useMutation({
9623
+ mutationFn: async ({ scope }) => {
9624
+ if (!client) throw new Error("Client not available");
9625
+ await client.deletePreference(prefKey, { scope });
9626
+ },
9627
+ onSuccess: () => {
9628
+ queryClient.invalidateQueries({ queryKey: ["preferences", prefKey] });
9629
+ }
9630
+ });
9631
+ const saveColumns = (scope) => savePreferenceMutation.mutateAsync({
9632
+ scope,
9633
+ value: localPreference
9634
+ });
9635
+ const resetColumns = () => resetPreferenceMutation.mutateAsync({ scope: "personal" });
9636
+ const dndSensors = useSensors(useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }));
9637
+ const handleDndDragEnd = (event) => {
9638
+ const { active, over } = event;
9639
+ if (!over || active.id === over.id) return;
9640
+ setLocalPreference((prev) => {
9641
+ const oldIndex = prev.columns.findIndex((c) => c.name === active.id);
9642
+ const newIndex = prev.columns.findIndex((c) => c.name === over.id);
9643
+ if (oldIndex === -1 || newIndex === -1) return prev;
9644
+ return {
9645
+ ...prev,
9646
+ columns: arrayMove(prev.columns, oldIndex, newIndex)
9647
+ };
9648
+ });
9649
+ };
8763
9650
  const { data: response, isLoading } = useQuery({
8764
9651
  queryKey: [
8765
9652
  "collection",
@@ -8908,18 +9795,9 @@ function CollectionListPage({ slug }) {
8908
9795
  if (!schema) return [];
8909
9796
  const allDisplayFields = schema.fields.filter((f) => f.name !== "password" && !f.admin?.hidden && f.type !== "row" && f.type !== "join");
8910
9797
  const fieldByName = new Map(allDisplayFields.map((field) => [field.name, field]));
8911
- const configuredColumns = Array.isArray(schema.admin?.defaultColumns) ? schema.admin.defaultColumns : [];
9798
+ const configuredColumns = localPreference.columns.filter((col) => col.visible).map((col) => col.name);
8912
9799
  const visibleColumnNames = configuredColumns.length > 0 ? configuredColumns : allDisplayFields.slice(0, 3).map((field) => field.name);
8913
- const systemColumnNames = [
8914
- "id",
8915
- "createdAt",
8916
- "updatedAt"
8917
- ];
8918
- const allColumnNames = [
8919
- ...visibleColumnNames,
8920
- ...allDisplayFields.map((field) => field.name),
8921
- ...systemColumnNames
8922
- ].filter((name, index, names) => names.indexOf(name) === index);
9800
+ const allColumnNames = visibleColumnNames;
8923
9801
  const firstVisibleFieldName = visibleColumnNames.find((name) => fieldByName.has(name));
8924
9802
  const titleFieldName = visibleColumnNames.includes(schema.admin?.useAsTitle || "") ? schema.admin?.useAsTitle : firstVisibleFieldName;
8925
9803
  const deleteAccess = schema.access?.delete;
@@ -9041,29 +9919,9 @@ function CollectionListPage({ slug }) {
9041
9919
  user,
9042
9920
  handleDelete,
9043
9921
  slug,
9044
- schemas
9922
+ schemas,
9923
+ localPreference.columns
9045
9924
  ]);
9046
- const initialColumnVisibility = React$1.useMemo(() => {
9047
- if (!schema) return {};
9048
- const allDisplayFields = schema.fields.filter((f) => f.name !== "password" && !f.admin?.hidden && f.type !== "row" && f.type !== "join");
9049
- const validColumnNames = new Set([
9050
- ...allDisplayFields.map((field) => field.name),
9051
- "id",
9052
- "createdAt",
9053
- "updatedAt"
9054
- ]);
9055
- const configuredColumns = Array.isArray(schema.admin?.defaultColumns) ? schema.admin.defaultColumns : [];
9056
- const visibleColumnNames = new Set((configuredColumns.length > 0 ? configuredColumns : allDisplayFields.slice(0, 3).map((field) => field.name)).filter((name) => validColumnNames.has(name)));
9057
- const visibility = {};
9058
- validColumnNames.forEach((name) => {
9059
- visibility[name] = visibleColumnNames.has(name);
9060
- });
9061
- return visibility;
9062
- }, [schema]);
9063
- const columnPreferenceKey = React$1.useMemo(() => {
9064
- if (!schema) return slug;
9065
- return `${slug}:${(Array.isArray(schema.admin?.defaultColumns) ? schema.admin.defaultColumns : []).join(",") || "default"}`;
9066
- }, [schema, slug]);
9067
9925
  if (!schema) return /* @__PURE__ */ jsxs("div", { children: ["Collection not found: ", slug] });
9068
9926
  const readAccess = schema.access?.read;
9069
9927
  let canRead = true;
@@ -9200,63 +10058,139 @@ function CollectionListPage({ slug }) {
9200
10058
  icon: Database,
9201
10059
  children: /* @__PURE__ */ jsxs("div", {
9202
10060
  className: "dy-flex dy-items-center dy-gap-2 dy-w-full sm:dy-w-auto",
9203
- children: [/* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
10061
+ children: [/* @__PURE__ */ jsxs(Popover, { children: [/* @__PURE__ */ jsx(PopoverTrigger, {
9204
10062
  asChild: true,
9205
10063
  children: /* @__PURE__ */ jsxs(Button, {
9206
10064
  variant: "outline",
9207
10065
  size: "sm",
9208
10066
  className: "dy-h-8 dy-px-3 dy-gap-1.5 dy-text-xs",
9209
- children: [
9210
- viewMode === "list" ? /* @__PURE__ */ jsx(List, { className: "dy-h-3.5 dy-w-3.5 dy-text-muted-foreground" }) : /* @__PURE__ */ jsx(Grid, { className: "dy-h-3.5 dy-w-3.5 dy-text-muted-foreground" }),
9211
- /* @__PURE__ */ jsx("span", { children: viewMode === "list" ? "List View" : "Spreadsheet" }),
9212
- /* @__PURE__ */ jsx(ChevronDown, { className: "dy-h-3 dy-w-3 dy-text-muted-foreground/60" })
9213
- ]
10067
+ children: [/* @__PURE__ */ jsx(Settings2, { className: "dy-h-3.5 dy-w-3.5" }), /* @__PURE__ */ jsx("span", { children: "View Settings" })]
9214
10068
  })
9215
- }), /* @__PURE__ */ jsxs(DropdownMenuContent, {
10069
+ }), /* @__PURE__ */ jsxs(PopoverContent, {
9216
10070
  align: "end",
9217
- className: "dy-w-56",
10071
+ className: "dy-w-80 dy-p-4 dy-space-y-4",
9218
10072
  children: [
9219
- /* @__PURE__ */ jsx(DropdownMenuLabel, {
9220
- className: "dy-text-xs dy-font-normal dy-text-muted-foreground/50 dy-px-3 dy-py-1.5",
9221
- children: "Layout"
9222
- }),
9223
- /* @__PURE__ */ jsx(DropdownMenuSeparator, { className: "dy-opacity-50" }),
9224
- /* @__PURE__ */ jsxs(DropdownMenuItem, {
9225
- onClick: () => setViewMode("list"),
9226
- className: "dy-flex dy-items-center dy-justify-between dy-py-2 dy-cursor-pointer",
9227
- children: [/* @__PURE__ */ jsxs("div", {
9228
- className: "dy-flex dy-items-center dy-gap-2.5",
9229
- children: [/* @__PURE__ */ jsx(List, { className: "dy-h-4 dy-w-4 dy-text-muted-foreground" }), /* @__PURE__ */ jsxs("div", {
9230
- className: "dy-flex dy-flex-col",
9231
- children: [/* @__PURE__ */ jsx("span", {
9232
- className: "dy-text-xs dy-font-medium",
9233
- children: "List View"
9234
- }), /* @__PURE__ */ jsx("span", {
9235
- className: "dy-text-[10px] dy-text-muted-foreground",
9236
- children: "Read-only table listing"
9237
- })]
10073
+ /* @__PURE__ */ jsxs("div", {
10074
+ className: "dy-space-y-1.5",
10075
+ children: [/* @__PURE__ */ jsx("h4", {
10076
+ className: "dy-text-xs dy-font-semibold dy-text-foreground dy-text-left",
10077
+ children: "Layout Mode"
10078
+ }), /* @__PURE__ */ jsxs("div", {
10079
+ className: "dy-grid dy-grid-cols-2 dy-gap-2",
10080
+ children: [/* @__PURE__ */ jsx(Button, {
10081
+ size: "sm",
10082
+ variant: localPreference.viewMode === "list" ? "default" : "outline",
10083
+ className: "dy-h-8 dy-text-xs",
10084
+ onClick: () => setLocalPreference((prev) => ({
10085
+ ...prev,
10086
+ viewMode: "list"
10087
+ })),
10088
+ children: "List View"
10089
+ }), /* @__PURE__ */ jsx(Button, {
10090
+ size: "sm",
10091
+ variant: localPreference.viewMode === "spreadsheet" ? "default" : "outline",
10092
+ className: "dy-h-8 dy-text-xs",
10093
+ onClick: () => setLocalPreference((prev) => ({
10094
+ ...prev,
10095
+ viewMode: "spreadsheet"
10096
+ })),
10097
+ children: "Spreadsheet"
9238
10098
  })]
9239
- }), viewMode === "list" && /* @__PURE__ */ jsx(Check, { className: "dy-h-3.5 dy-w-3.5 dy-text-primary" })]
10099
+ })]
9240
10100
  }),
9241
- /* @__PURE__ */ jsxs(DropdownMenuItem, {
9242
- onClick: () => setViewMode("spreadsheet"),
9243
- className: "dy-flex dy-items-center dy-justify-between dy-py-2 dy-cursor-pointer",
10101
+ /* @__PURE__ */ jsxs("div", {
10102
+ className: "dy-space-y-1.5",
10103
+ children: [
10104
+ /* @__PURE__ */ jsx("h4", {
10105
+ className: "dy-text-xs dy-font-semibold dy-text-foreground dy-text-left",
10106
+ children: "Columns"
10107
+ }),
10108
+ /* @__PURE__ */ jsx("p", {
10109
+ className: "dy-text-[10px] dy-text-muted-foreground dy-text-left",
10110
+ children: "Reorder and toggle column visibility."
10111
+ }),
10112
+ /* @__PURE__ */ jsx("div", {
10113
+ className: "dy-max-h-[200px] dy-overflow-y-auto dy-space-y-1 dy-pr-1 dy-outline-none",
10114
+ children: /* @__PURE__ */ jsx(DndContext, {
10115
+ sensors: dndSensors,
10116
+ collisionDetection: closestCenter,
10117
+ onDragEnd: handleDndDragEnd,
10118
+ children: /* @__PURE__ */ jsx(SortableContext, {
10119
+ items: localPreference.columns.map((c) => c.name),
10120
+ strategy: verticalListSortingStrategy,
10121
+ children: /* @__PURE__ */ jsx("div", {
10122
+ className: "dy-space-y-1",
10123
+ children: localPreference.columns.map((col) => {
10124
+ let label = col.name;
10125
+ if (col.name === "id") label = "ID";
10126
+ else if (col.name === "createdAt") label = "Created";
10127
+ else if (col.name === "updatedAt") label = "Last Updated";
10128
+ else {
10129
+ const field = schema.fields.find((f) => f.name === col.name);
10130
+ if (field) label = field.label || field.name;
10131
+ }
10132
+ return /* @__PURE__ */ jsx(SortableColumnItem, {
10133
+ id: col.name,
10134
+ label,
10135
+ visible: col.visible,
10136
+ onToggleVisible: (visible) => {
10137
+ setLocalPreference((prev) => ({
10138
+ ...prev,
10139
+ columns: prev.columns.map((c) => c.name === col.name ? {
10140
+ ...c,
10141
+ visible
10142
+ } : c)
10143
+ }));
10144
+ }
10145
+ }, col.name);
10146
+ })
10147
+ })
10148
+ })
10149
+ })
10150
+ })
10151
+ ]
10152
+ }),
10153
+ /* @__PURE__ */ jsxs("div", {
10154
+ className: "dy-pt-2 dy-border-t dy-border-border/60 dy-flex dy-flex-col dy-gap-2",
9244
10155
  children: [/* @__PURE__ */ jsxs("div", {
9245
- className: "dy-flex dy-items-center dy-gap-2.5",
9246
- children: [/* @__PURE__ */ jsx(Grid, { className: "dy-h-4 dy-w-4 dy-text-muted-foreground" }), /* @__PURE__ */ jsxs("div", {
9247
- className: "dy-flex dy-flex-col",
9248
- children: [/* @__PURE__ */ jsx("span", {
9249
- className: "dy-text-xs dy-font-medium",
9250
- children: "Spreadsheet"
9251
- }), /* @__PURE__ */ jsx("span", {
9252
- className: "dy-text-[10px] dy-text-muted-foreground",
9253
- children: "Airtable-style editing"
9254
- })]
10156
+ className: "dy-grid dy-grid-cols-2 dy-gap-2",
10157
+ children: [/* @__PURE__ */ jsx(Button, {
10158
+ size: "sm",
10159
+ className: "dy-h-7 dy-text-[10px]",
10160
+ onClick: () => {
10161
+ saveColumns("personal");
10162
+ toast.success("Saved personal view preferences");
10163
+ },
10164
+ children: "Save for Me"
10165
+ }), user?.role === "admin" && /* @__PURE__ */ jsx(Button, {
10166
+ size: "sm",
10167
+ variant: "secondary",
10168
+ className: "dy-h-7 dy-text-[10px]",
10169
+ onClick: () => {
10170
+ saveColumns("global");
10171
+ toast.success("Saved global view preferences");
10172
+ },
10173
+ children: "Save for Everyone"
9255
10174
  })]
9256
- }), viewMode === "spreadsheet" && /* @__PURE__ */ jsx(Check, { className: "dy-h-3.5 dy-w-3.5 dy-text-primary" })]
10175
+ }), /* @__PURE__ */ jsx(Button, {
10176
+ size: "sm",
10177
+ variant: "outline",
10178
+ className: "dy-h-7 dy-text-[10px] dy-w-full",
10179
+ onClick: () => {
10180
+ resetColumns();
10181
+ toast.success("Reset view preferences to default");
10182
+ },
10183
+ children: "Reset to Default"
10184
+ })]
9257
10185
  })
9258
10186
  ]
9259
- })] }), canCreate && /* @__PURE__ */ jsx(Link, {
10187
+ })] }), canCreate && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(Button, {
10188
+ variant: "outline",
10189
+ size: "sm",
10190
+ className: "dy-h-8 dy-px-3 dy-gap-1.5 dy-text-xs dy-w-full sm:dy-w-auto",
10191
+ onClick: () => setIsImportOpen(true),
10192
+ children: [/* @__PURE__ */ jsx(FileUp, { className: "dy-h-3.5 dy-w-3.5" }), /* @__PURE__ */ jsx("span", { children: "Import CSV" })]
10193
+ }), /* @__PURE__ */ jsx(Link, {
9260
10194
  to: `/collections/${slug}/new`,
9261
10195
  className: "dy-w-full sm:dy-w-auto",
9262
10196
  children: /* @__PURE__ */ jsxs(Button, {
@@ -9273,7 +10207,7 @@ function CollectionListPage({ slug }) {
9273
10207
  })
9274
10208
  ]
9275
10209
  })
9276
- })]
10210
+ })] })]
9277
10211
  })
9278
10212
  }),
9279
10213
  /* @__PURE__ */ jsx(AdminComponentSlot, {
@@ -9288,7 +10222,7 @@ function CollectionListPage({ slug }) {
9288
10222
  isLoading ? /* @__PURE__ */ jsx("div", {
9289
10223
  className: "dy-flex dy-h-[400px] dy-items-center dy-justify-center",
9290
10224
  children: /* @__PURE__ */ jsx("div", { className: "dy-h-8 dy-w-8 dy-animate-spin dy-rounded-full dy-border-4 dy-border-primary dy-border-t-transparent" })
9291
- }) : viewMode === "spreadsheet" ? /* @__PURE__ */ jsx(SpreadsheetEditor, {
10225
+ }) : localPreference.viewMode === "spreadsheet" ? /* @__PURE__ */ jsx(SpreadsheetEditor, {
9292
10226
  slug,
9293
10227
  schema,
9294
10228
  data: response?.docs || [],
@@ -9305,8 +10239,7 @@ function CollectionListPage({ slug }) {
9305
10239
  searchKey: schema.admin?.useAsTitle || schema.fields.find((f) => !f.admin?.hidden)?.name || "id",
9306
10240
  onRowSelectionChange: setRowSelection,
9307
10241
  rowSelection,
9308
- persistenceKey: columnPreferenceKey,
9309
- initialColumnVisibility,
10242
+ hideViewButton: true,
9310
10243
  toolbarActions: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(FilterBuilder, {
9311
10244
  schema,
9312
10245
  rules,
@@ -9383,6 +10316,12 @@ function CollectionListPage({ slug }) {
9383
10316
  componentProps: collectionComponentProps
9384
10317
  })
9385
10318
  ]
10319
+ }),
10320
+ /* @__PURE__ */ jsx(CsvImporterDialog, {
10321
+ open: isImportOpen,
10322
+ onOpenChange: setIsImportOpen,
10323
+ slug,
10324
+ schema
9386
10325
  })
9387
10326
  ]
9388
10327
  });
@@ -9744,7 +10683,172 @@ function WorkflowPanel({ collection, documentId, workflowMeta, workflowConfig })
9744
10683
  });
9745
10684
  }
9746
10685
  //#endregion
10686
+ //#region src/hooks/useLayoutPreference.ts
10687
+ function useLayoutPreference({ key, defaultKeys }) {
10688
+ const { client } = useDyrected();
10689
+ const queryClient = useQueryClient();
10690
+ const { data, isLoading } = useQuery({
10691
+ queryKey: ["preferences", key],
10692
+ queryFn: async () => {
10693
+ if (!client) return null;
10694
+ return (await client.getPreference(key)).value;
10695
+ },
10696
+ enabled: !!client,
10697
+ staleTime: 5e3,
10698
+ refetchOnWindowFocus: true
10699
+ });
10700
+ const reconciledLayout = useMemo(() => {
10701
+ const isStringArray = defaultKeys.every((k) => typeof k === "string");
10702
+ if (!data || !Array.isArray(data)) return defaultKeys;
10703
+ if (isStringArray) {
10704
+ const normalizedData = data.map((k) => typeof k === "string" ? k : k?.name || "").filter(Boolean);
10705
+ const defaultStrings = defaultKeys;
10706
+ const validKeys = normalizedData.filter((k) => defaultStrings.includes(k));
10707
+ const missingKeys = defaultStrings.filter((k) => !validKeys.includes(k));
10708
+ return [...validKeys, ...missingKeys];
10709
+ } else {
10710
+ const normalizedData = data.map((item) => {
10711
+ if (typeof item === "string") return { name: item };
10712
+ if (item && typeof item === "object" && "name" in item && typeof item.name === "string") return {
10713
+ name: item.name,
10714
+ width: item.width
10715
+ };
10716
+ return { name: "" };
10717
+ }).filter((item) => item.name !== "");
10718
+ const defaultItems = defaultKeys;
10719
+ const defaultNames = defaultItems.map((item) => item.name);
10720
+ const defaultMap = new Map(defaultItems.map((item) => [item.name, item]));
10721
+ const validItems = normalizedData.filter((item) => defaultNames.includes(item.name));
10722
+ const validNames = validItems.map((item) => item.name);
10723
+ const missingItems = defaultNames.filter((name) => !validNames.includes(name)).map((name) => defaultMap.get(name));
10724
+ return [...validItems.map((item) => {
10725
+ const def = defaultMap.get(item.name);
10726
+ return {
10727
+ ...item,
10728
+ width: item.width || def?.width
10729
+ };
10730
+ }), ...missingItems];
10731
+ }
10732
+ }, [data, defaultKeys]);
10733
+ const [localLayout, setLocalLayout] = useState(reconciledLayout);
10734
+ const syncLayout = useState(() => {
10735
+ return (next) => {
10736
+ setLocalLayout((prev) => {
10737
+ return prev.length === next.length && prev.every((v, i) => {
10738
+ if (typeof v === "string") return v === next[i];
10739
+ return v.name === next[i].name && v.width === next[i].width;
10740
+ }) ? prev : next;
10741
+ });
10742
+ };
10743
+ })[0];
10744
+ useEffect(() => {
10745
+ syncLayout(reconciledLayout);
10746
+ }, [reconciledLayout, syncLayout]);
10747
+ const saveMutation = useMutation({
10748
+ mutationFn: async ({ scope, value }) => {
10749
+ if (!client) throw new Error("Client not available");
10750
+ await client.setPreference(key, value, { scope });
10751
+ },
10752
+ onSuccess: () => {
10753
+ queryClient.invalidateQueries({ queryKey: ["preferences", key] });
10754
+ }
10755
+ });
10756
+ const resetMutation = useMutation({
10757
+ mutationFn: async ({ scope }) => {
10758
+ if (!client) throw new Error("Client not available");
10759
+ await client.deletePreference(key, { scope });
10760
+ },
10761
+ onSuccess: () => {
10762
+ queryClient.invalidateQueries({ queryKey: ["preferences", key] });
10763
+ }
10764
+ });
10765
+ return {
10766
+ layout: localLayout,
10767
+ setLayout: setLocalLayout,
10768
+ reconciledLayout,
10769
+ saveLayout: (scope) => saveMutation.mutateAsync({
10770
+ scope,
10771
+ value: localLayout
10772
+ }),
10773
+ resetLayout: (scope) => resetMutation.mutateAsync({ scope }),
10774
+ isLoading,
10775
+ isSaving: saveMutation.isPending,
10776
+ isResetting: resetMutation.isPending
10777
+ };
10778
+ }
10779
+ //#endregion
9747
10780
  //#region src/pages/collections/edit-page.tsx
10781
+ function SortableFieldItem({ id, label, type, width, onChangeWidth }) {
10782
+ const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id });
10783
+ return /* @__PURE__ */ jsxs("div", {
10784
+ ref: setNodeRef,
10785
+ style: {
10786
+ transform: CSS.Transform.toString(transform),
10787
+ transition,
10788
+ zIndex: isDragging ? 10 : 1
10789
+ },
10790
+ className: cn("dy-flex dy-items-center dy-gap-3 dy-p-3 dy-bg-card dy-border dy-border-border/60 dy-rounded-xl dy-shadow-sm dy-transition-all", isDragging && "dy-opacity-50 dy-border-primary"),
10791
+ children: [
10792
+ /* @__PURE__ */ jsx("div", {
10793
+ ...attributes,
10794
+ ...listeners,
10795
+ className: "dy-cursor-grab active:dy-cursor-grabbing dy-text-muted-foreground/60 hover:dy-text-foreground dy-p-1",
10796
+ children: /* @__PURE__ */ jsx(GripVertical, { className: "dy-h-4 dy-w-4" })
10797
+ }),
10798
+ /* @__PURE__ */ jsxs("div", {
10799
+ className: "dy-flex-1 dy-min-w-0",
10800
+ children: [/* @__PURE__ */ jsx("div", {
10801
+ className: "dy-text-sm dy-font-semibold dy-text-foreground dy-truncate",
10802
+ children: label
10803
+ }), /* @__PURE__ */ jsxs("div", {
10804
+ className: "dy-text-xs dy-text-muted-foreground",
10805
+ children: [
10806
+ id,
10807
+ " • ",
10808
+ /* @__PURE__ */ jsx("span", {
10809
+ className: "dy-uppercase",
10810
+ children: type
10811
+ })
10812
+ ]
10813
+ })]
10814
+ }),
10815
+ /* @__PURE__ */ jsx("div", {
10816
+ className: "dy-shrink-0",
10817
+ children: /* @__PURE__ */ jsxs("select", {
10818
+ value: width || "100%",
10819
+ onChange: (e) => onChangeWidth(e.target.value),
10820
+ className: "dy-text-[11px] dy-font-semibold dy-bg-muted/50 hover:dy-bg-muted dy-border dy-border-border/40 dy-rounded-lg dy-px-2 dy-py-1 dy-outline-none dy-cursor-pointer dy-transition-all",
10821
+ children: [
10822
+ /* @__PURE__ */ jsx("option", {
10823
+ value: "25%",
10824
+ children: "25%"
10825
+ }),
10826
+ /* @__PURE__ */ jsx("option", {
10827
+ value: "33.33%",
10828
+ children: "33%"
10829
+ }),
10830
+ /* @__PURE__ */ jsx("option", {
10831
+ value: "50%",
10832
+ children: "50%"
10833
+ }),
10834
+ /* @__PURE__ */ jsx("option", {
10835
+ value: "66.66%",
10836
+ children: "66%"
10837
+ }),
10838
+ /* @__PURE__ */ jsx("option", {
10839
+ value: "75%",
10840
+ children: "75%"
10841
+ }),
10842
+ /* @__PURE__ */ jsx("option", {
10843
+ value: "100%",
10844
+ children: "100%"
10845
+ })
10846
+ ]
10847
+ })
10848
+ })
10849
+ ]
10850
+ });
10851
+ }
9748
10852
  function EditEntryPage() {
9749
10853
  const { slug, id } = useParams();
9750
10854
  const [searchParams] = useSearchParams();
@@ -9755,6 +10859,77 @@ function EditEntryPage() {
9755
10859
  const [isDirty, setIsDirty] = useState(false);
9756
10860
  const [previewData, setPreviewData] = useState(null);
9757
10861
  const isEdit = !!id;
10862
+ const [isConfiguringView, setIsConfiguringView] = useState(false);
10863
+ const { data: schemas } = useQuery({
10864
+ queryKey: ["schemas"],
10865
+ queryFn: () => client.getSchemas(),
10866
+ enabled: !!client
10867
+ });
10868
+ const schema = schemas?.collections.find((c) => c.slug === slug);
10869
+ const fieldsList = useMemo(() => {
10870
+ if (!schema) return [];
10871
+ let fields = [...schema.fields];
10872
+ if (schema.upload) {
10873
+ const hasAlt = fields.some((f) => f.name === "alt");
10874
+ const hasCaption = fields.some((f) => f.name === "caption");
10875
+ const mediaFields = [];
10876
+ if (!hasAlt) mediaFields.push({
10877
+ name: "alt",
10878
+ type: "text",
10879
+ label: "Alt Text",
10880
+ admin: { description: "Describe the image for accessibility/screen readers." }
10881
+ });
10882
+ if (!hasCaption) mediaFields.push({
10883
+ name: "caption",
10884
+ type: "textarea",
10885
+ label: "Caption",
10886
+ admin: { description: "Add a caption/description for this media file." }
10887
+ });
10888
+ fields = [...mediaFields, ...fields];
10889
+ }
10890
+ return fields;
10891
+ }, [schema]);
10892
+ const defaultKeys = useMemo(() => {
10893
+ return fieldsList.filter((f) => !!f.name).map((f) => ({
10894
+ name: f.name,
10895
+ width: f.admin?.width || "100%"
10896
+ }));
10897
+ }, [fieldsList]);
10898
+ const { layout, setLayout, saveLayout, resetLayout, reconciledLayout, isLoading: isPreferenceLoading } = useLayoutPreference({
10899
+ key: `layout:collections:${slug}:edit`,
10900
+ defaultKeys
10901
+ });
10902
+ const orderedFields = useMemo(() => {
10903
+ const fieldMap = new Map(fieldsList.map((f) => [f.name, f]));
10904
+ const ordered = [];
10905
+ for (const item of layout) {
10906
+ const field = fieldMap.get(item.name);
10907
+ if (field) {
10908
+ const overriddenField = {
10909
+ ...field,
10910
+ admin: {
10911
+ ...field.admin,
10912
+ width: item.width || field.admin?.width || "100%"
10913
+ }
10914
+ };
10915
+ ordered.push(overriddenField);
10916
+ fieldMap.delete(item.name);
10917
+ }
10918
+ }
10919
+ for (const field of fieldMap.values()) ordered.push(field);
10920
+ return ordered;
10921
+ }, [fieldsList, layout]);
10922
+ const sensors = useSensors(useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }));
10923
+ const handleDragEnd = (event) => {
10924
+ const { active, over } = event;
10925
+ if (!over || active.id === over.id) return;
10926
+ setLayout((items) => {
10927
+ const oldIndex = items.findIndex((item) => item.name === active.id);
10928
+ const newIndex = items.findIndex((item) => item.name === over.id);
10929
+ if (oldIndex === -1 || newIndex === -1) return items;
10930
+ return arrayMove(items, oldIndex, newIndex);
10931
+ });
10932
+ };
9758
10933
  useEffect(() => {
9759
10934
  const handleBeforeUnload = (e) => {
9760
10935
  if (isDirty) {
@@ -9775,18 +10950,19 @@ function EditEntryPage() {
9775
10950
  window.addEventListener("keydown", handleSave);
9776
10951
  return () => window.removeEventListener("keydown", handleSave);
9777
10952
  }, []);
9778
- const { data: schemas } = useQuery({
9779
- queryKey: ["schemas"],
9780
- queryFn: () => client.getSchemas(),
9781
- enabled: !!client
9782
- });
9783
- const schema = schemas?.collections.find((c) => c.slug === slug);
9784
10953
  const schemaSlug = schema?.slug;
9785
10954
  const schemaPreviewUrl = schema?.admin?.previewUrl;
10955
+ const syncShowPreview = useState(() => (next) => {
10956
+ setShowPreview((prev) => prev === next ? prev : next);
10957
+ })[0];
9786
10958
  useEffect(() => {
9787
10959
  if (!schemaSlug || !schemaPreviewUrl) return;
9788
- setShowPreview((previous) => previous ? false : previous);
9789
- }, [schemaSlug, schemaPreviewUrl]);
10960
+ syncShowPreview(false);
10961
+ }, [
10962
+ schemaSlug,
10963
+ schemaPreviewUrl,
10964
+ syncShowPreview
10965
+ ]);
9790
10966
  const { data: entry, isLoading: isEntryLoading } = useQuery({
9791
10967
  queryKey: [
9792
10968
  "entry",
@@ -9796,14 +10972,13 @@ function EditEntryPage() {
9796
10972
  queryFn: () => client.collection(slug).findOne(id),
9797
10973
  enabled: !!client && isEdit
9798
10974
  });
10975
+ const syncPreviewData = useState(() => (next) => {
10976
+ setPreviewData((prev) => prev === next ? prev : next);
10977
+ })[0];
9799
10978
  useEffect(() => {
9800
10979
  if (!entry) return;
9801
- setPreviewData((previous) => previous === entry ? previous : entry);
9802
- }, [
9803
- entry,
9804
- id,
9805
- slug
9806
- ]);
10980
+ syncPreviewData(entry);
10981
+ }, [entry, syncPreviewData]);
9807
10982
  const isAdminUser = Array.isArray(user?.roles) && user.roles.includes("admin");
9808
10983
  const isSelf = !!user && !!id && (user.id === id || user.sub === id);
9809
10984
  const passwordChangeMode = schema?.auth ? isSelf ? "self" : isAdminUser ? "admin" : null : null;
@@ -9979,6 +11154,14 @@ function EditEntryPage() {
9979
11154
  title: showPreview ? "Hide Preview" : "Live Preview",
9980
11155
  children: showPreview ? /* @__PURE__ */ jsx(EyeOff, { className: "dy-h-3.5 dy-w-3.5" }) : /* @__PURE__ */ jsx(Eye, { className: "dy-h-3.5 dy-w-3.5" })
9981
11156
  }),
11157
+ /* @__PURE__ */ jsx(Button, {
11158
+ variant: "ghost",
11159
+ size: "icon",
11160
+ className: cn("dy-h-8 dy-w-8 dy-rounded-lg dy-transition-all", isConfiguringView ? "dy-bg-background dy-text-primary dy-shadow-sm hover:dy-bg-background" : "dy-text-muted-foreground hover:dy-bg-muted hover:dy-text-foreground"),
11161
+ onClick: () => setIsConfiguringView(!isConfiguringView),
11162
+ title: "View",
11163
+ children: /* @__PURE__ */ jsx(Settings2, { className: "dy-h-3.5 dy-w-3.5" })
11164
+ }),
9982
11165
  isEdit && canCreate && /* @__PURE__ */ jsx(Button, {
9983
11166
  variant: "outline",
9984
11167
  size: "icon",
@@ -10061,33 +11244,111 @@ function EditEntryPage() {
10061
11244
  })
10062
11245
  })]
10063
11246
  }),
10064
- (() => {
10065
- let fields = [...schema.fields];
10066
- if (schema.upload) {
10067
- const hasAlt = fields.some((f) => f.name === "alt");
10068
- const hasCaption = fields.some((f) => f.name === "caption");
10069
- const mediaFields = [];
10070
- if (!hasAlt) mediaFields.push({
10071
- name: "alt",
10072
- type: "text",
10073
- label: "Alt Text",
10074
- admin: { description: "Describe the image for accessibility/screen readers." }
10075
- });
10076
- if (!hasCaption) mediaFields.push({
10077
- name: "caption",
10078
- type: "textarea",
10079
- label: "Caption",
10080
- admin: { description: "Add a caption/description for this media file." }
10081
- });
10082
- fields = [...mediaFields, ...fields];
10083
- }
11247
+ isConfiguringView ? /* @__PURE__ */ jsxs("div", {
11248
+ className: "dy-space-y-6",
11249
+ children: [/* @__PURE__ */ jsxs("div", {
11250
+ className: "dy-p-4 dy-rounded-xl",
11251
+ children: [/* @__PURE__ */ jsx("p", {
11252
+ className: "mb-2 dy-text-sm dy-text-muted-foreground",
11253
+ children: "Drag and drop fields to reorder the form layout. Changes will be saved as your personal preference or global default."
11254
+ }), /* @__PURE__ */ jsx("div", {
11255
+ className: "dy-space-y-2 dy-pr-1 dy-outline-none",
11256
+ children: /* @__PURE__ */ jsx(DndContext, {
11257
+ sensors,
11258
+ collisionDetection: closestCenter,
11259
+ onDragEnd: handleDragEnd,
11260
+ children: /* @__PURE__ */ jsx(SortableContext, {
11261
+ items: layout.map((item) => item.name),
11262
+ strategy: verticalListSortingStrategy,
11263
+ children: /* @__PURE__ */ jsx("div", {
11264
+ className: "dy-space-y-1.5",
11265
+ children: layout.map((item) => {
11266
+ const field = fieldsList.find((f) => f.name === item.name);
11267
+ if (!field) return null;
11268
+ return /* @__PURE__ */ jsx(SortableFieldItem, {
11269
+ id: item.name,
11270
+ label: field.label || field.name,
11271
+ type: field.type,
11272
+ width: item.width || "100%",
11273
+ onChangeWidth: (newWidth) => {
11274
+ setLayout((prev) => prev.map((x) => x.name === item.name ? {
11275
+ ...x,
11276
+ width: newWidth
11277
+ } : x));
11278
+ }
11279
+ }, item.name);
11280
+ })
11281
+ })
11282
+ })
11283
+ })
11284
+ })]
11285
+ }), /* @__PURE__ */ jsx("div", {
11286
+ className: "dy-sticky dy-bottom-0 dy-left-0 dy-right-0 dy-z-20 dy-pointer-events-none",
11287
+ children: /* @__PURE__ */ jsx("div", {
11288
+ className: "dy-pointer-events-auto dy-mx-auto dy-max-w-2xl dy-px-4 dy-pb-6",
11289
+ children: /* @__PURE__ */ jsxs("div", {
11290
+ className: "dy-flex dy-items-center dy-justify-between dy-gap-3 dy-rounded-2xl dy-border dy-border-border/50 dy-bg-background/80 dy-backdrop-blur-xl dy-px-4 dy-py-3 dy-shadow-xl dy-shadow-black/10 dy-animate-in dy-slide-in-from-bottom-2 dy-fade-in dy-duration-200",
11291
+ children: [/* @__PURE__ */ jsx("p", {
11292
+ className: "dy-text-sm dy-font-medium dy-text-muted-foreground",
11293
+ children: "View"
11294
+ }), /* @__PURE__ */ jsxs("div", {
11295
+ className: "dy-flex dy-items-center dy-gap-2",
11296
+ children: [
11297
+ /* @__PURE__ */ jsx(Button, {
11298
+ variant: "outline",
11299
+ size: "sm",
11300
+ className: "dy-rounded-xl",
11301
+ onClick: async () => {
11302
+ await resetLayout("personal");
11303
+ setIsConfiguringView(false);
11304
+ toast.success("Personal layout reset to default");
11305
+ },
11306
+ children: "Reset"
11307
+ }),
11308
+ isAdminUser && /* @__PURE__ */ jsx(Button, {
11309
+ variant: "outline",
11310
+ size: "sm",
11311
+ className: "dy-rounded-xl",
11312
+ onClick: async () => {
11313
+ await saveLayout("global");
11314
+ setIsConfiguringView(false);
11315
+ toast.success("Saved for everyone successfully");
11316
+ },
11317
+ children: "Save for Everyone"
11318
+ }),
11319
+ /* @__PURE__ */ jsx(Button, {
11320
+ size: "sm",
11321
+ className: "dy-rounded-xl dy-bg-primary dy-text-primary-foreground",
11322
+ onClick: async () => {
11323
+ await saveLayout("personal");
11324
+ setIsConfiguringView(false);
11325
+ toast.success("Saved for me successfully");
11326
+ },
11327
+ children: "Save for Me"
11328
+ }),
11329
+ /* @__PURE__ */ jsx(Button, {
11330
+ variant: "ghost",
11331
+ size: "sm",
11332
+ className: "dy-rounded-xl",
11333
+ onClick: () => {
11334
+ setLayout(reconciledLayout);
11335
+ setIsConfiguringView(false);
11336
+ },
11337
+ children: "Cancel"
11338
+ })
11339
+ ]
11340
+ })]
11341
+ })
11342
+ })
11343
+ })]
11344
+ }) : (() => {
10084
11345
  const queryParamsDefaults = {};
10085
11346
  if (!isEdit) searchParams.forEach((value, key) => {
10086
11347
  queryParamsDefaults[key] = value;
10087
11348
  });
10088
11349
  return /* @__PURE__ */ jsx(FormEngine, {
10089
11350
  collection: slug,
10090
- fields,
11351
+ fields: orderedFields,
10091
11352
  defaultValues: isEdit ? entry : {
10092
11353
  ...queryParamsDefaults,
10093
11354
  ...entry
@@ -10098,7 +11359,7 @@ function EditEntryPage() {
10098
11359
  ...newData
10099
11360
  }),
10100
11361
  onChange: (dirty) => setIsDirty(dirty),
10101
- isLoading: saveMutation.isPending,
11362
+ isLoading: saveMutation.isPending || isPreferenceLoading,
10102
11363
  submitLabel: isEdit ? "Save Changes" : "Create Entry",
10103
11364
  readOnly: isEdit ? !canUpdate : !canCreate,
10104
11365
  passwordChangeMode: isEdit ? passwordChangeMode : null,
@@ -10240,18 +11501,6 @@ CardFooter.displayName = "CardFooter";
10240
11501
  //#region src/components/ui/aspect-ratio.tsx
10241
11502
  var AspectRatio = AspectRatioPrimitive.Root;
10242
11503
  //#endregion
10243
- //#region src/components/ui/progress.tsx
10244
- var Progress = React$1.forwardRef(({ className, value, ...props }, ref) => /* @__PURE__ */ jsx(ProgressPrimitive.Root, {
10245
- ref,
10246
- className: cn("dy-relative dy-h-4 dy-w-full dy-overflow-hidden dy-rounded-full dy-bg-secondary", className),
10247
- ...props,
10248
- children: /* @__PURE__ */ jsx(ProgressPrimitive.Indicator, {
10249
- className: "dy-h-full dy-w-full dy-flex-1 dy-bg-primary dy-transition-all",
10250
- style: { transform: `translateX(-${100 - (value || 0)}%)` }
10251
- })
10252
- }));
10253
- Progress.displayName = ProgressPrimitive.Root.displayName;
10254
- //#endregion
10255
11504
  //#region src/components/ui/separator.tsx
10256
11505
  var Separator = React$1.forwardRef(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => /* @__PURE__ */ jsx(SeparatorPrimitive.Root, {
10257
11506
  ref,