@nestledjs/data-browser 1.0.15 → 1.0.16

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.
@@ -4,5 +4,5 @@ interface RelationFilterFieldProps {
4
4
  currentValue: any;
5
5
  onChange: (value: any) => void;
6
6
  }
7
- export declare function RelationFilterField({ fieldName, relatedModelName, currentValue, onChange }: Readonly<RelationFilterFieldProps>): import("react/jsx-runtime").JSX.Element;
7
+ export declare function RelationFilterField({ fieldName, relatedModelName, currentValue, onChange, }: Readonly<RelationFilterFieldProps>): import("react/jsx-runtime").JSX.Element;
8
8
  export {};
@@ -17,7 +17,12 @@ function RelationFilterField({
17
17
  setIsOpen(false);
18
18
  }, []);
19
19
  useClickOutside(dropdownRef, handleCloseDropdown, isOpen);
20
- const { relatedItems, loading, error: relationError, hasDocument } = useRelationData(
20
+ const {
21
+ relatedItems,
22
+ loading,
23
+ error: relationError,
24
+ hasDocument
25
+ } = useRelationData(
21
26
  relatedModelName,
22
27
  searchTerm,
23
28
  isOpen,
@@ -28,11 +33,14 @@ function RelationFilterField({
28
33
  () => currentValue?.id ? relatedItems.find((item) => item.id === currentValue.id) : null,
29
34
  [currentValue?.id, relatedItems]
30
35
  );
31
- const handleSelect = useCallback((item) => {
32
- onChange({ id: item.id });
33
- setIsOpen(false);
34
- setSearchTerm("");
35
- }, [onChange]);
36
+ const handleSelect = useCallback(
37
+ (item) => {
38
+ onChange({ id: item.id });
39
+ setIsOpen(false);
40
+ setSearchTerm("");
41
+ },
42
+ [onChange]
43
+ );
36
44
  const handleClear = useCallback(() => {
37
45
  onChange(void 0);
38
46
  setIsOpen(false);
@@ -9,25 +9,10 @@ function AdminBreadcrumbs({
9
9
  }) {
10
10
  const renderHomeLink = () => {
11
11
  if (!showHome) return null;
12
- return /* @__PURE__ */ jsx("li", { className: "flex items-center", children: /* @__PURE__ */ jsx(
13
- Link,
14
- {
15
- to: homeHref,
16
- className: "text-gray-400 hover:text-gray-500",
17
- "aria-label": "Home",
18
- children: /* @__PURE__ */ jsx(HomeIcon, { className: "h-5 w-5 flex-shrink-0" })
19
- }
20
- ) }, "home");
12
+ return /* @__PURE__ */ jsx("li", { className: "flex items-center", children: /* @__PURE__ */ jsx(Link, { to: homeHref, className: "text-gray-400 hover:text-gray-500", "aria-label": "Home", children: /* @__PURE__ */ jsx(HomeIcon, { className: "h-5 w-5 flex-shrink-0" }) }) }, "home");
21
13
  };
22
14
  const renderBreadcrumbItem = (item, index, isLast) => {
23
- const content = item.href && !item.isActive ? /* @__PURE__ */ jsx(
24
- Link,
25
- {
26
- to: item.href,
27
- className: "text-sm font-medium text-gray-500 hover:text-gray-700",
28
- children: item.label
29
- }
30
- ) : /* @__PURE__ */ jsx(
15
+ const content = item.href && !item.isActive ? /* @__PURE__ */ jsx(Link, { to: item.href, className: "text-sm font-medium text-gray-500 hover:text-gray-700", children: item.label }) : /* @__PURE__ */ jsx(
31
16
  "span",
32
17
  {
33
18
  className: `text-sm font-medium ${item.isActive ? "text-gray-900" : "text-gray-500"}`,
@@ -109,7 +109,12 @@ function AdminLoadingState({
109
109
  return sizes[size];
110
110
  };
111
111
  return /* @__PURE__ */ jsxs("div", { className: `flex flex-col items-center justify-center p-8 ${className}`, children: [
112
- /* @__PURE__ */ jsx("div", { className: `animate-spin rounded-full border-b-2 border-green-web ${getSizeClasses()}` }),
112
+ /* @__PURE__ */ jsx(
113
+ "div",
114
+ {
115
+ className: `animate-spin rounded-full border-b-2 border-green-web ${getSizeClasses()}`
116
+ }
117
+ ),
113
118
  /* @__PURE__ */ jsx("h3", { className: "mt-4 text-sm font-medium text-gray-900", children: title }),
114
119
  message && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-gray-500", children: message })
115
120
  ] });
@@ -151,7 +151,12 @@ function AdminStatusDisplay({
151
151
  const finalClasses = `${baseClasses} ${interactiveClasses} ${className}`.trim();
152
152
  const renderDot = () => {
153
153
  if (!shouldShowDot) return null;
154
- return /* @__PURE__ */ jsx("span", { className: `inline-block rounded-full mr-2 ${config.bgColor.replace("bg-", "bg-").replace("-100", "-400")} ${sizeClasses.dot}` });
154
+ return /* @__PURE__ */ jsx(
155
+ "span",
156
+ {
157
+ className: `inline-block rounded-full mr-2 ${config.bgColor.replace("bg-", "bg-").replace("-100", "-400")} ${sizeClasses.dot}`
158
+ }
159
+ );
155
160
  };
156
161
  const renderIcon = () => {
157
162
  if (!shouldShowIcon) return null;
@@ -168,16 +173,7 @@ function AdminStatusDisplay({
168
173
  }
169
174
  };
170
175
  if (isClickable) {
171
- return /* @__PURE__ */ jsx(
172
- "button",
173
- {
174
- type: "button",
175
- onClick: handleClick,
176
- className: finalClasses,
177
- title: tooltip,
178
- children: content
179
- }
180
- );
176
+ return /* @__PURE__ */ jsx("button", { type: "button", onClick: handleClick, className: finalClasses, title: tooltip, children: content });
181
177
  }
182
178
  return /* @__PURE__ */ jsx("span", { className: finalClasses, title: tooltip, children: content });
183
179
  }
@@ -42,7 +42,7 @@ export interface AdminDataProviderProps {
42
42
  * ```tsx
43
43
  * import * as Sdk from '@your-project/shared/sdk'
44
44
  * import { DATABASE_MODELS } from '@your-project/shared/sdk'
45
- * import { AdminDataProvider } from '@nestledjs/admin-data'
45
+ * import { AdminDataProvider } from '@nestledjs/data-browser'
46
46
  *
47
47
  * <AdminDataProvider
48
48
  * sdk={Sdk}
@@ -54,7 +54,7 @@ export interface AdminDataProviderProps {
54
54
  * </AdminDataProvider>
55
55
  * ```
56
56
  */
57
- export declare function AdminDataProvider({ children, sdk, databaseModels, basePath, formTheme, displayFieldConfig }: Readonly<AdminDataProviderProps>): import("react/jsx-runtime").JSX.Element;
57
+ export declare function AdminDataProvider({ children, sdk, databaseModels, basePath, formTheme, displayFieldConfig, }: Readonly<AdminDataProviderProps>): import("react/jsx-runtime").JSX.Element;
58
58
  /**
59
59
  * Hook to access the admin data context
60
60
  * @throws Error if used outside of AdminDataProvider
@@ -3,7 +3,7 @@ function useClickOutside(ref, handler, isActive = true) {
3
3
  useEffect(() => {
4
4
  if (!isActive) return;
5
5
  const handleClickOutside = (event) => {
6
- if (ref.current && !ref.current.contains(event.target)) {
6
+ if (ref.current && event.target instanceof Node && !ref.current.contains(event.target)) {
7
7
  handler();
8
8
  }
9
9
  };
@@ -69,6 +69,7 @@ function useRelationData(relatedModelName, searchTerm, isOpen, currentValueId) {
69
69
  return true;
70
70
  });
71
71
  } catch (error) {
72
+ console.error("Unexpected error:", error);
72
73
  return [];
73
74
  }
74
75
  }, [relatedData, relatedDataPath, relationError]);
@@ -87,14 +87,41 @@ function AdminDataLayout() {
87
87
  fileInputRef.current?.click();
88
88
  };
89
89
  const content = /* @__PURE__ */ jsxs("div", { className: "max-w-full mx-auto flex flex-col p-0.5", children: [
90
- notification && /* @__PURE__ */ jsx("div", { className: `mb-4 px-4 py-3 rounded-md border ${notification.type === "success" ? "bg-green-50 border-green-200 text-green-800 dark:bg-green-900/20 dark:border-green-800 dark:text-green-200" : "bg-red-50 border-red-200 text-red-800 dark:bg-red-900/20 dark:border-red-800 dark:text-red-200"}`, children: /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
91
- notification.type === "success" ? /* @__PURE__ */ jsx("svg", { className: "w-5 h-5 mr-2", fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z", clipRule: "evenodd" }) }) : /* @__PURE__ */ jsx("svg", { className: "w-5 h-5 mr-2", fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z", clipRule: "evenodd" }) }),
92
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: notification.message })
93
- ] }) }),
90
+ notification && /* @__PURE__ */ jsx(
91
+ "div",
92
+ {
93
+ className: `mb-4 px-4 py-3 rounded-md border ${notification.type === "success" ? "bg-green-50 border-green-200 text-green-800 dark:bg-green-900/20 dark:border-green-800 dark:text-green-200" : "bg-red-50 border-red-200 text-red-800 dark:bg-red-900/20 dark:border-red-800 dark:text-red-200"}`,
94
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
95
+ notification.type === "success" ? /* @__PURE__ */ jsx("svg", { className: "w-5 h-5 mr-2", fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsx(
96
+ "path",
97
+ {
98
+ fillRule: "evenodd",
99
+ d: "M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z",
100
+ clipRule: "evenodd"
101
+ }
102
+ ) }) : /* @__PURE__ */ jsx("svg", { className: "w-5 h-5 mr-2", fill: "currentColor", viewBox: "0 0 20 20", children: /* @__PURE__ */ jsx(
103
+ "path",
104
+ {
105
+ fillRule: "evenodd",
106
+ d: "M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z",
107
+ clipRule: "evenodd"
108
+ }
109
+ ) }),
110
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: notification.message })
111
+ ] })
112
+ }
113
+ ),
94
114
  /* @__PURE__ */ jsxs("div", { className: "mb-6 space-y-3", children: [
95
115
  /* @__PURE__ */ jsxs("div", { className: "flex items-end gap-3", children: [
96
116
  /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
97
- /* @__PURE__ */ jsx("label", { htmlFor: "model-selector", className: "block text-sm font-medium text-gray-900 dark:text-gray-100 mb-2", children: "Select Model" }),
117
+ /* @__PURE__ */ jsx(
118
+ "label",
119
+ {
120
+ htmlFor: "model-selector",
121
+ className: "block text-sm font-medium text-gray-900 dark:text-gray-100 mb-2",
122
+ children: "Select Model"
123
+ }
124
+ ),
98
125
  /* @__PURE__ */ jsxs("div", { className: "relative", children: [
99
126
  /* @__PURE__ */ jsxs(
100
127
  "select",
@@ -109,7 +136,23 @@ function AdminDataLayout() {
109
136
  ]
110
137
  }
111
138
  ),
112
- /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3", children: /* @__PURE__ */ jsx("svg", { className: "h-5 w-5 text-gray-400", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z", clipRule: "evenodd" }) }) })
139
+ /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3", children: /* @__PURE__ */ jsx(
140
+ "svg",
141
+ {
142
+ className: "h-5 w-5 text-gray-400",
143
+ xmlns: "http://www.w3.org/2000/svg",
144
+ viewBox: "0 0 20 20",
145
+ fill: "currentColor",
146
+ children: /* @__PURE__ */ jsx(
147
+ "path",
148
+ {
149
+ fillRule: "evenodd",
150
+ d: "M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z",
151
+ clipRule: "evenodd"
152
+ }
153
+ )
154
+ }
155
+ ) })
113
156
  ] })
114
157
  ] }),
