@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,
|
|
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,
|
|
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
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
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-
|
|
1923
|
-
|
|
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/
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
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
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
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/
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
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/
|
|
2058
|
-
function
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
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 =
|
|
2162
|
-
var SheetPortal =
|
|
2163
|
-
var SheetOverlay = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
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 =
|
|
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(
|
|
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(
|
|
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 =
|
|
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(
|
|
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 =
|
|
2210
|
-
var SheetDescription = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
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 =
|
|
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: {
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
])
|
|
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
|
|
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-
|
|
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 (
|
|
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 =
|
|
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
|
|
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(
|
|
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(
|
|
10069
|
+
}), /* @__PURE__ */ jsxs(PopoverContent, {
|
|
9216
10070
|
align: "end",
|
|
9217
|
-
className: "dy-w-
|
|
10071
|
+
className: "dy-w-80 dy-p-4 dy-space-y-4",
|
|
9218
10072
|
children: [
|
|
9219
|
-
/* @__PURE__ */
|
|
9220
|
-
className: "dy-
|
|
9221
|
-
children: "
|
|
9222
|
-
|
|
9223
|
-
|
|
9224
|
-
|
|
9225
|
-
|
|
9226
|
-
|
|
9227
|
-
|
|
9228
|
-
|
|
9229
|
-
|
|
9230
|
-
|
|
9231
|
-
|
|
9232
|
-
|
|
9233
|
-
|
|
9234
|
-
|
|
9235
|
-
|
|
9236
|
-
|
|
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
|
-
})
|
|
10099
|
+
})]
|
|
9240
10100
|
}),
|
|
9241
|
-
/* @__PURE__ */ jsxs(
|
|
9242
|
-
|
|
9243
|
-
|
|
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-
|
|
9246
|
-
children: [/* @__PURE__ */ jsx(
|
|
9247
|
-
|
|
9248
|
-
|
|
9249
|
-
|
|
9250
|
-
|
|
9251
|
-
|
|
9252
|
-
|
|
9253
|
-
|
|
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
|
-
}),
|
|
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__ */
|
|
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
|
-
|
|
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
|
-
|
|
9789
|
-
}, [
|
|
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
|
-
|
|
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
|
-
|
|
10066
|
-
|
|
10067
|
-
|
|
10068
|
-
|
|
10069
|
-
|
|
10070
|
-
|
|
10071
|
-
|
|
10072
|
-
|
|
10073
|
-
|
|
10074
|
-
|
|
10075
|
-
|
|
10076
|
-
|
|
10077
|
-
|
|
10078
|
-
|
|
10079
|
-
|
|
10080
|
-
|
|
10081
|
-
|
|
10082
|
-
|
|
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,
|