@dyrected/admin 2.5.36 → 2.5.38
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";
|
|
@@ -16,10 +16,16 @@ import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
|
16
16
|
import * as SelectPrimitive from "@radix-ui/react-select";
|
|
17
17
|
import { flexRender, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, 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);
|
|
@@ -1828,8 +1829,8 @@ function DataTable({ columns, data, searchKey, rowSelection: externalRowSelectio
|
|
|
1828
1829
|
className: "dy-flex dy-flex-col dy-gap-3 sm:dy-flex-row sm:dy-items-center sm:dy-gap-4",
|
|
1829
1830
|
children: [
|
|
1830
1831
|
/* @__PURE__ */ jsxs("div", {
|
|
1831
|
-
className: `dy-order-1 dy-grid dy-w-full dy-gap-2 sm:dy-order-3 sm:dy-ml-auto sm:dy-flex sm:dy-w-auto sm:dy-items-center ${toolbarActions ? "dy-grid-cols-3" : "dy-grid-cols-1"}`,
|
|
1832
|
-
children: [toolbarActions, /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
|
|
1832
|
+
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"}`,
|
|
1833
|
+
children: [toolbarActions, !hideViewButton && /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
|
|
1833
1834
|
asChild: true,
|
|
1834
1835
|
children: /* @__PURE__ */ jsxs(Button, {
|
|
1835
1836
|
variant: "outline",
|
|
@@ -1943,6 +1944,621 @@ var Checkbox = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__
|
|
|
1943
1944
|
}));
|
|
1944
1945
|
Checkbox.displayName = CheckboxPrimitive.Root.displayName;
|
|
1945
1946
|
//#endregion
|
|
1947
|
+
//#region src/components/ui/dialog.tsx
|
|
1948
|
+
var Dialog = DialogPrimitive.Root;
|
|
1949
|
+
var DialogTrigger = DialogPrimitive.Trigger;
|
|
1950
|
+
var DialogPortal = DialogPrimitive.Portal;
|
|
1951
|
+
var DialogOverlay = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Overlay, {
|
|
1952
|
+
ref,
|
|
1953
|
+
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),
|
|
1954
|
+
...props
|
|
1955
|
+
}));
|
|
1956
|
+
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
|
1957
|
+
var DialogContent = React$1.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsx(DialogPortal, { children: /* @__PURE__ */ jsxs("div", {
|
|
1958
|
+
className: "dy-admin-ui",
|
|
1959
|
+
children: [/* @__PURE__ */ jsx(DialogOverlay, {}), /* @__PURE__ */ jsxs(DialogPrimitive.Content, {
|
|
1960
|
+
ref,
|
|
1961
|
+
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),
|
|
1962
|
+
...props,
|
|
1963
|
+
children: [children, /* @__PURE__ */ jsxs(DialogPrimitive.Close, {
|
|
1964
|
+
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",
|
|
1965
|
+
children: [/* @__PURE__ */ jsx(X, { className: "dy-h-4 dy-w-4" }), /* @__PURE__ */ jsx("span", {
|
|
1966
|
+
className: "dy-sr-only",
|
|
1967
|
+
children: "Close"
|
|
1968
|
+
})]
|
|
1969
|
+
})]
|
|
1970
|
+
})]
|
|
1971
|
+
}) }));
|
|
1972
|
+
DialogContent.displayName = DialogPrimitive.Content.displayName;
|
|
1973
|
+
var DialogHeader = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
|
|
1974
|
+
className: cn("dy-flex dy-flex-col dy-space-y-1.5 dy-text-center sm:dy-text-left", className),
|
|
1975
|
+
...props
|
|
1976
|
+
});
|
|
1977
|
+
DialogHeader.displayName = "DialogHeader";
|
|
1978
|
+
var DialogFooter = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
|
|
1979
|
+
className: cn("dy-flex dy-flex-col-reverse sm:dy-flex-row sm:dy-justify-end sm:dy-space-x-2", className),
|
|
1980
|
+
...props
|
|
1981
|
+
});
|
|
1982
|
+
DialogFooter.displayName = "DialogFooter";
|
|
1983
|
+
var DialogTitle = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Title, {
|
|
1984
|
+
ref,
|
|
1985
|
+
className: cn("dy-text-lg dy-font-semibold dy-leading-none dy-tracking-tight", className),
|
|
1986
|
+
...props
|
|
1987
|
+
}));
|
|
1988
|
+
DialogTitle.displayName = DialogPrimitive.Title.displayName;
|
|
1989
|
+
var DialogDescription = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Description, {
|
|
1990
|
+
ref,
|
|
1991
|
+
className: cn("dy-text-sm dy-text-muted-foreground", className),
|
|
1992
|
+
...props
|
|
1993
|
+
}));
|
|
1994
|
+
DialogDescription.displayName = DialogPrimitive.Description.displayName;
|
|
1995
|
+
//#endregion
|
|
1996
|
+
//#region src/components/ui/progress.tsx
|
|
1997
|
+
var Progress = React$1.forwardRef(({ className, value, ...props }, ref) => /* @__PURE__ */ jsx(ProgressPrimitive.Root, {
|
|
1998
|
+
ref,
|
|
1999
|
+
className: cn("dy-relative dy-h-4 dy-w-full dy-overflow-hidden dy-rounded-full dy-bg-secondary", className),
|
|
2000
|
+
...props,
|
|
2001
|
+
children: /* @__PURE__ */ jsx(ProgressPrimitive.Indicator, {
|
|
2002
|
+
className: "dy-h-full dy-w-full dy-flex-1 dy-bg-primary dy-transition-all",
|
|
2003
|
+
style: { transform: `translateX(-${100 - (value || 0)}%)` }
|
|
2004
|
+
})
|
|
2005
|
+
}));
|
|
2006
|
+
Progress.displayName = ProgressPrimitive.Root.displayName;
|
|
2007
|
+
//#endregion
|
|
2008
|
+
//#region src/components/ui/scroll-area.tsx
|
|
2009
|
+
var ScrollArea = React$1.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(ScrollAreaPrimitive.Root, {
|
|
2010
|
+
ref,
|
|
2011
|
+
className: cn("dy-relative dy-overflow-hidden", className),
|
|
2012
|
+
...props,
|
|
2013
|
+
children: [
|
|
2014
|
+
/* @__PURE__ */ jsx(ScrollAreaPrimitive.Viewport, {
|
|
2015
|
+
className: "dy-h-full dy-w-full dy-rounded-[inherit]",
|
|
2016
|
+
children
|
|
2017
|
+
}),
|
|
2018
|
+
/* @__PURE__ */ jsx(ScrollBar, {}),
|
|
2019
|
+
/* @__PURE__ */ jsx(ScrollAreaPrimitive.Corner, {})
|
|
2020
|
+
]
|
|
2021
|
+
}));
|
|
2022
|
+
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
|
|
2023
|
+
var ScrollBar = React$1.forwardRef(({ className, orientation = "vertical", ...props }, ref) => /* @__PURE__ */ jsx(ScrollAreaPrimitive.ScrollAreaScrollbar, {
|
|
2024
|
+
ref,
|
|
2025
|
+
orientation,
|
|
2026
|
+
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),
|
|
2027
|
+
...props,
|
|
2028
|
+
children: /* @__PURE__ */ jsx(ScrollAreaPrimitive.ScrollAreaThumb, { className: "dy-relative dy-flex-1 dy-rounded-full dy-bg-border" })
|
|
2029
|
+
}));
|
|
2030
|
+
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
|
|
2031
|
+
//#endregion
|
|
2032
|
+
//#region src/components/ui/csv-importer.tsx
|
|
2033
|
+
function CsvImporter({ slug, schema, onClose }) {
|
|
2034
|
+
const { client } = useDyrected();
|
|
2035
|
+
const queryClient = useQueryClient();
|
|
2036
|
+
const [step, setStep] = React$1.useState("upload");
|
|
2037
|
+
const [csvHeaders, setCsvHeaders] = React$1.useState([]);
|
|
2038
|
+
const [parsedRows, setParsedRows] = React$1.useState([]);
|
|
2039
|
+
const [mapping, setMapping] = React$1.useState({});
|
|
2040
|
+
const [validatedData, setValidatedData] = React$1.useState([]);
|
|
2041
|
+
const [totalRows, setTotalRows] = React$1.useState(0);
|
|
2042
|
+
const [processedCount, setProcessedCount] = React$1.useState(0);
|
|
2043
|
+
const [successCount, setSuccessCount] = React$1.useState(0);
|
|
2044
|
+
const [failedRows, setFailedRows] = React$1.useState([]);
|
|
2045
|
+
const importableFields = React$1.useMemo(() => {
|
|
2046
|
+
return schema.fields.filter((f) => f.name !== "id" && f.name !== "createdAt" && f.name !== "updatedAt" && f.type !== "row" && f.type !== "join" && !f.admin?.hidden);
|
|
2047
|
+
}, [schema]);
|
|
2048
|
+
const requiredFields = React$1.useMemo(() => {
|
|
2049
|
+
return importableFields.filter((f) => f.required);
|
|
2050
|
+
}, [importableFields]);
|
|
2051
|
+
const handleFileChange = (e) => {
|
|
2052
|
+
const selectedFile = e.target.files?.[0];
|
|
2053
|
+
if (!selectedFile) return;
|
|
2054
|
+
Papa.parse(selectedFile, {
|
|
2055
|
+
header: true,
|
|
2056
|
+
skipEmptyLines: "greedy",
|
|
2057
|
+
complete: (results) => {
|
|
2058
|
+
const headers = results.meta.fields || [];
|
|
2059
|
+
setCsvHeaders(headers);
|
|
2060
|
+
setParsedRows(results.data);
|
|
2061
|
+
const initialMapping = {};
|
|
2062
|
+
headers.forEach((header) => {
|
|
2063
|
+
const match = importableFields.find((f) => f.name.toLowerCase() === header.toLowerCase() || f.label?.toLowerCase() === header.toLowerCase());
|
|
2064
|
+
initialMapping[header] = match ? match.name : "__ignore__";
|
|
2065
|
+
});
|
|
2066
|
+
setMapping(initialMapping);
|
|
2067
|
+
setStep("map");
|
|
2068
|
+
},
|
|
2069
|
+
error: (error) => {
|
|
2070
|
+
console.error("PapaParse error:", error);
|
|
2071
|
+
}
|
|
2072
|
+
});
|
|
2073
|
+
};
|
|
2074
|
+
const handleMapChange = (header, fieldName) => {
|
|
2075
|
+
setMapping((prev) => ({
|
|
2076
|
+
...prev,
|
|
2077
|
+
[header]: fieldName
|
|
2078
|
+
}));
|
|
2079
|
+
};
|
|
2080
|
+
const isMapValid = React$1.useMemo(() => {
|
|
2081
|
+
const mappedFields = Object.values(mapping);
|
|
2082
|
+
return requiredFields.every((f) => mappedFields.includes(f.name));
|
|
2083
|
+
}, [requiredFields, mapping]);
|
|
2084
|
+
const coerceAndValidate = (val, field) => {
|
|
2085
|
+
if (val === void 0 || val === null || String(val).trim() === "") {
|
|
2086
|
+
if (field.required) return {
|
|
2087
|
+
value: null,
|
|
2088
|
+
error: `${field.label || field.name} is required`
|
|
2089
|
+
};
|
|
2090
|
+
return { value: null };
|
|
2091
|
+
}
|
|
2092
|
+
const strVal = String(val).trim();
|
|
2093
|
+
switch (field.type) {
|
|
2094
|
+
case "number": {
|
|
2095
|
+
const num = Number(strVal);
|
|
2096
|
+
if (isNaN(num)) return {
|
|
2097
|
+
value: null,
|
|
2098
|
+
error: "Must be a valid number"
|
|
2099
|
+
};
|
|
2100
|
+
return { value: num };
|
|
2101
|
+
}
|
|
2102
|
+
case "boolean": {
|
|
2103
|
+
const lower = strVal.toLowerCase();
|
|
2104
|
+
if (lower === "true" || lower === "yes" || lower === "1") return { value: true };
|
|
2105
|
+
if (lower === "false" || lower === "no" || lower === "0") return { value: false };
|
|
2106
|
+
return {
|
|
2107
|
+
value: null,
|
|
2108
|
+
error: "Must be a boolean (true/false, yes/no, 1/0)"
|
|
2109
|
+
};
|
|
2110
|
+
}
|
|
2111
|
+
case "date":
|
|
2112
|
+
case "datetime": {
|
|
2113
|
+
const date = new Date(strVal);
|
|
2114
|
+
if (isNaN(date.getTime())) return {
|
|
2115
|
+
value: null,
|
|
2116
|
+
error: "Must be a valid date"
|
|
2117
|
+
};
|
|
2118
|
+
return { value: date.toISOString() };
|
|
2119
|
+
}
|
|
2120
|
+
case "select":
|
|
2121
|
+
case "radio":
|
|
2122
|
+
if (field.options && Array.isArray(field.options) && field.options.length > 0) {
|
|
2123
|
+
const found = field.options.find((opt) => {
|
|
2124
|
+
const optVal = typeof opt === "string" ? opt : opt.value;
|
|
2125
|
+
return String(optVal).toLowerCase() === strVal.toLowerCase();
|
|
2126
|
+
});
|
|
2127
|
+
if (!found) return {
|
|
2128
|
+
value: null,
|
|
2129
|
+
error: `Must be one of: ${field.options.map((opt) => typeof opt === "string" ? opt : opt.label || opt.value).join(", ")}`
|
|
2130
|
+
};
|
|
2131
|
+
return { value: typeof found === "string" ? found : found.value };
|
|
2132
|
+
}
|
|
2133
|
+
return { value: strVal };
|
|
2134
|
+
default: return { value: strVal };
|
|
2135
|
+
}
|
|
2136
|
+
};
|
|
2137
|
+
const handleProceedToPreview = () => {
|
|
2138
|
+
setValidatedData(parsedRows.map((row, idx) => {
|
|
2139
|
+
const coercedRow = {};
|
|
2140
|
+
const errors = {};
|
|
2141
|
+
let isValid = true;
|
|
2142
|
+
Object.entries(mapping).forEach(([csvHeader, targetField]) => {
|
|
2143
|
+
if (!targetField || targetField === "__ignore__") return;
|
|
2144
|
+
const fieldSchema = importableFields.find((f) => f.name === targetField);
|
|
2145
|
+
if (!fieldSchema) return;
|
|
2146
|
+
const rawValue = row[csvHeader];
|
|
2147
|
+
const { value, error } = coerceAndValidate(rawValue, fieldSchema);
|
|
2148
|
+
if (error) {
|
|
2149
|
+
errors[targetField] = error;
|
|
2150
|
+
isValid = false;
|
|
2151
|
+
} else coercedRow[targetField] = value;
|
|
2152
|
+
});
|
|
2153
|
+
requiredFields.forEach((field) => {
|
|
2154
|
+
if (!Object.prototype.hasOwnProperty.call(coercedRow, field.name) || coercedRow[field.name] === null) {
|
|
2155
|
+
errors[field.name] = `${field.label || field.name} is required`;
|
|
2156
|
+
isValid = false;
|
|
2157
|
+
}
|
|
2158
|
+
});
|
|
2159
|
+
return {
|
|
2160
|
+
rowNumber: idx + 1,
|
|
2161
|
+
data: coercedRow,
|
|
2162
|
+
errors,
|
|
2163
|
+
isValid
|
|
2164
|
+
};
|
|
2165
|
+
}));
|
|
2166
|
+
setStep("preview");
|
|
2167
|
+
};
|
|
2168
|
+
const startImport = async () => {
|
|
2169
|
+
setStep("importing");
|
|
2170
|
+
setTotalRows(validatedData.length);
|
|
2171
|
+
setProcessedCount(0);
|
|
2172
|
+
setSuccessCount(0);
|
|
2173
|
+
setFailedRows([]);
|
|
2174
|
+
let success = 0;
|
|
2175
|
+
const failures = [];
|
|
2176
|
+
for (let i = 0; i < validatedData.length; i++) {
|
|
2177
|
+
const rowResult = validatedData[i];
|
|
2178
|
+
setProcessedCount(i + 1);
|
|
2179
|
+
if (!rowResult.isValid) {
|
|
2180
|
+
failures.push({
|
|
2181
|
+
row: rowResult.rowNumber,
|
|
2182
|
+
data: rowResult.data,
|
|
2183
|
+
error: `Validation error: ${Object.values(rowResult.errors).join(", ")}`
|
|
2184
|
+
});
|
|
2185
|
+
setFailedRows([...failures]);
|
|
2186
|
+
continue;
|
|
2187
|
+
}
|
|
2188
|
+
try {
|
|
2189
|
+
await client.collection(slug).create(rowResult.data);
|
|
2190
|
+
success++;
|
|
2191
|
+
setSuccessCount(success);
|
|
2192
|
+
} catch (error) {
|
|
2193
|
+
failures.push({
|
|
2194
|
+
row: rowResult.rowNumber,
|
|
2195
|
+
data: rowResult.data,
|
|
2196
|
+
error: error.message || "Failed to create entry"
|
|
2197
|
+
});
|
|
2198
|
+
setFailedRows([...failures]);
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2201
|
+
queryClient.invalidateQueries({ queryKey: ["collection", slug] });
|
|
2202
|
+
setStep("complete");
|
|
2203
|
+
};
|
|
2204
|
+
const downloadFailedCsv = () => {
|
|
2205
|
+
if (failedRows.length === 0) return;
|
|
2206
|
+
const headers = [...csvHeaders, "Import Error"];
|
|
2207
|
+
const rows = failedRows.map((f) => {
|
|
2208
|
+
return {
|
|
2209
|
+
...parsedRows[f.row - 1],
|
|
2210
|
+
"Import Error": f.error
|
|
2211
|
+
};
|
|
2212
|
+
});
|
|
2213
|
+
const csvContent = Papa.unparse({
|
|
2214
|
+
fields: headers,
|
|
2215
|
+
data: rows
|
|
2216
|
+
});
|
|
2217
|
+
const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
|
|
2218
|
+
const url = URL.createObjectURL(blob);
|
|
2219
|
+
const link = document.createElement("a");
|
|
2220
|
+
link.setAttribute("href", url);
|
|
2221
|
+
link.setAttribute("download", `${slug}_failed_imports.csv`);
|
|
2222
|
+
document.body.appendChild(link);
|
|
2223
|
+
link.click();
|
|
2224
|
+
document.body.removeChild(link);
|
|
2225
|
+
};
|
|
2226
|
+
const previewRows = React$1.useMemo(() => {
|
|
2227
|
+
return validatedData.slice(0, 100);
|
|
2228
|
+
}, [validatedData]);
|
|
2229
|
+
const totalErrors = React$1.useMemo(() => {
|
|
2230
|
+
return validatedData.filter((r) => !r.isValid).length;
|
|
2231
|
+
}, [validatedData]);
|
|
2232
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
2233
|
+
className: "dy-space-y-6",
|
|
2234
|
+
children: [
|
|
2235
|
+
step === "upload" && /* @__PURE__ */ jsx("div", {
|
|
2236
|
+
className: "dy-space-y-4",
|
|
2237
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
2238
|
+
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",
|
|
2239
|
+
children: /* @__PURE__ */ jsxs("label", {
|
|
2240
|
+
className: "dy-cursor-pointer dy-block dy-space-y-4 dy-px-6",
|
|
2241
|
+
children: [
|
|
2242
|
+
/* @__PURE__ */ jsx(Upload, { className: "dy-h-10 dy-w-10 dy-mx-auto dy-text-muted-foreground" }),
|
|
2243
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2244
|
+
className: "dy-space-y-1",
|
|
2245
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
2246
|
+
className: "dy-text-sm dy-font-semibold",
|
|
2247
|
+
children: "Click to upload or drag & drop"
|
|
2248
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
2249
|
+
className: "dy-text-xs dy-text-muted-foreground",
|
|
2250
|
+
children: "CSV files only"
|
|
2251
|
+
})]
|
|
2252
|
+
}),
|
|
2253
|
+
/* @__PURE__ */ jsx("input", {
|
|
2254
|
+
type: "file",
|
|
2255
|
+
accept: ".csv",
|
|
2256
|
+
className: "dy-hidden",
|
|
2257
|
+
onChange: handleFileChange
|
|
2258
|
+
})
|
|
2259
|
+
]
|
|
2260
|
+
})
|
|
2261
|
+
})
|
|
2262
|
+
}),
|
|
2263
|
+
step === "map" && /* @__PURE__ */ jsxs("div", {
|
|
2264
|
+
className: "dy-space-y-4",
|
|
2265
|
+
children: [
|
|
2266
|
+
/* @__PURE__ */ jsx("div", {
|
|
2267
|
+
className: "dy-p-4 dy-bg-muted/40 dy-border dy-border-border dy-rounded-xl dy-text-sm dy-text-muted-foreground",
|
|
2268
|
+
children: "Map the columns in your CSV file to the corresponding fields of the collection. All required fields must be mapped to proceed."
|
|
2269
|
+
}),
|
|
2270
|
+
/* @__PURE__ */ jsx(ScrollArea, {
|
|
2271
|
+
className: "dy-h-[300px] dy-pr-4 dy-border dy-border-border dy-rounded-xl",
|
|
2272
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
2273
|
+
className: "dy-divide-y dy-divide-border",
|
|
2274
|
+
children: csvHeaders.map((header) => {
|
|
2275
|
+
const mappedVal = mapping[header] ?? "__ignore__";
|
|
2276
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
2277
|
+
className: "dy-flex dy-items-center dy-justify-between dy-p-3.5",
|
|
2278
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
2279
|
+
className: "dy-space-y-1 dy-flex-1 dy-pr-4",
|
|
2280
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
2281
|
+
className: "dy-text-sm dy-font-semibold dy-text-foreground",
|
|
2282
|
+
children: header
|
|
2283
|
+
}), parsedRows[0] && /* @__PURE__ */ jsxs("p", {
|
|
2284
|
+
className: "dy-text-xs dy-text-muted-foreground dy-truncate",
|
|
2285
|
+
children: ["Sample: ", parsedRows[0][header]]
|
|
2286
|
+
})]
|
|
2287
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
2288
|
+
className: "dy-w-[200px]",
|
|
2289
|
+
children: /* @__PURE__ */ jsxs(Select, {
|
|
2290
|
+
value: mappedVal,
|
|
2291
|
+
onValueChange: (val) => handleMapChange(header, val),
|
|
2292
|
+
children: [/* @__PURE__ */ jsx(SelectTrigger, {
|
|
2293
|
+
className: "dy-w-full",
|
|
2294
|
+
children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Ignore field" })
|
|
2295
|
+
}), /* @__PURE__ */ jsxs(SelectContent, { children: [/* @__PURE__ */ jsx(SelectItem, {
|
|
2296
|
+
value: "__ignore__",
|
|
2297
|
+
children: "Ignore field"
|
|
2298
|
+
}), importableFields.map((field) => /* @__PURE__ */ jsxs(SelectItem, {
|
|
2299
|
+
value: field.name,
|
|
2300
|
+
children: [field.required ? "* " : "", field.label || field.name]
|
|
2301
|
+
}, field.name))] })]
|
|
2302
|
+
})
|
|
2303
|
+
})]
|
|
2304
|
+
}, header);
|
|
2305
|
+
})
|
|
2306
|
+
})
|
|
2307
|
+
}),
|
|
2308
|
+
requiredFields.length > 0 && /* @__PURE__ */ jsxs("div", {
|
|
2309
|
+
className: "dy-space-y-2",
|
|
2310
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
2311
|
+
className: "dy-text-xs dy-font-semibold dy-text-muted-foreground",
|
|
2312
|
+
children: "Required Fields:"
|
|
2313
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
2314
|
+
className: "dy-flex dy-flex-wrap dy-gap-2",
|
|
2315
|
+
children: requiredFields.map((f) => {
|
|
2316
|
+
const isMapped = Object.values(mapping).includes(f.name);
|
|
2317
|
+
return /* @__PURE__ */ jsxs(Badge, {
|
|
2318
|
+
variant: isMapped ? "secondary" : "destructive",
|
|
2319
|
+
className: "dy-rounded-md",
|
|
2320
|
+
children: [isMapped ? "✓ " : "✗ ", f.label || f.name]
|
|
2321
|
+
}, f.name);
|
|
2322
|
+
})
|
|
2323
|
+
})]
|
|
2324
|
+
}),
|
|
2325
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2326
|
+
className: "dy-flex dy-justify-between dy-pt-4",
|
|
2327
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
2328
|
+
variant: "outline",
|
|
2329
|
+
onClick: () => setStep("upload"),
|
|
2330
|
+
children: "Back"
|
|
2331
|
+
}), /* @__PURE__ */ jsxs(Button, {
|
|
2332
|
+
onClick: handleProceedToPreview,
|
|
2333
|
+
disabled: !isMapValid,
|
|
2334
|
+
children: ["Preview Validation ", /* @__PURE__ */ jsx(ArrowRight, { className: "dy-h-4 dy-w-4 dy-ml-2" })]
|
|
2335
|
+
})]
|
|
2336
|
+
})
|
|
2337
|
+
]
|
|
2338
|
+
}),
|
|
2339
|
+
step === "preview" && /* @__PURE__ */ jsxs("div", {
|
|
2340
|
+
className: "dy-space-y-4",
|
|
2341
|
+
children: [
|
|
2342
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2343
|
+
className: "dy-flex dy-items-center dy-justify-between",
|
|
2344
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
2345
|
+
className: "dy-space-y-1",
|
|
2346
|
+
children: [/* @__PURE__ */ jsx("h3", {
|
|
2347
|
+
className: "dy-text-sm dy-font-semibold",
|
|
2348
|
+
children: "Validation Preview"
|
|
2349
|
+
}), /* @__PURE__ */ jsxs("p", {
|
|
2350
|
+
className: "dy-text-xs dy-text-muted-foreground",
|
|
2351
|
+
children: ["Showing the first 100 rows. ", totalErrors > 0 ? `${totalErrors} rows have errors and will be skipped.` : "All rows are valid."]
|
|
2352
|
+
})]
|
|
2353
|
+
}), totalErrors > 0 && /* @__PURE__ */ jsxs(Badge, {
|
|
2354
|
+
variant: "destructive",
|
|
2355
|
+
className: "dy-rounded-md dy-gap-1.5",
|
|
2356
|
+
children: [
|
|
2357
|
+
/* @__PURE__ */ jsx(AlertTriangle, { className: "dy-h-3.5 dy-w-3.5" }),
|
|
2358
|
+
" ",
|
|
2359
|
+
totalErrors,
|
|
2360
|
+
" Invalid"
|
|
2361
|
+
]
|
|
2362
|
+
})]
|
|
2363
|
+
}),
|
|
2364
|
+
/* @__PURE__ */ jsx(ScrollArea, {
|
|
2365
|
+
className: "dy-h-[300px] dy-border dy-border-border dy-rounded-xl",
|
|
2366
|
+
children: /* @__PURE__ */ jsxs("table", {
|
|
2367
|
+
className: "dy-w-full dy-text-left dy-border-collapse",
|
|
2368
|
+
children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", {
|
|
2369
|
+
className: "dy-border-b dy-border-border dy-bg-muted/40 dy-text-xs dy-font-semibold dy-text-muted-foreground",
|
|
2370
|
+
children: [/* @__PURE__ */ jsx("th", {
|
|
2371
|
+
className: "dy-p-3 dy-w-16",
|
|
2372
|
+
children: "Row"
|
|
2373
|
+
}), importableFields.filter((f) => Object.values(mapping).includes(f.name)).map((f) => /* @__PURE__ */ jsx("th", {
|
|
2374
|
+
className: "dy-p-3",
|
|
2375
|
+
children: f.label || f.name
|
|
2376
|
+
}, f.name))]
|
|
2377
|
+
}) }), /* @__PURE__ */ jsx("tbody", {
|
|
2378
|
+
className: "dy-text-sm",
|
|
2379
|
+
children: previewRows.map((rowResult) => /* @__PURE__ */ jsxs("tr", {
|
|
2380
|
+
className: "dy-border-b dy-border-border hover:dy-bg-muted/20",
|
|
2381
|
+
children: [/* @__PURE__ */ jsx("td", {
|
|
2382
|
+
className: "dy-p-3 dy-font-medium dy-text-muted-foreground",
|
|
2383
|
+
children: rowResult.rowNumber
|
|
2384
|
+
}), importableFields.filter((f) => Object.values(mapping).includes(f.name)).map((f) => {
|
|
2385
|
+
const val = rowResult.data[f.name];
|
|
2386
|
+
const error = rowResult.errors[f.name];
|
|
2387
|
+
return /* @__PURE__ */ jsx("td", {
|
|
2388
|
+
className: "dy-p-3",
|
|
2389
|
+
children: error ? /* @__PURE__ */ jsxs("span", {
|
|
2390
|
+
className: "dy-flex dy-items-center dy-gap-1.5 dy-text-destructive dy-font-medium",
|
|
2391
|
+
children: [/* @__PURE__ */ jsx(AlertCircle, { className: "dy-h-3.5 dy-w-3.5" }), error]
|
|
2392
|
+
}) : String(val ?? "")
|
|
2393
|
+
}, f.name);
|
|
2394
|
+
})]
|
|
2395
|
+
}, rowResult.rowNumber))
|
|
2396
|
+
})]
|
|
2397
|
+
})
|
|
2398
|
+
}),
|
|
2399
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2400
|
+
className: "dy-flex dy-justify-between dy-pt-4",
|
|
2401
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
2402
|
+
variant: "outline",
|
|
2403
|
+
onClick: () => setStep("map"),
|
|
2404
|
+
children: "Back"
|
|
2405
|
+
}), /* @__PURE__ */ jsxs(Button, {
|
|
2406
|
+
onClick: startImport,
|
|
2407
|
+
variant: "default",
|
|
2408
|
+
className: "dy-gap-2",
|
|
2409
|
+
children: [
|
|
2410
|
+
/* @__PURE__ */ jsx(Play, { className: "dy-h-4 dy-w-4" }),
|
|
2411
|
+
" Start Import (",
|
|
2412
|
+
validatedData.length - totalErrors,
|
|
2413
|
+
" Rows)"
|
|
2414
|
+
]
|
|
2415
|
+
})]
|
|
2416
|
+
})
|
|
2417
|
+
]
|
|
2418
|
+
}),
|
|
2419
|
+
step === "importing" && /* @__PURE__ */ jsxs("div", {
|
|
2420
|
+
className: "dy-space-y-6 dy-py-6",
|
|
2421
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
2422
|
+
className: "dy-space-y-2",
|
|
2423
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
2424
|
+
className: "dy-flex dy-items-center dy-justify-between dy-text-sm dy-font-semibold",
|
|
2425
|
+
children: [/* @__PURE__ */ jsx("span", { children: "Importing rows..." }), /* @__PURE__ */ jsxs("span", { children: [
|
|
2426
|
+
processedCount,
|
|
2427
|
+
" / ",
|
|
2428
|
+
totalRows
|
|
2429
|
+
] })]
|
|
2430
|
+
}), /* @__PURE__ */ jsx(Progress, {
|
|
2431
|
+
value: processedCount / totalRows * 100,
|
|
2432
|
+
className: "dy-h-2"
|
|
2433
|
+
})]
|
|
2434
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
2435
|
+
className: "dy-grid dy-grid-cols-2 dy-gap-4",
|
|
2436
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
2437
|
+
className: "dy-p-4 dy-bg-muted/30 dy-border dy-border-border dy-rounded-xl dy-text-center",
|
|
2438
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
2439
|
+
className: "dy-text-2xl dy-font-bold dy-text-primary",
|
|
2440
|
+
children: successCount
|
|
2441
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
2442
|
+
className: "dy-text-xs dy-text-muted-foreground",
|
|
2443
|
+
children: "Successful"
|
|
2444
|
+
})]
|
|
2445
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
2446
|
+
className: "dy-p-4 dy-bg-muted/30 dy-border dy-border-border dy-rounded-xl dy-text-center",
|
|
2447
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
2448
|
+
className: "dy-text-2xl dy-font-bold dy-text-destructive",
|
|
2449
|
+
children: failedRows.length
|
|
2450
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
2451
|
+
className: "dy-text-xs dy-text-muted-foreground",
|
|
2452
|
+
children: "Failed/Skipped"
|
|
2453
|
+
})]
|
|
2454
|
+
})]
|
|
2455
|
+
})]
|
|
2456
|
+
}),
|
|
2457
|
+
step === "complete" && /* @__PURE__ */ jsxs("div", {
|
|
2458
|
+
className: "dy-space-y-6",
|
|
2459
|
+
children: [
|
|
2460
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2461
|
+
className: "dy-flex dy-flex-col dy-items-center dy-text-center dy-space-y-3 dy-py-6",
|
|
2462
|
+
children: [
|
|
2463
|
+
/* @__PURE__ */ jsx(CheckCircle2, { className: "dy-h-12 dy-w-12 dy-text-green-500" }),
|
|
2464
|
+
/* @__PURE__ */ jsx("h3", {
|
|
2465
|
+
className: "dy-text-lg dy-font-semibold",
|
|
2466
|
+
children: "Import Process Completed"
|
|
2467
|
+
}),
|
|
2468
|
+
/* @__PURE__ */ jsxs("p", {
|
|
2469
|
+
className: "dy-text-sm dy-text-muted-foreground dy-max-w-md",
|
|
2470
|
+
children: [
|
|
2471
|
+
"Successfully imported ",
|
|
2472
|
+
successCount,
|
|
2473
|
+
" rows. ",
|
|
2474
|
+
failedRows.length,
|
|
2475
|
+
" rows failed or were skipped due to validation/server errors."
|
|
2476
|
+
]
|
|
2477
|
+
})
|
|
2478
|
+
]
|
|
2479
|
+
}),
|
|
2480
|
+
failedRows.length > 0 && /* @__PURE__ */ jsxs("div", {
|
|
2481
|
+
className: "dy-space-y-3",
|
|
2482
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
2483
|
+
className: "dy-flex dy-items-center dy-justify-between",
|
|
2484
|
+
children: [/* @__PURE__ */ jsxs("p", {
|
|
2485
|
+
className: "dy-text-xs dy-font-bold dy-text-destructive uppercase tracking-wide",
|
|
2486
|
+
children: [
|
|
2487
|
+
"Failure Logs (",
|
|
2488
|
+
failedRows.length,
|
|
2489
|
+
" rows)"
|
|
2490
|
+
]
|
|
2491
|
+
}), /* @__PURE__ */ jsxs(Button, {
|
|
2492
|
+
size: "sm",
|
|
2493
|
+
variant: "outline",
|
|
2494
|
+
className: "dy-h-8 dy-text-xs dy-gap-1.5",
|
|
2495
|
+
onClick: downloadFailedCsv,
|
|
2496
|
+
children: [/* @__PURE__ */ jsx(Download, { className: "dy-h-3.5 dy-w-3.5" }), " Download Errors CSV"]
|
|
2497
|
+
})]
|
|
2498
|
+
}), /* @__PURE__ */ jsx(ScrollArea, {
|
|
2499
|
+
className: "dy-h-[180px] dy-border dy-border-border dy-rounded-xl",
|
|
2500
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
2501
|
+
className: "dy-divide-y dy-divide-border",
|
|
2502
|
+
children: failedRows.map((f, idx) => /* @__PURE__ */ jsxs("div", {
|
|
2503
|
+
className: "dy-flex dy-items-start dy-gap-3 dy-p-3 dy-text-xs",
|
|
2504
|
+
children: [/* @__PURE__ */ jsxs(Badge, {
|
|
2505
|
+
variant: "destructive",
|
|
2506
|
+
className: "dy-rounded dy-shrink-0",
|
|
2507
|
+
children: ["Row ", f.row]
|
|
2508
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
2509
|
+
className: "dy-text-muted-foreground font-medium dy-pt-0.5",
|
|
2510
|
+
children: f.error
|
|
2511
|
+
})]
|
|
2512
|
+
}, idx))
|
|
2513
|
+
})
|
|
2514
|
+
})]
|
|
2515
|
+
}),
|
|
2516
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2517
|
+
className: "dy-flex dy-justify-end dy-gap-2 dy-pt-4",
|
|
2518
|
+
children: [/* @__PURE__ */ jsxs(Button, {
|
|
2519
|
+
variant: "outline",
|
|
2520
|
+
className: "dy-gap-2",
|
|
2521
|
+
onClick: () => {
|
|
2522
|
+
setCsvHeaders([]);
|
|
2523
|
+
setParsedRows([]);
|
|
2524
|
+
setMapping({});
|
|
2525
|
+
setValidatedData([]);
|
|
2526
|
+
setFailedRows([]);
|
|
2527
|
+
setStep("upload");
|
|
2528
|
+
},
|
|
2529
|
+
children: [/* @__PURE__ */ jsx(RotateCcw, { className: "dy-h-4 dy-w-4" }), " Import Another File"]
|
|
2530
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
2531
|
+
onClick: onClose,
|
|
2532
|
+
children: "Close Importer"
|
|
2533
|
+
})]
|
|
2534
|
+
})
|
|
2535
|
+
]
|
|
2536
|
+
})
|
|
2537
|
+
]
|
|
2538
|
+
});
|
|
2539
|
+
}
|
|
2540
|
+
//#endregion
|
|
2541
|
+
//#region src/components/ui/csv-importer-dialog.tsx
|
|
2542
|
+
function CsvImporterDialog({ open, onOpenChange, slug, schema }) {
|
|
2543
|
+
const title = schema.labels?.singular || schema.slug || slug;
|
|
2544
|
+
return /* @__PURE__ */ jsx(Dialog, {
|
|
2545
|
+
open,
|
|
2546
|
+
onOpenChange,
|
|
2547
|
+
children: /* @__PURE__ */ jsxs(DialogContent, {
|
|
2548
|
+
className: "sm:dy-max-w-2xl",
|
|
2549
|
+
children: [/* @__PURE__ */ jsxs(DialogHeader, { children: [/* @__PURE__ */ jsx(DialogTitle, { children: "Import CSV Data" }), /* @__PURE__ */ jsxs(DialogDescription, { children: [
|
|
2550
|
+
"Import records into the ",
|
|
2551
|
+
title,
|
|
2552
|
+
" collection."
|
|
2553
|
+
] })] }), /* @__PURE__ */ jsx(CsvImporter, {
|
|
2554
|
+
slug,
|
|
2555
|
+
schema,
|
|
2556
|
+
onClose: () => onOpenChange(false)
|
|
2557
|
+
})]
|
|
2558
|
+
})
|
|
2559
|
+
});
|
|
2560
|
+
}
|
|
2561
|
+
//#endregion
|
|
1946
2562
|
//#region src/components/ui/render-cell.tsx
|
|
1947
2563
|
function RenderCell({ value, field, client, schemas }) {
|
|
1948
2564
|
if (value === null || value === void 0) return /* @__PURE__ */ jsx("span", {
|
|
@@ -2158,14 +2774,14 @@ function MediaGrid({ items, baseUrl, onDelete, slug }) {
|
|
|
2158
2774
|
}
|
|
2159
2775
|
//#endregion
|
|
2160
2776
|
//#region src/components/ui/sheet.tsx
|
|
2161
|
-
var Sheet =
|
|
2162
|
-
var SheetPortal =
|
|
2163
|
-
var SheetOverlay = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
2777
|
+
var Sheet = DialogPrimitive.Root;
|
|
2778
|
+
var SheetPortal = DialogPrimitive.Portal;
|
|
2779
|
+
var SheetOverlay = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Overlay, {
|
|
2164
2780
|
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
2781
|
...props,
|
|
2166
2782
|
ref
|
|
2167
2783
|
}));
|
|
2168
|
-
SheetOverlay.displayName =
|
|
2784
|
+
SheetOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
|
2169
2785
|
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
2786
|
variants: { side: {
|
|
2171
2787
|
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 +2793,11 @@ var sheetVariants = cva("dy-fixed dy-z-50 dy-gap-4 dy-bg-background dy-p-6 dy-sh
|
|
|
2177
2793
|
});
|
|
2178
2794
|
var SheetContent = React$1.forwardRef(({ side = "right", className, children, ...props }, ref) => /* @__PURE__ */ jsx(SheetPortal, { children: /* @__PURE__ */ jsxs("div", {
|
|
2179
2795
|
className: "dy-admin-ui",
|
|
2180
|
-
children: [/* @__PURE__ */ jsx(SheetOverlay, {}), /* @__PURE__ */ jsxs(
|
|
2796
|
+
children: [/* @__PURE__ */ jsx(SheetOverlay, {}), /* @__PURE__ */ jsxs(DialogPrimitive.Content, {
|
|
2181
2797
|
ref,
|
|
2182
2798
|
className: cn(sheetVariants({ side }), className),
|
|
2183
2799
|
...props,
|
|
2184
|
-
children: [children, /* @__PURE__ */ jsxs(
|
|
2800
|
+
children: [children, /* @__PURE__ */ jsxs(DialogPrimitive.Close, {
|
|
2185
2801
|
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
2802
|
children: [/* @__PURE__ */ jsx(X, { className: "dy-h-4 dy-w-4" }), /* @__PURE__ */ jsx("span", {
|
|
2187
2803
|
className: "dy-sr-only",
|
|
@@ -2190,7 +2806,7 @@ var SheetContent = React$1.forwardRef(({ side = "right", className, children, ..
|
|
|
2190
2806
|
})]
|
|
2191
2807
|
})]
|
|
2192
2808
|
}) }));
|
|
2193
|
-
SheetContent.displayName =
|
|
2809
|
+
SheetContent.displayName = DialogPrimitive.Content.displayName;
|
|
2194
2810
|
var SheetHeader = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
|
|
2195
2811
|
className: cn("dy-flex dy-flex-col dy-space-y-2 dy-text-center sm:dy-text-left", className),
|
|
2196
2812
|
...props
|
|
@@ -2201,18 +2817,18 @@ var SheetFooter = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
|
|
|
2201
2817
|
...props
|
|
2202
2818
|
});
|
|
2203
2819
|
SheetFooter.displayName = "SheetFooter";
|
|
2204
|
-
var SheetTitle = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
2820
|
+
var SheetTitle = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Title, {
|
|
2205
2821
|
ref,
|
|
2206
2822
|
className: cn("dy-text-lg dy-font-semibold dy-text-foreground", className),
|
|
2207
2823
|
...props
|
|
2208
2824
|
}));
|
|
2209
|
-
SheetTitle.displayName =
|
|
2210
|
-
var SheetDescription = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
2825
|
+
SheetTitle.displayName = DialogPrimitive.Title.displayName;
|
|
2826
|
+
var SheetDescription = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(DialogPrimitive.Description, {
|
|
2211
2827
|
ref,
|
|
2212
2828
|
className: cn("dy-text-sm dy-text-muted-foreground", className),
|
|
2213
2829
|
...props
|
|
2214
2830
|
}));
|
|
2215
|
-
SheetDescription.displayName =
|
|
2831
|
+
SheetDescription.displayName = DialogPrimitive.Description.displayName;
|
|
2216
2832
|
//#endregion
|
|
2217
2833
|
//#region src/components/ui/toggle.tsx
|
|
2218
2834
|
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 +2859,6 @@ var Toggle = React$1.forwardRef(({ className, variant, size, ...props }, ref) =>
|
|
|
2243
2859
|
}));
|
|
2244
2860
|
Toggle.displayName = TogglePrimitive.Root.displayName;
|
|
2245
2861
|
//#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
2862
|
//#region src/components/ui/tabs.tsx
|
|
2296
2863
|
var Tabs = TabsPrimitive.Root;
|
|
2297
2864
|
var TabsList = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(TabsPrimitive.List, {
|
|
@@ -2308,34 +2875,10 @@ var TabsTrigger = React$1.forwardRef(({ className, ...props }, ref) => /* @__PUR
|
|
|
2308
2875
|
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
|
|
2309
2876
|
var TabsContent = React$1.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(TabsPrimitive.Content, {
|
|
2310
2877
|
ref,
|
|
2311
|
-
className: cn("dy-mt-2 dy-ring-offset-background focus-visible:dy-outline-none focus-visible:dy-ring-2 focus-visible:dy-ring-ring focus-visible:dy-ring-offset-2", className),
|
|
2312
|
-
...props
|
|
2313
|
-
}));
|
|
2314
|
-
TabsContent.displayName = TabsPrimitive.Content.displayName;
|
|
2315
|
-
//#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" })
|
|
2878
|
+
className: cn("dy-mt-2 dy-ring-offset-background focus-visible:dy-outline-none focus-visible:dy-ring-2 focus-visible:dy-ring-ring focus-visible:dy-ring-offset-2", className),
|
|
2879
|
+
...props
|
|
2337
2880
|
}));
|
|
2338
|
-
|
|
2881
|
+
TabsContent.displayName = TabsPrimitive.Content.displayName;
|
|
2339
2882
|
//#endregion
|
|
2340
2883
|
//#region src/components/media/media-library-dialog.tsx
|
|
2341
2884
|
function MediaLibraryDialog({ collection, isOpen, onOpenChange, selectedValues, onSelect, multiple, onConfirm }) {
|
|
@@ -8724,13 +9267,41 @@ function SpreadsheetEditor({ slug, schema, data, onSave, isSaving }) {
|
|
|
8724
9267
|
}
|
|
8725
9268
|
//#endregion
|
|
8726
9269
|
//#region src/pages/collections/list-page.tsx
|
|
9270
|
+
function SortableColumnItem({ id, label, visible, onToggleVisible }) {
|
|
9271
|
+
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id });
|
|
9272
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
9273
|
+
ref: setNodeRef,
|
|
9274
|
+
style: {
|
|
9275
|
+
transform: CSS.Transform.toString(transform),
|
|
9276
|
+
transition,
|
|
9277
|
+
zIndex: isDragging ? 10 : 1
|
|
9278
|
+
},
|
|
9279
|
+
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"),
|
|
9280
|
+
children: [
|
|
9281
|
+
/* @__PURE__ */ jsx("div", {
|
|
9282
|
+
...attributes,
|
|
9283
|
+
...listeners,
|
|
9284
|
+
className: "dy-cursor-grab active:dy-cursor-grabbing dy-text-muted-foreground/60 hover:dy-text-foreground dy-p-0.5",
|
|
9285
|
+
children: /* @__PURE__ */ jsx(GripVertical, { className: "dy-h-3.5 dy-w-3.5" })
|
|
9286
|
+
}),
|
|
9287
|
+
/* @__PURE__ */ jsx(Checkbox, {
|
|
9288
|
+
checked: visible,
|
|
9289
|
+
onCheckedChange: (val) => onToggleVisible(!!val)
|
|
9290
|
+
}),
|
|
9291
|
+
/* @__PURE__ */ jsx("div", {
|
|
9292
|
+
className: "dy-flex-1 dy-text-xs dy-font-medium dy-text-foreground",
|
|
9293
|
+
children: label
|
|
9294
|
+
})
|
|
9295
|
+
]
|
|
9296
|
+
});
|
|
9297
|
+
}
|
|
8727
9298
|
function CollectionListPage({ slug }) {
|
|
8728
9299
|
const { client, components, user } = useDyrected();
|
|
8729
9300
|
const queryClient = useQueryClient();
|
|
8730
9301
|
const [page, setPage] = React$1.useState(1);
|
|
8731
9302
|
const [rowSelection, setRowSelection] = React$1.useState({});
|
|
9303
|
+
const [isImportOpen, setIsImportOpen] = React$1.useState(false);
|
|
8732
9304
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
8733
|
-
const [viewMode, setViewMode] = React$1.useState("list");
|
|
8734
9305
|
const whereParam = searchParams.get("where");
|
|
8735
9306
|
const rules = React$1.useMemo(() => {
|
|
8736
9307
|
if (!whereParam) return [];
|
|
@@ -8760,6 +9331,145 @@ function CollectionListPage({ slug }) {
|
|
|
8760
9331
|
enabled: !!client
|
|
8761
9332
|
});
|
|
8762
9333
|
const schema = schemas?.collections.find((c) => c.slug === slug);
|
|
9334
|
+
const allAvailableColumns = React$1.useMemo(() => {
|
|
9335
|
+
if (!schema) return [];
|
|
9336
|
+
return [
|
|
9337
|
+
...schema.fields.filter((f) => f.name !== "password" && !f.admin?.hidden && f.type !== "row" && f.type !== "join").map((field) => field.name),
|
|
9338
|
+
"id",
|
|
9339
|
+
"createdAt",
|
|
9340
|
+
"updatedAt"
|
|
9341
|
+
].filter((name, index, self) => self.indexOf(name) === index);
|
|
9342
|
+
}, [schema]);
|
|
9343
|
+
const defaultListColumns = React$1.useMemo(() => {
|
|
9344
|
+
if (!schema) return [];
|
|
9345
|
+
const allDisplayFields = schema.fields.filter((f) => f.name !== "password" && !f.admin?.hidden && f.type !== "row" && f.type !== "join");
|
|
9346
|
+
const configuredColumns = Array.isArray(schema.admin?.defaultColumns) ? schema.admin.defaultColumns : [];
|
|
9347
|
+
return configuredColumns.length > 0 ? configuredColumns : allDisplayFields.slice(0, 3).map((field) => field.name);
|
|
9348
|
+
}, [schema]);
|
|
9349
|
+
const defaultListPreference = React$1.useMemo(() => {
|
|
9350
|
+
const visibleSet = new Set(defaultListColumns);
|
|
9351
|
+
return {
|
|
9352
|
+
viewMode: "list",
|
|
9353
|
+
columns: allAvailableColumns.map((name) => ({
|
|
9354
|
+
name,
|
|
9355
|
+
visible: visibleSet.has(name)
|
|
9356
|
+
}))
|
|
9357
|
+
};
|
|
9358
|
+
}, [allAvailableColumns, defaultListColumns]);
|
|
9359
|
+
const prefKey = `layout:collections:${slug}:list`;
|
|
9360
|
+
const { data: rawPreference } = useQuery({
|
|
9361
|
+
queryKey: ["preferences", prefKey],
|
|
9362
|
+
queryFn: async () => {
|
|
9363
|
+
if (!client) return null;
|
|
9364
|
+
return (await client.getPreference(prefKey)).value;
|
|
9365
|
+
},
|
|
9366
|
+
enabled: !!client,
|
|
9367
|
+
staleTime: 5e3,
|
|
9368
|
+
refetchOnWindowFocus: true
|
|
9369
|
+
});
|
|
9370
|
+
const reconciledPreference = React$1.useMemo(() => {
|
|
9371
|
+
if (!rawPreference) return defaultListPreference;
|
|
9372
|
+
let parsedViewMode = "list";
|
|
9373
|
+
let parsedColumns = [];
|
|
9374
|
+
if (Array.isArray(rawPreference)) {
|
|
9375
|
+
parsedViewMode = "list";
|
|
9376
|
+
parsedColumns = rawPreference.map((item) => {
|
|
9377
|
+
if (typeof item === "string") return {
|
|
9378
|
+
name: item,
|
|
9379
|
+
visible: true
|
|
9380
|
+
};
|
|
9381
|
+
if (item && typeof item === "object" && "name" in item) {
|
|
9382
|
+
const obj = item;
|
|
9383
|
+
return {
|
|
9384
|
+
name: obj.name,
|
|
9385
|
+
visible: obj.visible !== false
|
|
9386
|
+
};
|
|
9387
|
+
}
|
|
9388
|
+
return null;
|
|
9389
|
+
}).filter((x) => x !== null);
|
|
9390
|
+
} else if (rawPreference && typeof rawPreference === "object") {
|
|
9391
|
+
const rawPrefObj = rawPreference;
|
|
9392
|
+
parsedViewMode = rawPrefObj.viewMode === "spreadsheet" ? "spreadsheet" : "list";
|
|
9393
|
+
if (Array.isArray(rawPrefObj.columns)) parsedColumns = rawPrefObj.columns.map((item) => {
|
|
9394
|
+
if (typeof item === "string") return {
|
|
9395
|
+
name: item,
|
|
9396
|
+
visible: true
|
|
9397
|
+
};
|
|
9398
|
+
if (item && typeof item === "object" && "name" in item) {
|
|
9399
|
+
const obj = item;
|
|
9400
|
+
return {
|
|
9401
|
+
name: obj.name,
|
|
9402
|
+
visible: obj.visible !== false
|
|
9403
|
+
};
|
|
9404
|
+
}
|
|
9405
|
+
return null;
|
|
9406
|
+
}).filter((x) => x !== null);
|
|
9407
|
+
}
|
|
9408
|
+
const validColumns = parsedColumns.filter((col) => allAvailableColumns.includes(col.name));
|
|
9409
|
+
const validNames = validColumns.map((col) => col.name);
|
|
9410
|
+
const missingColumns = allAvailableColumns.filter((name) => !validNames.includes(name)).map((name) => ({
|
|
9411
|
+
name,
|
|
9412
|
+
visible: false
|
|
9413
|
+
}));
|
|
9414
|
+
return {
|
|
9415
|
+
viewMode: parsedViewMode,
|
|
9416
|
+
columns: [...validColumns, ...missingColumns]
|
|
9417
|
+
};
|
|
9418
|
+
}, [
|
|
9419
|
+
rawPreference,
|
|
9420
|
+
defaultListPreference,
|
|
9421
|
+
allAvailableColumns
|
|
9422
|
+
]);
|
|
9423
|
+
const [localPreference, setLocalPreference] = React$1.useState(reconciledPreference);
|
|
9424
|
+
const syncPreference = React$1.useState(() => {
|
|
9425
|
+
return (next) => {
|
|
9426
|
+
setLocalPreference((prev) => {
|
|
9427
|
+
const isViewModeSame = prev.viewMode === next.viewMode;
|
|
9428
|
+
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);
|
|
9429
|
+
return isViewModeSame && isColsSame ? prev : next;
|
|
9430
|
+
});
|
|
9431
|
+
};
|
|
9432
|
+
})[0];
|
|
9433
|
+
React$1.useEffect(() => {
|
|
9434
|
+
syncPreference(reconciledPreference);
|
|
9435
|
+
}, [reconciledPreference, syncPreference]);
|
|
9436
|
+
const savePreferenceMutation = useMutation({
|
|
9437
|
+
mutationFn: async ({ scope, value }) => {
|
|
9438
|
+
if (!client) throw new Error("Client not available");
|
|
9439
|
+
await client.setPreference(prefKey, value, { scope });
|
|
9440
|
+
},
|
|
9441
|
+
onSuccess: () => {
|
|
9442
|
+
queryClient.invalidateQueries({ queryKey: ["preferences", prefKey] });
|
|
9443
|
+
}
|
|
9444
|
+
});
|
|
9445
|
+
const resetPreferenceMutation = useMutation({
|
|
9446
|
+
mutationFn: async ({ scope }) => {
|
|
9447
|
+
if (!client) throw new Error("Client not available");
|
|
9448
|
+
await client.deletePreference(prefKey, { scope });
|
|
9449
|
+
},
|
|
9450
|
+
onSuccess: () => {
|
|
9451
|
+
queryClient.invalidateQueries({ queryKey: ["preferences", prefKey] });
|
|
9452
|
+
}
|
|
9453
|
+
});
|
|
9454
|
+
const saveColumns = (scope) => savePreferenceMutation.mutateAsync({
|
|
9455
|
+
scope,
|
|
9456
|
+
value: localPreference
|
|
9457
|
+
});
|
|
9458
|
+
const resetColumns = () => resetPreferenceMutation.mutateAsync({ scope: "personal" });
|
|
9459
|
+
const dndSensors = useSensors(useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }));
|
|
9460
|
+
const handleDndDragEnd = (event) => {
|
|
9461
|
+
const { active, over } = event;
|
|
9462
|
+
if (!over || active.id === over.id) return;
|
|
9463
|
+
setLocalPreference((prev) => {
|
|
9464
|
+
const oldIndex = prev.columns.findIndex((c) => c.name === active.id);
|
|
9465
|
+
const newIndex = prev.columns.findIndex((c) => c.name === over.id);
|
|
9466
|
+
if (oldIndex === -1 || newIndex === -1) return prev;
|
|
9467
|
+
return {
|
|
9468
|
+
...prev,
|
|
9469
|
+
columns: arrayMove(prev.columns, oldIndex, newIndex)
|
|
9470
|
+
};
|
|
9471
|
+
});
|
|
9472
|
+
};
|
|
8763
9473
|
const { data: response, isLoading } = useQuery({
|
|
8764
9474
|
queryKey: [
|
|
8765
9475
|
"collection",
|
|
@@ -8908,18 +9618,9 @@ function CollectionListPage({ slug }) {
|
|
|
8908
9618
|
if (!schema) return [];
|
|
8909
9619
|
const allDisplayFields = schema.fields.filter((f) => f.name !== "password" && !f.admin?.hidden && f.type !== "row" && f.type !== "join");
|
|
8910
9620
|
const fieldByName = new Map(allDisplayFields.map((field) => [field.name, field]));
|
|
8911
|
-
const configuredColumns =
|
|
9621
|
+
const configuredColumns = localPreference.columns.filter((col) => col.visible).map((col) => col.name);
|
|
8912
9622
|
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);
|
|
9623
|
+
const allColumnNames = visibleColumnNames;
|
|
8923
9624
|
const firstVisibleFieldName = visibleColumnNames.find((name) => fieldByName.has(name));
|
|
8924
9625
|
const titleFieldName = visibleColumnNames.includes(schema.admin?.useAsTitle || "") ? schema.admin?.useAsTitle : firstVisibleFieldName;
|
|
8925
9626
|
const deleteAccess = schema.access?.delete;
|
|
@@ -9041,29 +9742,9 @@ function CollectionListPage({ slug }) {
|
|
|
9041
9742
|
user,
|
|
9042
9743
|
handleDelete,
|
|
9043
9744
|
slug,
|
|
9044
|
-
schemas
|
|
9745
|
+
schemas,
|
|
9746
|
+
localPreference.columns
|
|
9045
9747
|
]);
|
|
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
9748
|
if (!schema) return /* @__PURE__ */ jsxs("div", { children: ["Collection not found: ", slug] });
|
|
9068
9749
|
const readAccess = schema.access?.read;
|
|
9069
9750
|
let canRead = true;
|
|
@@ -9200,63 +9881,139 @@ function CollectionListPage({ slug }) {
|
|
|
9200
9881
|
icon: Database,
|
|
9201
9882
|
children: /* @__PURE__ */ jsxs("div", {
|
|
9202
9883
|
className: "dy-flex dy-items-center dy-gap-2 dy-w-full sm:dy-w-auto",
|
|
9203
|
-
children: [/* @__PURE__ */ jsxs(
|
|
9884
|
+
children: [/* @__PURE__ */ jsxs(Popover, { children: [/* @__PURE__ */ jsx(PopoverTrigger, {
|
|
9204
9885
|
asChild: true,
|
|
9205
9886
|
children: /* @__PURE__ */ jsxs(Button, {
|
|
9206
9887
|
variant: "outline",
|
|
9207
9888
|
size: "sm",
|
|
9208
9889
|
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
|
-
]
|
|
9890
|
+
children: [/* @__PURE__ */ jsx(Settings2, { className: "dy-h-3.5 dy-w-3.5" }), /* @__PURE__ */ jsx("span", { children: "View Settings" })]
|
|
9214
9891
|
})
|
|
9215
|
-
}), /* @__PURE__ */ jsxs(
|
|
9892
|
+
}), /* @__PURE__ */ jsxs(PopoverContent, {
|
|
9216
9893
|
align: "end",
|
|
9217
|
-
className: "dy-w-
|
|
9894
|
+
className: "dy-w-80 dy-p-4 dy-space-y-4",
|
|
9218
9895
|
children: [
|
|
9219
|
-
/* @__PURE__ */
|
|
9220
|
-
className: "dy-
|
|
9221
|
-
children: "
|
|
9222
|
-
|
|
9223
|
-
|
|
9224
|
-
|
|
9225
|
-
|
|
9226
|
-
|
|
9227
|
-
|
|
9228
|
-
|
|
9229
|
-
|
|
9230
|
-
|
|
9231
|
-
|
|
9232
|
-
|
|
9233
|
-
|
|
9234
|
-
|
|
9235
|
-
|
|
9236
|
-
|
|
9237
|
-
|
|
9896
|
+
/* @__PURE__ */ jsxs("div", {
|
|
9897
|
+
className: "dy-space-y-1.5",
|
|
9898
|
+
children: [/* @__PURE__ */ jsx("h4", {
|
|
9899
|
+
className: "dy-text-xs dy-font-semibold dy-text-foreground dy-text-left",
|
|
9900
|
+
children: "Layout Mode"
|
|
9901
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
9902
|
+
className: "dy-grid dy-grid-cols-2 dy-gap-2",
|
|
9903
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
9904
|
+
size: "sm",
|
|
9905
|
+
variant: localPreference.viewMode === "list" ? "default" : "outline",
|
|
9906
|
+
className: "dy-h-8 dy-text-xs",
|
|
9907
|
+
onClick: () => setLocalPreference((prev) => ({
|
|
9908
|
+
...prev,
|
|
9909
|
+
viewMode: "list"
|
|
9910
|
+
})),
|
|
9911
|
+
children: "List View"
|
|
9912
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
9913
|
+
size: "sm",
|
|
9914
|
+
variant: localPreference.viewMode === "spreadsheet" ? "default" : "outline",
|
|
9915
|
+
className: "dy-h-8 dy-text-xs",
|
|
9916
|
+
onClick: () => setLocalPreference((prev) => ({
|
|
9917
|
+
...prev,
|
|
9918
|
+
viewMode: "spreadsheet"
|
|
9919
|
+
})),
|
|
9920
|
+
children: "Spreadsheet"
|
|
9238
9921
|
})]
|
|
9239
|
-
})
|
|
9922
|
+
})]
|
|
9240
9923
|
}),
|
|
9241
|
-
/* @__PURE__ */ jsxs(
|
|
9242
|
-
|
|
9243
|
-
|
|
9924
|
+
/* @__PURE__ */ jsxs("div", {
|
|
9925
|
+
className: "dy-space-y-1.5",
|
|
9926
|
+
children: [
|
|
9927
|
+
/* @__PURE__ */ jsx("h4", {
|
|
9928
|
+
className: "dy-text-xs dy-font-semibold dy-text-foreground dy-text-left",
|
|
9929
|
+
children: "Columns"
|
|
9930
|
+
}),
|
|
9931
|
+
/* @__PURE__ */ jsx("p", {
|
|
9932
|
+
className: "dy-text-[10px] dy-text-muted-foreground dy-text-left",
|
|
9933
|
+
children: "Reorder and toggle column visibility."
|
|
9934
|
+
}),
|
|
9935
|
+
/* @__PURE__ */ jsx("div", {
|
|
9936
|
+
className: "dy-max-h-[200px] dy-overflow-y-auto dy-space-y-1 dy-pr-1 dy-outline-none",
|
|
9937
|
+
children: /* @__PURE__ */ jsx(DndContext, {
|
|
9938
|
+
sensors: dndSensors,
|
|
9939
|
+
collisionDetection: closestCenter,
|
|
9940
|
+
onDragEnd: handleDndDragEnd,
|
|
9941
|
+
children: /* @__PURE__ */ jsx(SortableContext, {
|
|
9942
|
+
items: localPreference.columns.map((c) => c.name),
|
|
9943
|
+
strategy: verticalListSortingStrategy,
|
|
9944
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
9945
|
+
className: "dy-space-y-1",
|
|
9946
|
+
children: localPreference.columns.map((col) => {
|
|
9947
|
+
let label = col.name;
|
|
9948
|
+
if (col.name === "id") label = "ID";
|
|
9949
|
+
else if (col.name === "createdAt") label = "Created";
|
|
9950
|
+
else if (col.name === "updatedAt") label = "Last Updated";
|
|
9951
|
+
else {
|
|
9952
|
+
const field = schema.fields.find((f) => f.name === col.name);
|
|
9953
|
+
if (field) label = field.label || field.name;
|
|
9954
|
+
}
|
|
9955
|
+
return /* @__PURE__ */ jsx(SortableColumnItem, {
|
|
9956
|
+
id: col.name,
|
|
9957
|
+
label,
|
|
9958
|
+
visible: col.visible,
|
|
9959
|
+
onToggleVisible: (visible) => {
|
|
9960
|
+
setLocalPreference((prev) => ({
|
|
9961
|
+
...prev,
|
|
9962
|
+
columns: prev.columns.map((c) => c.name === col.name ? {
|
|
9963
|
+
...c,
|
|
9964
|
+
visible
|
|
9965
|
+
} : c)
|
|
9966
|
+
}));
|
|
9967
|
+
}
|
|
9968
|
+
}, col.name);
|
|
9969
|
+
})
|
|
9970
|
+
})
|
|
9971
|
+
})
|
|
9972
|
+
})
|
|
9973
|
+
})
|
|
9974
|
+
]
|
|
9975
|
+
}),
|
|
9976
|
+
/* @__PURE__ */ jsxs("div", {
|
|
9977
|
+
className: "dy-pt-2 dy-border-t dy-border-border/60 dy-flex dy-flex-col dy-gap-2",
|
|
9244
9978
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
9245
|
-
className: "dy-
|
|
9246
|
-
children: [/* @__PURE__ */ jsx(
|
|
9247
|
-
|
|
9248
|
-
|
|
9249
|
-
|
|
9250
|
-
|
|
9251
|
-
|
|
9252
|
-
|
|
9253
|
-
|
|
9254
|
-
|
|
9979
|
+
className: "dy-grid dy-grid-cols-2 dy-gap-2",
|
|
9980
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
9981
|
+
size: "sm",
|
|
9982
|
+
className: "dy-h-7 dy-text-[10px]",
|
|
9983
|
+
onClick: () => {
|
|
9984
|
+
saveColumns("personal");
|
|
9985
|
+
toast.success("Saved personal view preferences");
|
|
9986
|
+
},
|
|
9987
|
+
children: "Save for Me"
|
|
9988
|
+
}), user?.role === "admin" && /* @__PURE__ */ jsx(Button, {
|
|
9989
|
+
size: "sm",
|
|
9990
|
+
variant: "secondary",
|
|
9991
|
+
className: "dy-h-7 dy-text-[10px]",
|
|
9992
|
+
onClick: () => {
|
|
9993
|
+
saveColumns("global");
|
|
9994
|
+
toast.success("Saved global view preferences");
|
|
9995
|
+
},
|
|
9996
|
+
children: "Save for Everyone"
|
|
9255
9997
|
})]
|
|
9256
|
-
}),
|
|
9998
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
9999
|
+
size: "sm",
|
|
10000
|
+
variant: "outline",
|
|
10001
|
+
className: "dy-h-7 dy-text-[10px] dy-w-full",
|
|
10002
|
+
onClick: () => {
|
|
10003
|
+
resetColumns();
|
|
10004
|
+
toast.success("Reset view preferences to default");
|
|
10005
|
+
},
|
|
10006
|
+
children: "Reset to Default"
|
|
10007
|
+
})]
|
|
9257
10008
|
})
|
|
9258
10009
|
]
|
|
9259
|
-
})] }), canCreate && /* @__PURE__ */
|
|
10010
|
+
})] }), canCreate && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(Button, {
|
|
10011
|
+
variant: "outline",
|
|
10012
|
+
size: "sm",
|
|
10013
|
+
className: "dy-h-8 dy-px-3 dy-gap-1.5 dy-text-xs dy-w-full sm:dy-w-auto",
|
|
10014
|
+
onClick: () => setIsImportOpen(true),
|
|
10015
|
+
children: [/* @__PURE__ */ jsx(FileUp, { className: "dy-h-3.5 dy-w-3.5" }), /* @__PURE__ */ jsx("span", { children: "Import CSV" })]
|
|
10016
|
+
}), /* @__PURE__ */ jsx(Link, {
|
|
9260
10017
|
to: `/collections/${slug}/new`,
|
|
9261
10018
|
className: "dy-w-full sm:dy-w-auto",
|
|
9262
10019
|
children: /* @__PURE__ */ jsxs(Button, {
|
|
@@ -9273,7 +10030,7 @@ function CollectionListPage({ slug }) {
|
|
|
9273
10030
|
})
|
|
9274
10031
|
]
|
|
9275
10032
|
})
|
|
9276
|
-
})]
|
|
10033
|
+
})] })]
|
|
9277
10034
|
})
|
|
9278
10035
|
}),
|
|
9279
10036
|
/* @__PURE__ */ jsx(AdminComponentSlot, {
|
|
@@ -9288,7 +10045,7 @@ function CollectionListPage({ slug }) {
|
|
|
9288
10045
|
isLoading ? /* @__PURE__ */ jsx("div", {
|
|
9289
10046
|
className: "dy-flex dy-h-[400px] dy-items-center dy-justify-center",
|
|
9290
10047
|
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, {
|
|
10048
|
+
}) : localPreference.viewMode === "spreadsheet" ? /* @__PURE__ */ jsx(SpreadsheetEditor, {
|
|
9292
10049
|
slug,
|
|
9293
10050
|
schema,
|
|
9294
10051
|
data: response?.docs || [],
|
|
@@ -9305,8 +10062,7 @@ function CollectionListPage({ slug }) {
|
|
|
9305
10062
|
searchKey: schema.admin?.useAsTitle || schema.fields.find((f) => !f.admin?.hidden)?.name || "id",
|
|
9306
10063
|
onRowSelectionChange: setRowSelection,
|
|
9307
10064
|
rowSelection,
|
|
9308
|
-
|
|
9309
|
-
initialColumnVisibility,
|
|
10065
|
+
hideViewButton: true,
|
|
9310
10066
|
toolbarActions: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(FilterBuilder, {
|
|
9311
10067
|
schema,
|
|
9312
10068
|
rules,
|
|
@@ -9383,6 +10139,12 @@ function CollectionListPage({ slug }) {
|
|
|
9383
10139
|
componentProps: collectionComponentProps
|
|
9384
10140
|
})
|
|
9385
10141
|
]
|
|
10142
|
+
}),
|
|
10143
|
+
/* @__PURE__ */ jsx(CsvImporterDialog, {
|
|
10144
|
+
open: isImportOpen,
|
|
10145
|
+
onOpenChange: setIsImportOpen,
|
|
10146
|
+
slug,
|
|
10147
|
+
schema
|
|
9386
10148
|
})
|
|
9387
10149
|
]
|
|
9388
10150
|
});
|
|
@@ -9744,7 +10506,172 @@ function WorkflowPanel({ collection, documentId, workflowMeta, workflowConfig })
|
|
|
9744
10506
|
});
|
|
9745
10507
|
}
|
|
9746
10508
|
//#endregion
|
|
10509
|
+
//#region src/hooks/useLayoutPreference.ts
|
|
10510
|
+
function useLayoutPreference({ key, defaultKeys }) {
|
|
10511
|
+
const { client } = useDyrected();
|
|
10512
|
+
const queryClient = useQueryClient();
|
|
10513
|
+
const { data, isLoading } = useQuery({
|
|
10514
|
+
queryKey: ["preferences", key],
|
|
10515
|
+
queryFn: async () => {
|
|
10516
|
+
if (!client) return null;
|
|
10517
|
+
return (await client.getPreference(key)).value;
|
|
10518
|
+
},
|
|
10519
|
+
enabled: !!client,
|
|
10520
|
+
staleTime: 5e3,
|
|
10521
|
+
refetchOnWindowFocus: true
|
|
10522
|
+
});
|
|
10523
|
+
const reconciledLayout = useMemo(() => {
|
|
10524
|
+
const isStringArray = defaultKeys.every((k) => typeof k === "string");
|
|
10525
|
+
if (!data || !Array.isArray(data)) return defaultKeys;
|
|
10526
|
+
if (isStringArray) {
|
|
10527
|
+
const normalizedData = data.map((k) => typeof k === "string" ? k : k?.name || "").filter(Boolean);
|
|
10528
|
+
const defaultStrings = defaultKeys;
|
|
10529
|
+
const validKeys = normalizedData.filter((k) => defaultStrings.includes(k));
|
|
10530
|
+
const missingKeys = defaultStrings.filter((k) => !validKeys.includes(k));
|
|
10531
|
+
return [...validKeys, ...missingKeys];
|
|
10532
|
+
} else {
|
|
10533
|
+
const normalizedData = data.map((item) => {
|
|
10534
|
+
if (typeof item === "string") return { name: item };
|
|
10535
|
+
if (item && typeof item === "object" && "name" in item && typeof item.name === "string") return {
|
|
10536
|
+
name: item.name,
|
|
10537
|
+
width: item.width
|
|
10538
|
+
};
|
|
10539
|
+
return { name: "" };
|
|
10540
|
+
}).filter((item) => item.name !== "");
|
|
10541
|
+
const defaultItems = defaultKeys;
|
|
10542
|
+
const defaultNames = defaultItems.map((item) => item.name);
|
|
10543
|
+
const defaultMap = new Map(defaultItems.map((item) => [item.name, item]));
|
|
10544
|
+
const validItems = normalizedData.filter((item) => defaultNames.includes(item.name));
|
|
10545
|
+
const validNames = validItems.map((item) => item.name);
|
|
10546
|
+
const missingItems = defaultNames.filter((name) => !validNames.includes(name)).map((name) => defaultMap.get(name));
|
|
10547
|
+
return [...validItems.map((item) => {
|
|
10548
|
+
const def = defaultMap.get(item.name);
|
|
10549
|
+
return {
|
|
10550
|
+
...item,
|
|
10551
|
+
width: item.width || def?.width
|
|
10552
|
+
};
|
|
10553
|
+
}), ...missingItems];
|
|
10554
|
+
}
|
|
10555
|
+
}, [data, defaultKeys]);
|
|
10556
|
+
const [localLayout, setLocalLayout] = useState(reconciledLayout);
|
|
10557
|
+
const syncLayout = useState(() => {
|
|
10558
|
+
return (next) => {
|
|
10559
|
+
setLocalLayout((prev) => {
|
|
10560
|
+
return prev.length === next.length && prev.every((v, i) => {
|
|
10561
|
+
if (typeof v === "string") return v === next[i];
|
|
10562
|
+
return v.name === next[i].name && v.width === next[i].width;
|
|
10563
|
+
}) ? prev : next;
|
|
10564
|
+
});
|
|
10565
|
+
};
|
|
10566
|
+
})[0];
|
|
10567
|
+
useEffect(() => {
|
|
10568
|
+
syncLayout(reconciledLayout);
|
|
10569
|
+
}, [reconciledLayout, syncLayout]);
|
|
10570
|
+
const saveMutation = useMutation({
|
|
10571
|
+
mutationFn: async ({ scope, value }) => {
|
|
10572
|
+
if (!client) throw new Error("Client not available");
|
|
10573
|
+
await client.setPreference(key, value, { scope });
|
|
10574
|
+
},
|
|
10575
|
+
onSuccess: () => {
|
|
10576
|
+
queryClient.invalidateQueries({ queryKey: ["preferences", key] });
|
|
10577
|
+
}
|
|
10578
|
+
});
|
|
10579
|
+
const resetMutation = useMutation({
|
|
10580
|
+
mutationFn: async ({ scope }) => {
|
|
10581
|
+
if (!client) throw new Error("Client not available");
|
|
10582
|
+
await client.deletePreference(key, { scope });
|
|
10583
|
+
},
|
|
10584
|
+
onSuccess: () => {
|
|
10585
|
+
queryClient.invalidateQueries({ queryKey: ["preferences", key] });
|
|
10586
|
+
}
|
|
10587
|
+
});
|
|
10588
|
+
return {
|
|
10589
|
+
layout: localLayout,
|
|
10590
|
+
setLayout: setLocalLayout,
|
|
10591
|
+
reconciledLayout,
|
|
10592
|
+
saveLayout: (scope) => saveMutation.mutateAsync({
|
|
10593
|
+
scope,
|
|
10594
|
+
value: localLayout
|
|
10595
|
+
}),
|
|
10596
|
+
resetLayout: (scope) => resetMutation.mutateAsync({ scope }),
|
|
10597
|
+
isLoading,
|
|
10598
|
+
isSaving: saveMutation.isPending,
|
|
10599
|
+
isResetting: resetMutation.isPending
|
|
10600
|
+
};
|
|
10601
|
+
}
|
|
10602
|
+
//#endregion
|
|
9747
10603
|
//#region src/pages/collections/edit-page.tsx
|
|
10604
|
+
function SortableFieldItem({ id, label, type, width, onChangeWidth }) {
|
|
10605
|
+
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id });
|
|
10606
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
10607
|
+
ref: setNodeRef,
|
|
10608
|
+
style: {
|
|
10609
|
+
transform: CSS.Transform.toString(transform),
|
|
10610
|
+
transition,
|
|
10611
|
+
zIndex: isDragging ? 10 : 1
|
|
10612
|
+
},
|
|
10613
|
+
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"),
|
|
10614
|
+
children: [
|
|
10615
|
+
/* @__PURE__ */ jsx("div", {
|
|
10616
|
+
...attributes,
|
|
10617
|
+
...listeners,
|
|
10618
|
+
className: "dy-cursor-grab active:dy-cursor-grabbing dy-text-muted-foreground/60 hover:dy-text-foreground dy-p-1",
|
|
10619
|
+
children: /* @__PURE__ */ jsx(GripVertical, { className: "dy-h-4 dy-w-4" })
|
|
10620
|
+
}),
|
|
10621
|
+
/* @__PURE__ */ jsxs("div", {
|
|
10622
|
+
className: "dy-flex-1 dy-min-w-0",
|
|
10623
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
10624
|
+
className: "dy-text-sm dy-font-semibold dy-text-foreground dy-truncate",
|
|
10625
|
+
children: label
|
|
10626
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
10627
|
+
className: "dy-text-xs dy-text-muted-foreground",
|
|
10628
|
+
children: [
|
|
10629
|
+
id,
|
|
10630
|
+
" • ",
|
|
10631
|
+
/* @__PURE__ */ jsx("span", {
|
|
10632
|
+
className: "dy-uppercase",
|
|
10633
|
+
children: type
|
|
10634
|
+
})
|
|
10635
|
+
]
|
|
10636
|
+
})]
|
|
10637
|
+
}),
|
|
10638
|
+
/* @__PURE__ */ jsx("div", {
|
|
10639
|
+
className: "dy-shrink-0",
|
|
10640
|
+
children: /* @__PURE__ */ jsxs("select", {
|
|
10641
|
+
value: width || "100%",
|
|
10642
|
+
onChange: (e) => onChangeWidth(e.target.value),
|
|
10643
|
+
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",
|
|
10644
|
+
children: [
|
|
10645
|
+
/* @__PURE__ */ jsx("option", {
|
|
10646
|
+
value: "25%",
|
|
10647
|
+
children: "25%"
|
|
10648
|
+
}),
|
|
10649
|
+
/* @__PURE__ */ jsx("option", {
|
|
10650
|
+
value: "33.33%",
|
|
10651
|
+
children: "33%"
|
|
10652
|
+
}),
|
|
10653
|
+
/* @__PURE__ */ jsx("option", {
|
|
10654
|
+
value: "50%",
|
|
10655
|
+
children: "50%"
|
|
10656
|
+
}),
|
|
10657
|
+
/* @__PURE__ */ jsx("option", {
|
|
10658
|
+
value: "66.66%",
|
|
10659
|
+
children: "66%"
|
|
10660
|
+
}),
|
|
10661
|
+
/* @__PURE__ */ jsx("option", {
|
|
10662
|
+
value: "75%",
|
|
10663
|
+
children: "75%"
|
|
10664
|
+
}),
|
|
10665
|
+
/* @__PURE__ */ jsx("option", {
|
|
10666
|
+
value: "100%",
|
|
10667
|
+
children: "100%"
|
|
10668
|
+
})
|
|
10669
|
+
]
|
|
10670
|
+
})
|
|
10671
|
+
})
|
|
10672
|
+
]
|
|
10673
|
+
});
|
|
10674
|
+
}
|
|
9748
10675
|
function EditEntryPage() {
|
|
9749
10676
|
const { slug, id } = useParams();
|
|
9750
10677
|
const [searchParams] = useSearchParams();
|
|
@@ -9755,6 +10682,77 @@ function EditEntryPage() {
|
|
|
9755
10682
|
const [isDirty, setIsDirty] = useState(false);
|
|
9756
10683
|
const [previewData, setPreviewData] = useState(null);
|
|
9757
10684
|
const isEdit = !!id;
|
|
10685
|
+
const [isConfiguringView, setIsConfiguringView] = useState(false);
|
|
10686
|
+
const { data: schemas } = useQuery({
|
|
10687
|
+
queryKey: ["schemas"],
|
|
10688
|
+
queryFn: () => client.getSchemas(),
|
|
10689
|
+
enabled: !!client
|
|
10690
|
+
});
|
|
10691
|
+
const schema = schemas?.collections.find((c) => c.slug === slug);
|
|
10692
|
+
const fieldsList = useMemo(() => {
|
|
10693
|
+
if (!schema) return [];
|
|
10694
|
+
let fields = [...schema.fields];
|
|
10695
|
+
if (schema.upload) {
|
|
10696
|
+
const hasAlt = fields.some((f) => f.name === "alt");
|
|
10697
|
+
const hasCaption = fields.some((f) => f.name === "caption");
|
|
10698
|
+
const mediaFields = [];
|
|
10699
|
+
if (!hasAlt) mediaFields.push({
|
|
10700
|
+
name: "alt",
|
|
10701
|
+
type: "text",
|
|
10702
|
+
label: "Alt Text",
|
|
10703
|
+
admin: { description: "Describe the image for accessibility/screen readers." }
|
|
10704
|
+
});
|
|
10705
|
+
if (!hasCaption) mediaFields.push({
|
|
10706
|
+
name: "caption",
|
|
10707
|
+
type: "textarea",
|
|
10708
|
+
label: "Caption",
|
|
10709
|
+
admin: { description: "Add a caption/description for this media file." }
|
|
10710
|
+
});
|
|
10711
|
+
fields = [...mediaFields, ...fields];
|
|
10712
|
+
}
|
|
10713
|
+
return fields;
|
|
10714
|
+
}, [schema]);
|
|
10715
|
+
const defaultKeys = useMemo(() => {
|
|
10716
|
+
return fieldsList.filter((f) => !!f.name).map((f) => ({
|
|
10717
|
+
name: f.name,
|
|
10718
|
+
width: f.admin?.width || "100%"
|
|
10719
|
+
}));
|
|
10720
|
+
}, [fieldsList]);
|
|
10721
|
+
const { layout, setLayout, saveLayout, resetLayout, reconciledLayout, isLoading: isPreferenceLoading } = useLayoutPreference({
|
|
10722
|
+
key: `layout:collections:${slug}:edit`,
|
|
10723
|
+
defaultKeys
|
|
10724
|
+
});
|
|
10725
|
+
const orderedFields = useMemo(() => {
|
|
10726
|
+
const fieldMap = new Map(fieldsList.map((f) => [f.name, f]));
|
|
10727
|
+
const ordered = [];
|
|
10728
|
+
for (const item of layout) {
|
|
10729
|
+
const field = fieldMap.get(item.name);
|
|
10730
|
+
if (field) {
|
|
10731
|
+
const overriddenField = {
|
|
10732
|
+
...field,
|
|
10733
|
+
admin: {
|
|
10734
|
+
...field.admin,
|
|
10735
|
+
width: item.width || field.admin?.width || "100%"
|
|
10736
|
+
}
|
|
10737
|
+
};
|
|
10738
|
+
ordered.push(overriddenField);
|
|
10739
|
+
fieldMap.delete(item.name);
|
|
10740
|
+
}
|
|
10741
|
+
}
|
|
10742
|
+
for (const field of fieldMap.values()) ordered.push(field);
|
|
10743
|
+
return ordered;
|
|
10744
|
+
}, [fieldsList, layout]);
|
|
10745
|
+
const sensors = useSensors(useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }));
|
|
10746
|
+
const handleDragEnd = (event) => {
|
|
10747
|
+
const { active, over } = event;
|
|
10748
|
+
if (!over || active.id === over.id) return;
|
|
10749
|
+
setLayout((items) => {
|
|
10750
|
+
const oldIndex = items.findIndex((item) => item.name === active.id);
|
|
10751
|
+
const newIndex = items.findIndex((item) => item.name === over.id);
|
|
10752
|
+
if (oldIndex === -1 || newIndex === -1) return items;
|
|
10753
|
+
return arrayMove(items, oldIndex, newIndex);
|
|
10754
|
+
});
|
|
10755
|
+
};
|
|
9758
10756
|
useEffect(() => {
|
|
9759
10757
|
const handleBeforeUnload = (e) => {
|
|
9760
10758
|
if (isDirty) {
|
|
@@ -9775,18 +10773,19 @@ function EditEntryPage() {
|
|
|
9775
10773
|
window.addEventListener("keydown", handleSave);
|
|
9776
10774
|
return () => window.removeEventListener("keydown", handleSave);
|
|
9777
10775
|
}, []);
|
|
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
10776
|
const schemaSlug = schema?.slug;
|
|
9785
10777
|
const schemaPreviewUrl = schema?.admin?.previewUrl;
|
|
10778
|
+
const syncShowPreview = useState(() => (next) => {
|
|
10779
|
+
setShowPreview((prev) => prev === next ? prev : next);
|
|
10780
|
+
})[0];
|
|
9786
10781
|
useEffect(() => {
|
|
9787
10782
|
if (!schemaSlug || !schemaPreviewUrl) return;
|
|
9788
|
-
|
|
9789
|
-
}, [
|
|
10783
|
+
syncShowPreview(false);
|
|
10784
|
+
}, [
|
|
10785
|
+
schemaSlug,
|
|
10786
|
+
schemaPreviewUrl,
|
|
10787
|
+
syncShowPreview
|
|
10788
|
+
]);
|
|
9790
10789
|
const { data: entry, isLoading: isEntryLoading } = useQuery({
|
|
9791
10790
|
queryKey: [
|
|
9792
10791
|
"entry",
|
|
@@ -9796,14 +10795,13 @@ function EditEntryPage() {
|
|
|
9796
10795
|
queryFn: () => client.collection(slug).findOne(id),
|
|
9797
10796
|
enabled: !!client && isEdit
|
|
9798
10797
|
});
|
|
10798
|
+
const syncPreviewData = useState(() => (next) => {
|
|
10799
|
+
setPreviewData((prev) => prev === next ? prev : next);
|
|
10800
|
+
})[0];
|
|
9799
10801
|
useEffect(() => {
|
|
9800
10802
|
if (!entry) return;
|
|
9801
|
-
|
|
9802
|
-
}, [
|
|
9803
|
-
entry,
|
|
9804
|
-
id,
|
|
9805
|
-
slug
|
|
9806
|
-
]);
|
|
10803
|
+
syncPreviewData(entry);
|
|
10804
|
+
}, [entry, syncPreviewData]);
|
|
9807
10805
|
const isAdminUser = Array.isArray(user?.roles) && user.roles.includes("admin");
|
|
9808
10806
|
const isSelf = !!user && !!id && (user.id === id || user.sub === id);
|
|
9809
10807
|
const passwordChangeMode = schema?.auth ? isSelf ? "self" : isAdminUser ? "admin" : null : null;
|
|
@@ -9979,6 +10977,14 @@ function EditEntryPage() {
|
|
|
9979
10977
|
title: showPreview ? "Hide Preview" : "Live Preview",
|
|
9980
10978
|
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
10979
|
}),
|
|
10980
|
+
/* @__PURE__ */ jsx(Button, {
|
|
10981
|
+
variant: "ghost",
|
|
10982
|
+
size: "icon",
|
|
10983
|
+
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"),
|
|
10984
|
+
onClick: () => setIsConfiguringView(!isConfiguringView),
|
|
10985
|
+
title: "View",
|
|
10986
|
+
children: /* @__PURE__ */ jsx(Settings2, { className: "dy-h-3.5 dy-w-3.5" })
|
|
10987
|
+
}),
|
|
9982
10988
|
isEdit && canCreate && /* @__PURE__ */ jsx(Button, {
|
|
9983
10989
|
variant: "outline",
|
|
9984
10990
|
size: "icon",
|
|
@@ -10061,33 +11067,111 @@ function EditEntryPage() {
|
|
|
10061
11067
|
})
|
|
10062
11068
|
})]
|
|
10063
11069
|
}),
|
|
10064
|
-
(
|
|
10065
|
-
|
|
10066
|
-
|
|
10067
|
-
|
|
10068
|
-
|
|
10069
|
-
|
|
10070
|
-
|
|
10071
|
-
|
|
10072
|
-
|
|
10073
|
-
|
|
10074
|
-
|
|
10075
|
-
|
|
10076
|
-
|
|
10077
|
-
|
|
10078
|
-
|
|
10079
|
-
|
|
10080
|
-
|
|
10081
|
-
|
|
10082
|
-
|
|
10083
|
-
|
|
11070
|
+
isConfiguringView ? /* @__PURE__ */ jsxs("div", {
|
|
11071
|
+
className: "dy-space-y-6",
|
|
11072
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
11073
|
+
className: "dy-p-4 dy-rounded-xl",
|
|
11074
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
11075
|
+
className: "mb-2 dy-text-sm dy-text-muted-foreground",
|
|
11076
|
+
children: "Drag and drop fields to reorder the form layout. Changes will be saved as your personal preference or global default."
|
|
11077
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
11078
|
+
className: "dy-space-y-2 dy-pr-1 dy-outline-none",
|
|
11079
|
+
children: /* @__PURE__ */ jsx(DndContext, {
|
|
11080
|
+
sensors,
|
|
11081
|
+
collisionDetection: closestCenter,
|
|
11082
|
+
onDragEnd: handleDragEnd,
|
|
11083
|
+
children: /* @__PURE__ */ jsx(SortableContext, {
|
|
11084
|
+
items: layout.map((item) => item.name),
|
|
11085
|
+
strategy: verticalListSortingStrategy,
|
|
11086
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
11087
|
+
className: "dy-space-y-1.5",
|
|
11088
|
+
children: layout.map((item) => {
|
|
11089
|
+
const field = fieldsList.find((f) => f.name === item.name);
|
|
11090
|
+
if (!field) return null;
|
|
11091
|
+
return /* @__PURE__ */ jsx(SortableFieldItem, {
|
|
11092
|
+
id: item.name,
|
|
11093
|
+
label: field.label || field.name,
|
|
11094
|
+
type: field.type,
|
|
11095
|
+
width: item.width || "100%",
|
|
11096
|
+
onChangeWidth: (newWidth) => {
|
|
11097
|
+
setLayout((prev) => prev.map((x) => x.name === item.name ? {
|
|
11098
|
+
...x,
|
|
11099
|
+
width: newWidth
|
|
11100
|
+
} : x));
|
|
11101
|
+
}
|
|
11102
|
+
}, item.name);
|
|
11103
|
+
})
|
|
11104
|
+
})
|
|
11105
|
+
})
|
|
11106
|
+
})
|
|
11107
|
+
})]
|
|
11108
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
11109
|
+
className: "dy-sticky dy-bottom-0 dy-left-0 dy-right-0 dy-z-20 dy-pointer-events-none",
|
|
11110
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
11111
|
+
className: "dy-pointer-events-auto dy-mx-auto dy-max-w-2xl dy-px-4 dy-pb-6",
|
|
11112
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
11113
|
+
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",
|
|
11114
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
11115
|
+
className: "dy-text-sm dy-font-medium dy-text-muted-foreground",
|
|
11116
|
+
children: "View"
|
|
11117
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
11118
|
+
className: "dy-flex dy-items-center dy-gap-2",
|
|
11119
|
+
children: [
|
|
11120
|
+
/* @__PURE__ */ jsx(Button, {
|
|
11121
|
+
variant: "outline",
|
|
11122
|
+
size: "sm",
|
|
11123
|
+
className: "dy-rounded-xl",
|
|
11124
|
+
onClick: async () => {
|
|
11125
|
+
await resetLayout("personal");
|
|
11126
|
+
setIsConfiguringView(false);
|
|
11127
|
+
toast.success("Personal layout reset to default");
|
|
11128
|
+
},
|
|
11129
|
+
children: "Reset"
|
|
11130
|
+
}),
|
|
11131
|
+
isAdminUser && /* @__PURE__ */ jsx(Button, {
|
|
11132
|
+
variant: "outline",
|
|
11133
|
+
size: "sm",
|
|
11134
|
+
className: "dy-rounded-xl",
|
|
11135
|
+
onClick: async () => {
|
|
11136
|
+
await saveLayout("global");
|
|
11137
|
+
setIsConfiguringView(false);
|
|
11138
|
+
toast.success("Saved for everyone successfully");
|
|
11139
|
+
},
|
|
11140
|
+
children: "Save for Everyone"
|
|
11141
|
+
}),
|
|
11142
|
+
/* @__PURE__ */ jsx(Button, {
|
|
11143
|
+
size: "sm",
|
|
11144
|
+
className: "dy-rounded-xl dy-bg-primary dy-text-primary-foreground",
|
|
11145
|
+
onClick: async () => {
|
|
11146
|
+
await saveLayout("personal");
|
|
11147
|
+
setIsConfiguringView(false);
|
|
11148
|
+
toast.success("Saved for me successfully");
|
|
11149
|
+
},
|
|
11150
|
+
children: "Save for Me"
|
|
11151
|
+
}),
|
|
11152
|
+
/* @__PURE__ */ jsx(Button, {
|
|
11153
|
+
variant: "ghost",
|
|
11154
|
+
size: "sm",
|
|
11155
|
+
className: "dy-rounded-xl",
|
|
11156
|
+
onClick: () => {
|
|
11157
|
+
setLayout(reconciledLayout);
|
|
11158
|
+
setIsConfiguringView(false);
|
|
11159
|
+
},
|
|
11160
|
+
children: "Cancel"
|
|
11161
|
+
})
|
|
11162
|
+
]
|
|
11163
|
+
})]
|
|
11164
|
+
})
|
|
11165
|
+
})
|
|
11166
|
+
})]
|
|
11167
|
+
}) : (() => {
|
|
10084
11168
|
const queryParamsDefaults = {};
|
|
10085
11169
|
if (!isEdit) searchParams.forEach((value, key) => {
|
|
10086
11170
|
queryParamsDefaults[key] = value;
|
|
10087
11171
|
});
|
|
10088
11172
|
return /* @__PURE__ */ jsx(FormEngine, {
|
|
10089
11173
|
collection: slug,
|
|
10090
|
-
fields,
|
|
11174
|
+
fields: orderedFields,
|
|
10091
11175
|
defaultValues: isEdit ? entry : {
|
|
10092
11176
|
...queryParamsDefaults,
|
|
10093
11177
|
...entry
|
|
@@ -10098,7 +11182,7 @@ function EditEntryPage() {
|
|
|
10098
11182
|
...newData
|
|
10099
11183
|
}),
|
|
10100
11184
|
onChange: (dirty) => setIsDirty(dirty),
|
|
10101
|
-
isLoading: saveMutation.isPending,
|
|
11185
|
+
isLoading: saveMutation.isPending || isPreferenceLoading,
|
|
10102
11186
|
submitLabel: isEdit ? "Save Changes" : "Create Entry",
|
|
10103
11187
|
readOnly: isEdit ? !canUpdate : !canCreate,
|
|
10104
11188
|
passwordChangeMode: isEdit ? passwordChangeMode : null,
|
|
@@ -10240,18 +11324,6 @@ CardFooter.displayName = "CardFooter";
|
|
|
10240
11324
|
//#region src/components/ui/aspect-ratio.tsx
|
|
10241
11325
|
var AspectRatio = AspectRatioPrimitive.Root;
|
|
10242
11326
|
//#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
11327
|
//#region src/components/ui/separator.tsx
|
|
10256
11328
|
var Separator = React$1.forwardRef(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => /* @__PURE__ */ jsx(SeparatorPrimitive.Root, {
|
|
10257
11329
|
ref,
|