115
158
  /* @__PURE__ */ jsx(
@@ -118,7 +161,23 @@ function AdminDataLayout() {
118
161
  onClick: toggleFullscreen,
119
162
  className: "h-[50px] px-4 py-3 bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-green-web focus:border-green-web transition-colors",
120
163
  title: isFullscreen ? "Exit fullscreen" : "Enter fullscreen",
121
- children: isFullscreen ? /* @__PURE__ */ jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 9V4.5M9 9H4.5M9 9L3.75 3.75M9 15v4.5M9 15H4.5M9 15l-5.25 5.25M15 9h4.5M15 9V4.5M15 9l5.25-5.25M15 15h4.5M15 15v4.5m0-4.5l5.25 5.25" }) }) : /* @__PURE__ */ jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5l-5-5m5 5v-4m0 4h-4" }) })
164
+ children: isFullscreen ? /* @__PURE__ */ jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx(
165
+ "path",
166
+ {
167
+ strokeLinecap: "round",
168
+ strokeLinejoin: "round",
169
+ strokeWidth: 2,
170
+ d: "M9 9V4.5M9 9H4.5M9 9L3.75 3.75M9 15v4.5M9 15H4.5M9 15l-5.25 5.25M15 9h4.5M15 9V4.5M15 9l5.25-5.25M15 15h4.5M15 15v4.5m0-4.5l5.25 5.25"
171
+ }
172
+ ) }) : /* @__PURE__ */ jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx(
173
+ "path",
174
+ {
175
+ strokeLinecap: "round",
176
+ strokeLinejoin: "round",
177
+ strokeWidth: 2,
178
+ d: "M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5l-5-5m5 5v-4m0 4h-4"
179
+ }
180
+ ) })
122
181
  }
