@nestledjs/data-browser 0.1.13 → 0.1.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.
- package/README.md +0 -2
- package/index.d.ts +1 -0
- package/index.js +2 -0
- package/lib/hooks/useAdminList.js +2 -0
- package/lib/hooks/useRelationData.js +0 -3
- package/lib/layouts/AdminDataLayout.js +1 -1
- package/lib/pages/AdminDataCreatePage.js +41 -44
- package/lib/pages/AdminDataEditPage.js +101 -121
- package/lib/pages/AdminDataIndexPage.js +1 -1
- package/lib/pages/AdminDataListPage.js +115 -124
- package/lib/types/index.d.ts +3 -0
- package/lib/utils/get-plural-names.d.ts +21 -0
- package/lib/utils/get-plural-names.js +41 -0
- package/lib/utils/graphql-utils.d.ts +1 -1
- package/lib/utils/graphql-utils.js +18 -20
- package/lib/utils/secure-storage.js +0 -13
- package/package.json +2 -3
package/README.md
CHANGED
|
@@ -30,7 +30,6 @@ This package requires a Nestled framework project with:
|
|
|
30
30
|
- **Apollo Client v4+** for GraphQL operations
|
|
31
31
|
- **React Router v7+** for routing
|
|
32
32
|
- **@nestledjs/forms** for form generation
|
|
33
|
-
- **@nestledjs/helpers** for utility functions
|
|
34
33
|
- **Prisma** for database models
|
|
35
34
|
- **Generated GraphQL SDK** with admin CRUD operations
|
|
36
35
|
|
|
@@ -40,7 +39,6 @@ This package requires a Nestled framework project with:
|
|
|
40
39
|
{
|
|
41
40
|
"@apollo/client": "^4.0.0",
|
|
42
41
|
"@nestledjs/forms": "^0.5.0",
|
|
43
|
-
"@nestledjs/helpers": "^0.1.0",
|
|
44
42
|
"react": "^19.0.0",
|
|
45
43
|
"react-router": "^7.0.0"
|
|
46
44
|
}
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
|
@@ -20,6 +20,7 @@ import { RelationFieldWrapper } from "./lib/components/RelationFieldWrapper.js";
|
|
|
20
20
|
import { buildFormFields, cleanFormInput, getAdminDocuments, getMutationName } from "./lib/utils/graphql-utils.js";
|
|
21
21
|
import { AdminLocalStorage, SecureAdminLocalStorage } from "./lib/utils/secure-storage.js";
|
|
22
22
|
import { formatFieldName, getItemDisplayName, getSmartSearchFields, kebabCase, normalizeModelNameForDocument, spacedWords } from "./lib/utils/string-utils.js";
|
|
23
|
+
import { getPluralName } from "./lib/utils/get-plural-names.js";
|
|
23
24
|
export {
|
|
24
25
|
AdminBreadcrumbs,
|
|
25
26
|
AdminDataCreateErrorBoundary,
|
|
@@ -53,6 +54,7 @@ export {
|
|
|
53
54
|
getAdminDocuments,
|
|
54
55
|
getItemDisplayName,
|
|
55
56
|
getMutationName,
|
|
57
|
+
getPluralName,
|
|
56
58
|
getSmartSearchFields,
|
|
57
59
|
initialState,
|
|
58
60
|
kebabCase,
|
|
@@ -24,6 +24,8 @@ function adminListReducer(state, action) {
|
|
|
24
24
|
return { ...state, filters: action.payload };
|
|
25
25
|
case "TOGGLE_FILTERS":
|
|
26
26
|
return { ...state, showFilters: !state.showFilters };
|
|
27
|
+
case "SET_SHOW_FILTERS":
|
|
28
|
+
return { ...state, showFilters: action.payload };
|
|
27
29
|
case "RESET_PAGINATION":
|
|
28
30
|
return { ...state, skip: 0 };
|
|
29
31
|
case "RESET_FILTERS":
|
|
@@ -55,14 +55,12 @@ function useRelationData(relatedModelName, searchTerm, isOpen, currentValueId) {
|
|
|
55
55
|
});
|
|
56
56
|
const relatedItems = useMemo(() => {
|
|
57
57
|
if (relationError) {
|
|
58
|
-
console.warn("[RelationData] GraphQL error:", relationError.message);
|
|
59
58
|
return [];
|
|
60
59
|
}
|
|
61
60
|
if (!relatedData) return [];
|
|
62
61
|
try {
|
|
63
62
|
const items = relatedData[relatedDataPath] || [];
|
|
64
63
|
if (!Array.isArray(items)) {
|
|
65
|
-
console.warn("[RelationData] Expected array but got:", typeof items);
|
|
66
64
|
return [];
|
|
67
65
|
}
|
|
68
66
|
return items.filter((item) => {
|
|
@@ -71,7 +69,6 @@ function useRelationData(relatedModelName, searchTerm, isOpen, currentValueId) {
|
|
|
71
69
|
return true;
|
|
72
70
|
});
|
|
73
71
|
} catch (error) {
|
|
74
|
-
console.error("[RelationData] Error processing relation data:", error);
|
|
75
72
|
return [];
|
|
76
73
|
}
|
|
77
74
|
}, [relatedData, relatedDataPath, relationError]);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useState, useRef, useMemo } from "react";
|
|
2
|
-
import { getPluralName } from "
|
|
2
|
+
import { getPluralName } from "../utils/get-plural-names.js";
|
|
3
3
|
import { useNavigate, useParams, Outlet } from "react-router";
|
|
4
4
|
import { AdminLocalStorage } from "../utils/secure-storage.js";
|
|
5
5
|
import { spacedWords, kebabCase } from "../utils/string-utils.js";
|
|
@@ -132,7 +132,8 @@ function AdminDataCreatePageContent({
|
|
|
132
132
|
}) {
|
|
133
133
|
const navigate = useNavigate();
|
|
134
134
|
const {
|
|
135
|
-
sdk
|
|
135
|
+
sdk,
|
|
136
|
+
databaseModels
|
|
136
137
|
} = useAdminDataContext();
|
|
137
138
|
const [submissionState, setSubmissionState] = useState({
|
|
138
139
|
status: "idle"
|
|
@@ -141,7 +142,6 @@ function AdminDataCreatePageContent({
|
|
|
141
142
|
try {
|
|
142
143
|
return getAdminDocuments(sdk, model);
|
|
143
144
|
} catch (error) {
|
|
144
|
-
console.error("[AdminDataCreatePage] Error getting documents:", error);
|
|
145
145
|
return null;
|
|
146
146
|
}
|
|
147
147
|
}, [sdk, model]);
|
|
@@ -154,7 +154,6 @@ function AdminDataCreatePageContent({
|
|
|
154
154
|
}
|
|
155
155
|
return gql(documents.create);
|
|
156
156
|
} catch (error) {
|
|
157
|
-
console.error("[AdminDataCreatePage] Error parsing CREATE mutation:", error);
|
|
158
157
|
return null;
|
|
159
158
|
}
|
|
160
159
|
}, [documents]);
|
|
@@ -166,43 +165,43 @@ function AdminDataCreatePageContent({
|
|
|
166
165
|
if (!documents || !CREATE_MUTATION) {
|
|
167
166
|
return /* @__PURE__ */ React.createElement("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", __self: this, __source: {
|
|
168
167
|
fileName: _jsxFileName,
|
|
169
|
-
lineNumber:
|
|
168
|
+
lineNumber: 220,
|
|
170
169
|
columnNumber: 7
|
|
171
170
|
} }, /* @__PURE__ */ React.createElement("div", { className: "mt-8 sm:mx-auto sm:w-full sm:max-w-2xl", __self: this, __source: {
|
|
172
171
|
fileName: _jsxFileName,
|
|
173
|
-
lineNumber:
|
|
172
|
+
lineNumber: 221,
|
|
174
173
|
columnNumber: 9
|
|
175
174
|
} }, /* @__PURE__ */ React.createElement("div", { className: "bg-white dark:bg-gray-800 py-8 px-4 shadow sm:rounded-lg sm:px-10", __self: this, __source: {
|
|
176
175
|
fileName: _jsxFileName,
|
|
177
|
-
lineNumber:
|
|
176
|
+
lineNumber: 222,
|
|
178
177
|
columnNumber: 11
|
|
179
178
|
} }, /* @__PURE__ */ React.createElement("div", { className: "text-center", __self: this, __source: {
|
|
180
179
|
fileName: _jsxFileName,
|
|
181
|
-
lineNumber:
|
|
180
|
+
lineNumber: 223,
|
|
182
181
|
columnNumber: 13
|
|
183
182
|
} }, /* @__PURE__ */ React.createElement(ExclamationCircleIcon, { className: "mx-auto h-12 w-12 text-red-400", __self: this, __source: {
|
|
184
183
|
fileName: _jsxFileName,
|
|
185
|
-
lineNumber:
|
|
184
|
+
lineNumber: 224,
|
|
186
185
|
columnNumber: 15
|
|
187
186
|
} }), /* @__PURE__ */ React.createElement("h2", { className: "mt-4 text-lg font-medium text-gray-900 dark:text-gray-100", __self: this, __source: {
|
|
188
187
|
fileName: _jsxFileName,
|
|
189
|
-
lineNumber:
|
|
188
|
+
lineNumber: 225,
|
|
190
189
|
columnNumber: 15
|
|
191
190
|
} }, "GraphQL Schema Error"), /* @__PURE__ */ React.createElement("p", { className: "mt-2 text-sm text-gray-600 dark:text-gray-400", __self: this, __source: {
|
|
192
191
|
fileName: _jsxFileName,
|
|
193
|
-
lineNumber:
|
|
192
|
+
lineNumber: 226,
|
|
194
193
|
columnNumber: 15
|
|
195
194
|
} }, "Unable to load GraphQL documents for this model. Please ensure the API server is running and the GraphQL schema is up to date."), /* @__PURE__ */ React.createElement("div", { className: "mt-6", __self: this, __source: {
|
|
196
195
|
fileName: _jsxFileName,
|
|
197
|
-
lineNumber:
|
|
196
|
+
lineNumber: 230,
|
|
198
197
|
columnNumber: 15
|
|
199
198
|
} }, /* @__PURE__ */ React.createElement(Link, { to: basePath, className: "w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-green-web hover:bg-green-web-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-web", __self: this, __source: {
|
|
200
199
|
fileName: _jsxFileName,
|
|
201
|
-
lineNumber:
|
|
200
|
+
lineNumber: 231,
|
|
202
201
|
columnNumber: 17
|
|
203
202
|
} }, "Return to Data Browser"))))));
|
|
204
203
|
}
|
|
205
|
-
const formFields = buildFormFields(sdk, model, "create", void 0, submissionState.status === "loading", basePath);
|
|
204
|
+
const formFields = buildFormFields(sdk, model, "create", void 0, submissionState.status === "loading", basePath, databaseModels);
|
|
206
205
|
const handleSubmit = async (formData) => {
|
|
207
206
|
try {
|
|
208
207
|
setSubmissionState({
|
|
@@ -215,7 +214,6 @@ function AdminDataCreatePageContent({
|
|
|
215
214
|
}
|
|
216
215
|
});
|
|
217
216
|
if (result.errors) {
|
|
218
|
-
console.error("GraphQL errors:", result.errors);
|
|
219
217
|
setSubmissionState({
|
|
220
218
|
status: "error",
|
|
221
219
|
message: result.errors.map((err) => err.message).join(", ")
|
|
@@ -230,7 +228,6 @@ function AdminDataCreatePageContent({
|
|
|
230
228
|
navigate(`${basePath}/${toKebabCase(model.pluralName)}`);
|
|
231
229
|
}, 1500);
|
|
232
230
|
} catch (error) {
|
|
233
|
-
console.error("Error creating record:", error);
|
|
234
231
|
setSubmissionState({
|
|
235
232
|
status: "error",
|
|
236
233
|
message: error instanceof Error ? error.message : "An unexpected error occurred"
|
|
@@ -249,115 +246,115 @@ function AdminDataCreatePageContent({
|
|
|
249
246
|
}, [submissionState.status]);
|
|
250
247
|
return /* @__PURE__ */ React.createElement("div", { className: "space-y-6", __self: this, __source: {
|
|
251
248
|
fileName: _jsxFileName,
|
|
252
|
-
lineNumber:
|
|
249
|
+
lineNumber: 299,
|
|
253
250
|
columnNumber: 5
|
|
254
251
|
} }, /* @__PURE__ */ React.createElement("div", { className: "mb-8", __self: this, __source: {
|
|
255
252
|
fileName: _jsxFileName,
|
|
256
|
-
lineNumber:
|
|
253
|
+
lineNumber: 301,
|
|
257
254
|
columnNumber: 7
|
|
258
255
|
} }, /* @__PURE__ */ React.createElement("nav", { className: "flex mb-6", "aria-label": "Breadcrumb", __self: this, __source: {
|
|
259
256
|
fileName: _jsxFileName,
|
|
260
|
-
lineNumber:
|
|
257
|
+
lineNumber: 302,
|
|
261
258
|
columnNumber: 11
|
|
262
259
|
} }, /* @__PURE__ */ React.createElement("ol", { className: "flex items-center space-x-4", __self: this, __source: {
|
|
263
260
|
fileName: _jsxFileName,
|
|
264
|
-
lineNumber:
|
|
261
|
+
lineNumber: 303,
|
|
265
262
|
columnNumber: 13
|
|
266
263
|
} }, /* @__PURE__ */ React.createElement("li", { __self: this, __source: {
|
|
267
264
|
fileName: _jsxFileName,
|
|
268
|
-
lineNumber:
|
|
265
|
+
lineNumber: 304,
|
|
269
266
|
columnNumber: 15
|
|
270
267
|
} }, /* @__PURE__ */ React.createElement(Link, { to: basePath, className: "text-gray-400 dark:text-gray-500 hover:text-gray-500 dark:hover:text-gray-400", __self: this, __source: {
|
|
271
268
|
fileName: _jsxFileName,
|
|
272
|
-
lineNumber:
|
|
269
|
+
lineNumber: 305,
|
|
273
270
|
columnNumber: 17
|
|
274
271
|
} }, "Data Browser")), /* @__PURE__ */ React.createElement("li", { __self: this, __source: {
|
|
275
272
|
fileName: _jsxFileName,
|
|
276
|
-
lineNumber:
|
|
273
|
+
lineNumber: 309,
|
|
277
274
|
columnNumber: 15
|
|
278
275
|
} }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center", __self: this, __source: {
|
|
279
276
|
fileName: _jsxFileName,
|
|
280
|
-
lineNumber:
|
|
277
|
+
lineNumber: 310,
|
|
281
278
|
columnNumber: 17
|
|
282
279
|
} }, /* @__PURE__ */ React.createElement("svg", { className: "flex-shrink-0 h-5 w-5 text-gray-300 dark:text-gray-600", fill: "currentColor", viewBox: "0 0 20 20", __self: this, __source: {
|
|
283
280
|
fileName: _jsxFileName,
|
|
284
|
-
lineNumber:
|
|
281
|
+
lineNumber: 311,
|
|
285
282
|
columnNumber: 19
|
|
286
283
|
} }, /* @__PURE__ */ React.createElement("path", { fillRule: "evenodd", d: "M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 111.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z", clipRule: "evenodd", __self: this, __source: {
|
|
287
284
|
fileName: _jsxFileName,
|
|
288
|
-
lineNumber:
|
|
285
|
+
lineNumber: 316,
|
|
289
286
|
columnNumber: 21
|
|
290
287
|
} })), /* @__PURE__ */ React.createElement(Link, { to: `${basePath}/${toKebabCase(model.pluralName)}`, className: "ml-4 text-gray-400 dark:text-gray-500 hover:text-gray-500 dark:hover:text-gray-400", __self: this, __source: {
|
|
291
288
|
fileName: _jsxFileName,
|
|
292
|
-
lineNumber:
|
|
289
|
+
lineNumber: 322,
|
|
293
290
|
columnNumber: 19
|
|
294
291
|
} }, toReadableText(model.pluralName)))), /* @__PURE__ */ React.createElement("li", { __self: this, __source: {
|
|
295
292
|
fileName: _jsxFileName,
|
|
296
|
-
lineNumber:
|
|
293
|
+
lineNumber: 330,
|
|
297
294
|
columnNumber: 15
|
|
298
295
|
} }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center", __self: this, __source: {
|
|
299
296
|
fileName: _jsxFileName,
|
|
300
|
-
lineNumber:
|
|
297
|
+
lineNumber: 331,
|
|
301
298
|
columnNumber: 17
|
|
302
299
|
} }, /* @__PURE__ */ React.createElement("svg", { className: "flex-shrink-0 h-5 w-5 text-gray-300 dark:text-gray-600", fill: "currentColor", viewBox: "0 0 20 20", __self: this, __source: {
|
|
303
300
|
fileName: _jsxFileName,
|
|
304
|
-
lineNumber:
|
|
301
|
+
lineNumber: 332,
|
|
305
302
|
columnNumber: 19
|
|
306
303
|
} }, /* @__PURE__ */ React.createElement("path", { fillRule: "evenodd", d: "M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 111.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z", clipRule: "evenodd", __self: this, __source: {
|
|
307
304
|
fileName: _jsxFileName,
|
|
308
|
-
lineNumber:
|
|
305
|
+
lineNumber: 337,
|
|
309
306
|
columnNumber: 21
|
|
310
307
|
} })), /* @__PURE__ */ React.createElement("span", { className: "ml-4 text-gray-500 dark:text-gray-400", __self: this, __source: {
|
|
311
308
|
fileName: _jsxFileName,
|
|
312
|
-
lineNumber:
|
|
309
|
+
lineNumber: 343,
|
|
313
310
|
columnNumber: 19
|
|
314
311
|
} }, "Create New"))))), /* @__PURE__ */ React.createElement("h1", { className: "mt-4 text-3xl font-bold text-gray-900 dark:text-gray-100", __self: this, __source: {
|
|
315
312
|
fileName: _jsxFileName,
|
|
316
|
-
lineNumber:
|
|
313
|
+
lineNumber: 348,
|
|
317
314
|
columnNumber: 11
|
|
318
315
|
} }, "Create ", toReadableText(model.name))), submissionState.status !== "idle" && /* @__PURE__ */ React.createElement("div", { 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"}`, __self: this, __source: {
|
|
319
316
|
fileName: _jsxFileName,
|
|
320
|
-
lineNumber:
|
|
317
|
+
lineNumber: 355,
|
|
321
318
|
columnNumber: 11
|
|
322
319
|
} }, /* @__PURE__ */ React.createElement("div", { className: "flex", __self: this, __source: {
|
|
323
320
|
fileName: _jsxFileName,
|
|
324
|
-
lineNumber:
|
|
321
|
+
lineNumber: 364,
|
|
325
322
|
columnNumber: 13
|
|
326
323
|
} }, /* @__PURE__ */ React.createElement("div", { className: "flex-shrink-0", __self: this, __source: {
|
|
327
324
|
fileName: _jsxFileName,
|
|
328
|
-
lineNumber:
|
|
325
|
+
lineNumber: 365,
|
|
329
326
|
columnNumber: 15
|
|
330
327
|
} }, submissionState.status === "success" ? /* @__PURE__ */ React.createElement(CheckCircleIcon, { className: "h-5 w-5 text-green-400", __self: this, __source: {
|
|
331
328
|
fileName: _jsxFileName,
|
|
332
|
-
lineNumber:
|
|
329
|
+
lineNumber: 367,
|
|
333
330
|
columnNumber: 19
|
|
334
331
|
} }) : submissionState.status === "error" ? /* @__PURE__ */ React.createElement(ExclamationCircleIcon, { className: "h-5 w-5 text-red-400", __self: this, __source: {
|
|
335
332
|
fileName: _jsxFileName,
|
|
336
|
-
lineNumber:
|
|
333
|
+
lineNumber: 369,
|
|
337
334
|
columnNumber: 19
|
|
338
335
|
} }) : /* @__PURE__ */ React.createElement("div", { className: "h-5 w-5 border-2 border-blue-400 border-t-transparent rounded-full animate-spin", __self: this, __source: {
|
|
339
336
|
fileName: _jsxFileName,
|
|
340
|
-
lineNumber:
|
|
337
|
+
lineNumber: 371,
|
|
341
338
|
columnNumber: 19
|
|
342
339
|
} })), /* @__PURE__ */ React.createElement("div", { className: "ml-3", __self: this, __source: {
|
|
343
340
|
fileName: _jsxFileName,
|
|
344
|
-
lineNumber:
|
|
341
|
+
lineNumber: 374,
|
|
345
342
|
columnNumber: 15
|
|
346
343
|
} }, /* @__PURE__ */ React.createElement("p", { className: `text-sm font-medium ${submissionState.status === "success" ? "text-green-800" : submissionState.status === "error" ? "text-red-800" : "text-blue-800"}`, __self: this, __source: {
|
|
347
344
|
fileName: _jsxFileName,
|
|
348
|
-
lineNumber:
|
|
345
|
+
lineNumber: 375,
|
|
349
346
|
columnNumber: 17
|
|
350
347
|
} }, submissionState.status === "loading" ? `Creating ${toReadableText(model.name)}...` : submissionState.message)))), /* @__PURE__ */ React.createElement("div", { className: "bg-white dark:bg-gray-800 shadow-sm rounded-lg", __self: this, __source: {
|
|
351
348
|
fileName: _jsxFileName,
|
|
352
|
-
lineNumber:
|
|
349
|
+
lineNumber: 394,
|
|
353
350
|
columnNumber: 9
|
|
354
351
|
} }, /* @__PURE__ */ React.createElement("div", { className: "px-6 py-8", __self: this, __source: {
|
|
355
352
|
fileName: _jsxFileName,
|
|
356
|
-
lineNumber:
|
|
353
|
+
lineNumber: 395,
|
|
357
354
|
columnNumber: 11
|
|
358
355
|
} }, /* @__PURE__ */ React.createElement(Form, { id: `create-${model.name.toLowerCase()}-form`, fields: formFields, submit: handleSubmit, disabled: submissionState.status === "loading", theme: formTheme, __self: this, __source: {
|
|
359
356
|
fileName: _jsxFileName,
|
|
360
|
-
lineNumber:
|
|
357
|
+
lineNumber: 396,
|
|
361
358
|
columnNumber: 13
|
|
362
359
|
} }))));
|
|
363
360
|
}
|
|
@@ -366,7 +363,7 @@ function AdminDataCreateErrorBoundary({
|
|
|
366
363
|
}) {
|
|
367
364
|
return /* @__PURE__ */ React.createElement(ErrorBoundary, { error, __self: this, __source: {
|
|
368
365
|
fileName: _jsxFileName,
|
|
369
|
-
lineNumber:
|
|
366
|
+
lineNumber: 410,
|
|
370
367
|
columnNumber: 10
|
|
371
368
|
} });
|
|
372
369
|
}
|