123
182
  )
124
183
  ] }),
@@ -131,7 +190,15 @@ function AdminDataLayout() {
131
190
  className: "inline-flex items-center px-3 py-1.5 text-gray-700 dark:text-gray-300 hover:text-green-web dark:hover:text-green-web transition-colors",
132
191
  title: "Export your preferences to a file",
133
192
  children: [
134
- /* @__PURE__ */ jsx("svg", { className: "w-4 h-4 mr-1.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" }) }),
193
+ /* @__PURE__ */ jsx("svg", { className: "w-4 h-4 mr-1.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx(
194
+ "path",
195
+ {
196
+ strokeLinecap: "round",
197
+ strokeLinejoin: "round",
198
+ strokeWidth: 2,
199
+ d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"
200
+ }
201
+ ) }),
135
202
  "Export"
136
203
  ]
137
204
  }
@@ -144,7 +211,15 @@ function AdminDataLayout() {
144
211
  className: "inline-flex items-center px-3 py-1.5 text-gray-700 dark:text-gray-300 hover:text-green-web dark:hover:text-green-web transition-colors",
145
212
  title: "Import preferences from a file",
146
213
  children: [
147
- /* @__PURE__ */ jsx("svg", { className: "w-4 h-4 mr-1.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" }) }),
214
+ /* @__PURE__ */ jsx("svg", { className: "w-4 h-4 mr-1.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx(
215
+ "path",
216
+ {
217
+ strokeLinecap: "round",
218
+ strokeLinejoin: "round",
219
+ strokeWidth: 2,
220
+ d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"
221
+ }
222
+ ) }),
148
223
  "Import"
149
224
  ]
150
225
  }
@@ -1,4 +1,4 @@
1
- export declare function AdminDataCreatePage(): import("react/jsx-runtime").JSX.Element;
1
+ export declare function AdminDataCreatePage(): import("react/jsx-runtime").JSX.Element | null;
2
2
  export declare function AdminDataCreateErrorBoundary({ error }: Readonly<{
3
3
  error: Error;
4
4
  }>): import("react/jsx-runtime").JSX.Element;
@@ -7,20 +7,15 @@ import { ErrorBoundary } from "@nestledjs/shared-components";
7
7
  import { Form } from "@nestledjs/forms";
8
8
  import { useAdminDataContext } from "../context/AdminDataContext.js";
9
9
  import { useParams, Link, useNavigate } from "react-router";
10
- import { getAdminDocuments, buildFormFields, cleanFormInput } from "../utils/graphql-utils.js";
11
- function toReadableText(text) {
12
- return text.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/^./, (str) => str.toUpperCase());
13
- }
14
- function sanitizeInput(input) {
15
- if (!input || typeof input !== "string") return "";
16
- return input.replace(/[<>"'%;()&+]/g, "").replace(/javascript:/gi, "").replace(/on\w+\s*=/gi, "").trim().substring(0, 100);
17
- }
18
- const toKebabCase = (str) => {
19
- return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
20
- };
10
+ import { sanitizeInput, getAdminDocuments, toKebabCase, buildFormFields, toReadableText, cleanFormInput } from "../utils/graphql-utils.js";
21
11
  function AdminDataCreatePage() {
22
12
  const { dataType } = useParams();
23
- const { databaseModels, basePath = "/admin/data", formTheme, displayFieldConfig } = useAdminDataContext();
13
+ const {
14
+ databaseModels,
15
+ basePath = "/admin/data",
16
+ formTheme,
17
+ displayFieldConfig
18
+ } = useAdminDataContext();
24
19
  const findModelByName = (name) => {
25
20
  return databaseModels.find((model2) => model2.name === name);
26
21
  };
@@ -69,9 +64,23 @@ function AdminDataCreatePage() {
69
64
  ) })
70
65
  ] }) }) }) });
71
66
  }
72
- return /* @__PURE__ */ jsx(AdminDataCreatePageContent, { model, basePath, formTheme, displayFieldConfig });
67
+ if (!model) return null;
68
+ return /* @__PURE__ */ jsx(
69
+ AdminDataCreatePageContent,
70
+ {
71
+ model,
72
+ basePath,
73
+ formTheme,
74
+ displayFieldConfig
75
+ }
76
+ );
73
77
  }
74
- function AdminDataCreatePageContent({ model, basePath, formTheme, displayFieldConfig }) {
78
+ function AdminDataCreatePageContent({
79
+ model,
80
+ basePath,
81
+ formTheme,
82
+ displayFieldConfig
83
+ }) {
75
84
  const navigate = useNavigate();
76
85
  const { sdk, databaseModels } = useAdminDataContext();
77
86
  const [submissionState, setSubmissionState] = useState({ status: "idle" });
@@ -79,6 +88,7 @@ function AdminDataCreatePageContent({ model, basePath, formTheme, displayFieldCo
79
88
  try {
80
89
  return getAdminDocuments(sdk, model);
81
90
  } catch (error) {
91
+ console.error("Unexpected error:", error);
82
92
  return null;
83
93
  }
84
94
  }, [sdk, model]);
@@ -90,6 +100,7 @@ function AdminDataCreatePageContent({ model, basePath, formTheme, displayFieldCo
90
100
  }
91
101
  return gql(documents.create);
92
102
  } catch (error) {
103
+ console.error("Unexpected error:", error);
93
104
  return null;
94
105
  }
95
106
  }, [documents]);
@@ -100,6 +111,21 @@ function AdminDataCreatePageContent({ model, basePath, formTheme, displayFieldCo
100
111
  }
101
112
  `
102
113
  );
114
+ useEffect(() => {
115
+ if (submissionState.status !== "success") return;
116
+ const timer = setTimeout(() => {
117
+ navigate(`${basePath}/${toKebabCase(model.pluralName)}`);
118
+ }, 1500);
119
+ return () => clearTimeout(timer);
120
+ }, [submissionState.status, navigate, basePath, model.pluralName]);
121
+ useEffect(() => {
122
+ if (submissionState.status === "error") {
123
+ const timer = setTimeout(() => {
124
+ setSubmissionState({ status: "idle" });
125
+ }, 5e3);
126
+ return () => clearTimeout(timer);
127
+ }
128
+ }, [submissionState.status]);
103
129
  if (!documents || !CREATE_MUTATION) {
104
130
  return /* @__PURE__ */ jsx("div", { className: "min-h-screen bg-gray-50 dark:bg-gray-900 flex flex-col justify-center py-12 sm:px-6 lg:px-8", children: /* @__PURE__ */ jsx("div", { className: "mt-8 sm:mx-auto sm:w-full sm:max-w-2xl", children: /* @__PURE__ */ jsx("div", { className: "bg-white dark:bg-gray-800 py-8 px-4 shadow sm:rounded-lg sm:px-10", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
105
131
  /* @__PURE__ */ jsx(ExclamationCircleIcon, { className: "mx-auto h-12 w-12 text-red-400" }),
@@ -148,25 +174,17 @@ function AdminDataCreatePageContent({ model, basePath, formTheme, displayFieldCo
148
174
  });
149
175
  }
150
176
  };
151
- useEffect(() => {
152
- if (submissionState.status !== "success") return;
153
- const timer = setTimeout(() => {
154
- navigate(`${basePath}/${toKebabCase(model.pluralName)}`);
155
- }, 1500);
156
- return () => clearTimeout(timer);
157
- }, [submissionState.status, navigate, basePath, model.pluralName]);
158
- useEffect(() => {
159
- if (submissionState.status === "error") {
160
- const timer = setTimeout(() => {
161
- setSubmissionState({ status: "idle" });
162
- }, 5e3);
163
- return () => clearTimeout(timer);
164
- }
165
- }, [submissionState.status]);
166
177
  return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
167
178
  /* @__PURE__ */ jsxs("div", { className: "mb-8", children: [
168
179
  /* @__PURE__ */ jsx("nav", { className: "flex mb-6", "aria-label": "Breadcrumb", children: /* @__PURE__ */ jsxs("ol", { className: "flex items-center space-x-4", children: [
169
- /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(Link, { to: basePath, className: "text-gray-400 dark:text-gray-500 hover:text-gray-500 dark:hover:text-gray-400", children: "Data Browser" }) }),
180
+ /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
181
+ Link,
182
+ {
183
+ to: basePath,
184
+ className: "text-gray-400 dark:text-gray-500 hover:text-gray-500 dark:hover:text-gray-400",
185
+ children: "Data Browser"
186
+ }
187
+ ) }),
170
188
  /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
171
189
  /* @__PURE__ */ jsx(
172
190
  "svg",
@@ -218,22 +236,24 @@ function AdminDataCreatePageContent({ model, basePath, formTheme, displayFieldCo
218
236
  toReadableText(model.name)
219
237
  ] })
220
238
  ] }),
221
- submissionState.status !== "idle" && /* @__PURE__ */ jsx(
222
- "div",
223
- {
224
- className: `mb-6 rounded-md p-4 ${submissionState.status === "success" ? "bg-green-50 border border-green-200" : submissionState.status === "error" ? "bg-red-50 border border-red-200" : "bg-blue-50 border border-blue-200"}`,
225
- children: /* @__PURE__ */ jsxs("div", { className: "flex", children: [
226
- /* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: submissionState.status === "success" ? /* @__PURE__ */ jsx(CheckCircleIcon, { className: "h-5 w-5 text-green-400" }) : submissionState.status === "error" ? /* @__PURE__ */ jsx(ExclamationCircleIcon, { className: "h-5 w-5 text-red-400" }) : /* @__PURE__ */ jsx("div", { className: "h-5 w-5 border-2 border-blue-400 border-t-transparent rounded-full animate-spin" }) }),
227
- /* @__PURE__ */ jsx("div", { className: "ml-3", children: /* @__PURE__ */ jsx(
228
- "p",
229
- {
230
- className: `text-sm font-medium ${submissionState.status === "success" ? "text-green-800" : submissionState.status === "error" ? "text-red-800" : "text-blue-800"}`,
231
- children: submissionState.status === "loading" ? `Creating ${toReadableText(model.name)}...` : submissionState.message
232
- }
233
- ) })
234
- ] })
235
- }
236
- ),
239
+ submissionState.status !== "idle" && (() => {
240
+ const isSuccess = submissionState.status === "success";
241
+ const isError = submissionState.status === "error";
242
+ let bgClass = "bg-blue-50 border border-blue-200";
243
+ if (isSuccess) bgClass = "bg-green-50 border border-green-200";
244
+ else if (isError) bgClass = "bg-red-50 border border-red-200";
245
+ let textClass = "text-blue-800";
246
+ if (isSuccess) textClass = "text-green-800";
247
+ else if (isError) textClass = "text-red-800";
248
+ let icon = /* @__PURE__ */ jsx("div", { className: "h-5 w-5 border-2 border-blue-400 border-t-transparent rounded-full animate-spin" });
249
+ if (isSuccess) icon = /* @__PURE__ */ jsx(CheckCircleIcon, { className: "h-5 w-5 text-green-400" });
250
+ else if (isError) icon = /* @__PURE__ */ jsx(ExclamationCircleIcon, { className: "h-5 w-5 text-red-400" });
251
+ const message = submissionState.status === "loading" ? `Creating ${toReadableText(model.name)}...` : submissionState.message;
252
+ return /* @__PURE__ */ jsx("div", { className: `mb-6 rounded-md p-4 ${bgClass}`, children: /* @__PURE__ */ jsxs("div", { className: "flex", children: [
253
+ /* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: icon }),
254
+ /* @__PURE__ */ jsx("div", { className: "ml-3", children: /* @__PURE__ */ jsx("p", { className: `text-sm font-medium ${textClass}`, children: message }) })
255
+ ] }) });
256
+ })(),
237
257
  /* @__PURE__ */ jsx("div", { className: "bg-white dark:bg-gray-800 shadow-sm rounded-lg", children: /* @__PURE__ */ jsx("div", { className: "px-6 py-8", children: /* @__PURE__ */ jsx(
238
258
  Form,
239
259
  {
@@ -10,7 +10,7 @@ import { Form } from "@nestledjs/forms";
10
10
  import { useAdminDataContext } from "../context/AdminDataContext.js";
11
11
  import { AdminDataStateMessage } from "../components/AdminDataStateMessage.js";
12
12
  import { useParams, Link, useNavigate } from "react-router";
13
- import { getAdminDocuments, buildFormFields, cleanFormInput } from "../utils/graphql-utils.js";
13
+ import { sanitizeInput, getAdminDocuments, toKebabCase, toReadableText, buildFormFields, cleanFormInput } from "../utils/graphql-utils.js";
14
14
  function toLowerCamelCase(name) {
15
15
  if (!name) return "";
16
16
  return name.charAt(0).toLowerCase() + name.slice(1);
@@ -21,20 +21,10 @@ function getModelIdVariableName(modelName) {
21
21
  function getModelResponseFieldName(modelName) {
22
22
  return toLowerCamelCase(modelName);
23
23
  }
24
- function toReadableText(text) {
25
- return text.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/^./, (str) => str.toUpperCase());
26
- }
27
24
  if (process.env.NODE_ENV !== "production") {
28
25
  loadDevMessages();
29
26
  loadErrorMessages();
30
27
  }
31
- const sanitizeInput = (input) => {
32
- if (!input || typeof input !== "string") return "";
33
- return input.replace(/[<>"'%;()&+]/g, "").replace(/javascript:/gi, "").replace(/on\w+\s*=/gi, "").trim().substring(0, 100);
34
- };
35
- const toKebabCase = (str) => {
36
- return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
37
- };
38
28
  function processRelationFieldValue(field, item) {
39
29
  const relationFieldName = field.relationFromFields?.[0] || `${field.name}Id`;
40
30
  let value = item[relationFieldName];
@@ -62,6 +52,7 @@ function processDateFieldValue(field, value) {
62
52
  return dateValue.toISOString().substring(0, 16);
63
53
  }
64
54
  } catch (e) {
55
+ console.error("Unexpected error:", e);
65
56
  return "";
66
57
  }
67
58
  }
@@ -149,7 +140,12 @@ const validateId = (id) => {
149
140
  };
150
141
  function AdminDataEditPage() {
151
142
  const { dataType, id } = useParams();
152
- const { databaseModels, basePath = "/admin/data", formTheme, displayFieldConfig } = useAdminDataContext();
143
+ const {
144
+ databaseModels,
145
+ basePath = "/admin/data",
146
+ formTheme,
147
+ displayFieldConfig
148
+ } = useAdminDataContext();
153
149
  const findModelByName = (name) => {
154
150
  return databaseModels.find((model2) => model2.name === name);
155
151
  };
@@ -203,9 +199,24 @@ function AdminDataEditPage() {
203
199
  ) })
204
200
  ] }) }) }) });
205
201
  }
206
- return /* @__PURE__ */ jsx(AdminDataEditPageContent, { model, id: validatedId, basePath, formTheme, displayFieldConfig });
202
+ return /* @__PURE__ */ jsx(
203
+ AdminDataEditPageContent,
204
+ {
205
+ model,
206
+ id: validatedId,
207
+ basePath,
208
+ formTheme,
209
+ displayFieldConfig
210
+ }
211
+ );
207
212
  }
208
- function DeleteConfirmModal({ show, modelName, isDeleting, onConfirm, onCancel }) {
213
+ function DeleteConfirmModal({
214
+ show,
215
+ modelName,
216
+ isDeleting,
217
+ onConfirm,
218
+ onCancel
219
+ }) {
209
220
  if (!show) return null;
210
221
  return /* @__PURE__ */ jsx("div", { className: "fixed inset-0 bg-gray-600 dark:bg-gray-900 bg-opacity-50 dark:bg-opacity-75 overflow-y-auto h-full w-full z-50 flex items-center justify-center", children: /* @__PURE__ */ jsxs("div", { className: "bg-white dark:bg-gray-800 p-6 rounded-lg shadow-xl max-w-md w-full mx-4", children: [
211
222
  /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
@@ -271,7 +282,13 @@ function getStatusIcon(isSuccess, isError) {
271
282
  }
272
283
  return /* @__PURE__ */ jsx("div", { className: "h-5 w-5 border-2 border-blue-400 border-t-transparent rounded-full animate-spin" });
273
284
  }
274
- function StatusMessage({ submissionStatus, deleteStatus, submissionMessage, deleteMessage, modelName }) {
285
+ function StatusMessage({
286
+ submissionStatus,
287
+ deleteStatus,
288
+ submissionMessage,
289
+ deleteMessage,
290
+ modelName
291
+ }) {
275
292
  if (submissionStatus === "idle" && deleteStatus === "idle") return null;
276
293
  const isSuccess = submissionStatus === "success" || deleteStatus === "success";
277
294
  const isError = submissionStatus === "error" || deleteStatus === "error";
@@ -334,7 +351,13 @@ async function executeDeleteMutation(deleteMutation, id, idVariableName, model)
334
351
  };
335
352
  }
336
353
  }
337
- function AdminDataEditPageContent({ model, id, basePath, formTheme, displayFieldConfig }) {
354
+ function AdminDataEditPageContent({
355
+ model,
356
+ id,
357
+ basePath,
358
+ formTheme,
359
+ displayFieldConfig
360
+ }) {
338
361
  const navigate = useNavigate();
339
362
  const { sdk, databaseModels } = useAdminDataContext();
340
363
  const [submissionState, setSubmissionState] = useState({ status: "idle" });
@@ -344,6 +367,7 @@ function AdminDataEditPageContent({ model, id, basePath, formTheme, displayField
344
367
  try {
345
368
  return getAdminDocuments(sdk, model);
346
369
  } catch (error2) {
370
+ console.error("Unexpected error:", error2);
347
371
  return null;
348
372
  }
349
373
  }, [sdk, model]);
@@ -355,6 +379,7 @@ function AdminDataEditPageContent({ model, id, basePath, formTheme, displayField
355
379
  }
356
380
  return gql(documents.query);
357
381
  } catch (error2) {
382
+ console.error("Unexpected error:", error2);
358
383
  return null;
359
384
  }
360
385
  }, [documents]);
@@ -366,6 +391,7 @@ function AdminDataEditPageContent({ model, id, basePath, formTheme, displayField
366
391
  }
367
392
  return gql(documents.update);
368
393
  } catch (error2) {
394
+ console.error("Unexpected error:", error2);
369
395
  return null;
370
396
  }
371
397
  }, [documents]);
@@ -377,6 +403,7 @@ function AdminDataEditPageContent({ model, id, basePath, formTheme, displayField
377
403
  }
378
404
  return gql(documents.delete);
379
405
  } catch (error2) {
406
+ console.error("Unexpected error:", error2);
380
407
  return null;
381
408
  }
382
409
  }, [documents]);
@@ -496,8 +523,8 @@ function AdminDataEditPageContent({ model, id, basePath, formTheme, displayField
496
523
  status: result.success ? "success" : "error",
497
524
  message: result.message
498
525
  });
499
- if (typeof window !== "undefined") {
500
- window.scrollTo({ top: 0, behavior: "smooth" });
526
+ if (globalThis.window !== void 0) {
527
+ globalThis.scrollTo({ top: 0, behavior: "smooth" });
501
528
  }
502
529
  if (result.success) {
503
530
  await refetch();
@@ -514,7 +541,14 @@ function AdminDataEditPageContent({ model, id, basePath, formTheme, displayField
514
541
  return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
515
542
  /* @__PURE__ */ jsxs("div", { className: "mb-8", children: [
516
543
  /* @__PURE__ */ jsx("nav", { className: "flex mb-6", "aria-label": "Breadcrumb", children: /* @__PURE__ */ jsxs("ol", { className: "flex items-center space-x-4", children: [
517
- /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(Link, { to: basePath, className: "text-gray-400 dark:text-gray-500 hover:text-gray-500 dark:hover:text-gray-400", children: "Data Browser" }) }),
544
+ /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
545
+ Link,
546
+ {
547
+ to: basePath,
548
+ className: "text-gray-400 dark:text-gray-500 hover:text-gray-500 dark:hover:text-gray-400",
549
+ children: "Data Browser"
550
+ }
551
+ ) }),
518
552
  /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
519
553
  /* @__PURE__ */ jsx(
520
554
  "svg",