@nestledjs/data-browser 0.1.25 → 1.0.0
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/index.js +2 -0
- package/lib/components/AdminDataStateMessage.d.ts +15 -0
- package/lib/components/AdminDataStateMessage.js +44 -0
- package/lib/components/FilterField.d.ts +14 -0
- package/lib/components/FilterField.js +152 -0
- package/lib/components/filters/EnumFilter.d.ts +8 -0
- package/lib/components/filters/EnumFilter.js +39 -0
- package/lib/components/filters/index.d.ts +1 -0
- package/lib/context/AdminDataContext.d.ts +16 -1
- package/lib/context/AdminDataContext.js +4 -3
- package/lib/pages/AdminDataCreatePage.js +9 -4
- package/lib/pages/AdminDataEditPage.js +313 -246
- package/lib/pages/AdminDataListPage.js +65 -101
- package/lib/utils/graphql-utils.d.ts +12 -1
- package/lib/utils/graphql-utils.js +59 -36
- package/lib/utils/string-utils.js +14 -0
- package/package.json +2 -2
|
@@ -8,6 +8,7 @@ import { TrashIcon } from "@heroicons/react/24/solid";
|
|
|
8
8
|
import { ErrorBoundary } from "@nestledjs/shared-components";
|
|
9
9
|
import { Form } from "@nestledjs/forms";
|
|
10
10
|
import { useAdminDataContext } from "../context/AdminDataContext.js";
|
|
11
|
+
import { AdminDataStateMessage } from "../components/AdminDataStateMessage.js";
|
|
11
12
|
import { useParams, Link, useNavigate } from "react-router";
|
|
12
13
|
import { getAdminDocuments, buildFormFields, cleanFormInput } from "../utils/graphql-utils.js";
|
|
13
14
|
function toLowerCamelCase(name) {
|
|
@@ -34,6 +35,106 @@ const sanitizeInput = (input) => {
|
|
|
34
35
|
const toKebabCase = (str) => {
|
|
35
36
|
return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
36
37
|
};
|
|
38
|
+
function processRelationFieldValue(field, item) {
|
|
39
|
+
var _a;
|
|
40
|
+
const relationFieldName = ((_a = field.relationFromFields) == null ? void 0 : _a[0]) || `${field.name}Id`;
|
|
41
|
+
let value = item[relationFieldName];
|
|
42
|
+
if (value === void 0) {
|
|
43
|
+
const relationObject = item[field.name];
|
|
44
|
+
if (relationObject && typeof relationObject === "object" && relationObject.id) {
|
|
45
|
+
value = relationObject.id;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (value && typeof value === "object") {
|
|
49
|
+
value = value.id || "";
|
|
50
|
+
}
|
|
51
|
+
return value || "";
|
|
52
|
+
}
|
|
53
|
+
function processDateFieldValue(field, value) {
|
|
54
|
+
if (value === null || value === void 0 || value === "") {
|
|
55
|
+
return "";
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
const dateValue = value instanceof Date ? value : new Date(value);
|
|
59
|
+
const fieldTypeLower = field.type.toLowerCase();
|
|
60
|
+
if (fieldTypeLower === "date") {
|
|
61
|
+
return dateValue.toISOString().split("T")[0];
|
|
62
|
+
} else {
|
|
63
|
+
return dateValue.toISOString().substring(0, 16);
|
|
64
|
+
}
|
|
65
|
+
} catch (e) {
|
|
66
|
+
return "";
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function sanitizeFieldValue(value, field) {
|
|
70
|
+
const fieldTypeLower = field.type.toLowerCase();
|
|
71
|
+
if (value === null) {
|
|
72
|
+
return fieldTypeLower === "boolean" ? false : "";
|
|
73
|
+
}
|
|
74
|
+
if (fieldTypeLower === "boolean") {
|
|
75
|
+
return Boolean(value);
|
|
76
|
+
}
|
|
77
|
+
if (value !== null && typeof value === "object" && "id" in value) {
|
|
78
|
+
return value.id || "";
|
|
79
|
+
}
|
|
80
|
+
if (typeof value === "object") {
|
|
81
|
+
return "";
|
|
82
|
+
}
|
|
83
|
+
return value;
|
|
84
|
+
}
|
|
85
|
+
function performFinalSafetyChecks(initialValues, model) {
|
|
86
|
+
for (const [key, value] of Object.entries(initialValues)) {
|
|
87
|
+
if (value === void 0) {
|
|
88
|
+
initialValues[key] = "";
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (value === null || typeof value !== "object") {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if ("id" in value && typeof value.id === "string") {
|
|
95
|
+
initialValues[key] = value.id;
|
|
96
|
+
} else if (value instanceof Date) {
|
|
97
|
+
const field = model.fields.find((f) => f.name === key);
|
|
98
|
+
initialValues[key] = (field == null ? void 0 : field.type.toLowerCase()) === "date" ? value.toISOString().split("T")[0] : value.toISOString();
|
|
99
|
+
} else {
|
|
100
|
+
initialValues[key] = "";
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function extractInitialValues(model, item) {
|
|
105
|
+
const initialValues = {};
|
|
106
|
+
if (!item) {
|
|
107
|
+
return initialValues;
|
|
108
|
+
}
|
|
109
|
+
const idField = model.fields.find((f) => f.isId);
|
|
110
|
+
if (idField) {
|
|
111
|
+
initialValues[idField.name] = item[idField.name];
|
|
112
|
+
}
|
|
113
|
+
const editableFields = model.fields.filter((field) => {
|
|
114
|
+
if (field.isId || field.isReadOnly || field.isGenerated) return false;
|
|
115
|
+
if (field.isUpdatedAt || field.name === "createdAt") return false;
|
|
116
|
+
if (field.relationName && field.isList) return false;
|
|
117
|
+
return true;
|
|
118
|
+
});
|
|
119
|
+
editableFields.forEach((field) => {
|
|
120
|
+
var _a;
|
|
121
|
+
let value = item[field.name];
|
|
122
|
+
if (field.relationName && !field.isList) {
|
|
123
|
+
const relationFieldName = ((_a = field.relationFromFields) == null ? void 0 : _a[0]) || `${field.name}Id`;
|
|
124
|
+
initialValues[relationFieldName] = processRelationFieldValue(field, item);
|
|
125
|
+
} else {
|
|
126
|
+
const fieldTypeLower = field.type.toLowerCase();
|
|
127
|
+
if (fieldTypeLower === "datetime" || fieldTypeLower === "date") {
|
|
128
|
+
value = processDateFieldValue(field, value);
|
|
129
|
+
} else {
|
|
130
|
+
value = sanitizeFieldValue(value, field);
|
|
131
|
+
}
|
|
132
|
+
initialValues[field.name] = value;
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
performFinalSafetyChecks(initialValues, model);
|
|
136
|
+
return initialValues;
|
|
137
|
+
}
|
|
37
138
|
const validateId = (id) => {
|
|
38
139
|
if (!id) return null;
|
|
39
140
|
const sanitized = sanitizeInput(id);
|
|
@@ -45,7 +146,7 @@ const validateId = (id) => {
|
|
|
45
146
|
};
|
|
46
147
|
function AdminDataEditPage() {
|
|
47
148
|
const { dataType, id } = useParams();
|
|
48
|
-
const { databaseModels, basePath = "/admin/data", formTheme } = useAdminDataContext();
|
|
149
|
+
const { databaseModels, basePath = "/admin/data", formTheme, displayFieldConfig } = useAdminDataContext();
|
|
49
150
|
const findModelByName = (name) => {
|
|
50
151
|
return databaseModels.find((model2) => model2.name === name);
|
|
51
152
|
};
|
|
@@ -99,9 +200,138 @@ function AdminDataEditPage() {
|
|
|
99
200
|
) })
|
|
100
201
|
] }) }) }) });
|
|
101
202
|
}
|
|
102
|
-
return /* @__PURE__ */ jsx(AdminDataEditPageContent, { model, id: validatedId, basePath, formTheme });
|
|
203
|
+
return /* @__PURE__ */ jsx(AdminDataEditPageContent, { model, id: validatedId, basePath, formTheme, displayFieldConfig });
|
|
204
|
+
}
|
|
205
|
+
function DeleteConfirmModal({ show, modelName, isDeleting, onConfirm, onCancel }) {
|
|
206
|
+
if (!show) return null;
|
|
207
|
+
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: [
|
|
208
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
|
|
209
|
+
/* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsx(ExclamationCircleIcon, { className: "h-6 w-6 text-red-600" }) }),
|
|
210
|
+
/* @__PURE__ */ jsxs("div", { className: "ml-3", children: [
|
|
211
|
+
/* @__PURE__ */ jsxs("h3", { className: "text-lg font-medium text-gray-900 dark:text-gray-100", children: [
|
|
212
|
+
"Delete ",
|
|
213
|
+
toReadableText(modelName)
|
|
214
|
+
] }),
|
|
215
|
+
/* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsxs("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: [
|
|
216
|
+
"Are you sure you want to delete this ",
|
|
217
|
+
toReadableText(modelName).toLowerCase(),
|
|
218
|
+
"? This action cannot be undone."
|
|
219
|
+
] }) })
|
|
220
|
+
] })
|
|
221
|
+
] }),
|
|
222
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-5 sm:mt-4 sm:flex sm:flex-row-reverse", children: [
|
|
223
|
+
/* @__PURE__ */ jsx(
|
|
224
|
+
"button",
|
|
225
|
+
{
|
|
226
|
+
onClick: onConfirm,
|
|
227
|
+
disabled: isDeleting,
|
|
228
|
+
className: "w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm disabled:opacity-50 disabled:cursor-not-allowed",
|
|
229
|
+
children: isDeleting ? "Deleting..." : "Delete"
|
|
230
|
+
}
|
|
231
|
+
),
|
|
232
|
+
/* @__PURE__ */ jsx(
|
|
233
|
+
"button",
|
|
234
|
+
{
|
|
235
|
+
onClick: onCancel,
|
|
236
|
+
disabled: isDeleting,
|
|
237
|
+
className: "mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-web sm:mt-0 sm:w-auto sm:text-sm disabled:opacity-50 disabled:cursor-not-allowed",
|
|
238
|
+
children: "Cancel"
|
|
239
|
+
}
|
|
240
|
+
)
|
|
241
|
+
] })
|
|
242
|
+
] }) });
|
|
103
243
|
}
|
|
104
|
-
function
|
|
244
|
+
function getStatusColors(isSuccess, isError) {
|
|
245
|
+
if (isSuccess) {
|
|
246
|
+
return {
|
|
247
|
+
bgColor: "bg-green-50 border border-green-200",
|
|
248
|
+
textColor: "text-green-800"
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
if (isError) {
|
|
252
|
+
return {
|
|
253
|
+
bgColor: "bg-red-50 border border-red-200",
|
|
254
|
+
textColor: "text-red-800"
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
return {
|
|
258
|
+
bgColor: "bg-blue-50 border border-blue-200",
|
|
259
|
+
textColor: "text-blue-800"
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
function getStatusIcon(isSuccess, isError) {
|
|
263
|
+
if (isSuccess) {
|
|
264
|
+
return /* @__PURE__ */ jsx(CheckCircleIcon, { className: "h-5 w-5 text-green-400" });
|
|
265
|
+
}
|
|
266
|
+
if (isError) {
|
|
267
|
+
return /* @__PURE__ */ jsx(ExclamationCircleIcon, { className: "h-5 w-5 text-red-400" });
|
|
268
|
+
}
|
|
269
|
+
return /* @__PURE__ */ jsx("div", { className: "h-5 w-5 border-2 border-blue-400 border-t-transparent rounded-full animate-spin" });
|
|
270
|
+
}
|
|
271
|
+
function StatusMessage({ submissionStatus, deleteStatus, submissionMessage, deleteMessage, modelName }) {
|
|
272
|
+
if (submissionStatus === "idle" && deleteStatus === "idle") return null;
|
|
273
|
+
const isSuccess = submissionStatus === "success" || deleteStatus === "success";
|
|
274
|
+
const isError = submissionStatus === "error" || deleteStatus === "error";
|
|
275
|
+
const isLoading = submissionStatus === "loading" || deleteStatus === "loading";
|
|
276
|
+
const { bgColor, textColor } = getStatusColors(isSuccess, isError);
|
|
277
|
+
const loadingMessage = submissionStatus === "loading" ? `Updating ${toReadableText(modelName)}...` : `Deleting ${toReadableText(modelName)}...`;
|
|
278
|
+
const message = isLoading ? loadingMessage : submissionMessage || deleteMessage;
|
|
279
|
+
return /* @__PURE__ */ jsx("div", { className: `mb-6 rounded-md p-4 ${bgColor}`, children: /* @__PURE__ */ jsxs("div", { className: "flex", children: [
|
|
280
|
+
/* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: getStatusIcon(isSuccess, isError) }),
|
|
281
|
+
/* @__PURE__ */ jsx("div", { className: "ml-3", children: /* @__PURE__ */ jsx("p", { className: `text-sm font-medium ${textColor}`, children: message }) })
|
|
282
|
+
] }) });
|
|
283
|
+
}
|
|
284
|
+
async function executeUpdateMutation(updateMutation, formData, model, id, idVariableName) {
|
|
285
|
+
try {
|
|
286
|
+
const cleanedInput = cleanFormInput(formData, model);
|
|
287
|
+
const result = await updateMutation({
|
|
288
|
+
variables: {
|
|
289
|
+
input: cleanedInput,
|
|
290
|
+
[idVariableName]: id
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
if (result.errors) {
|
|
294
|
+
return {
|
|
295
|
+
success: false,
|
|
296
|
+
message: result.errors.map((err) => err.message).join(", ")
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
return {
|
|
300
|
+
success: true,
|
|
301
|
+
message: `${toReadableText(model.name)} updated successfully!`
|
|
302
|
+
};
|
|
303
|
+
} catch (error) {
|
|
304
|
+
return {
|
|
305
|
+
success: false,
|
|
306
|
+
message: error instanceof Error ? error.message : "An unexpected error occurred"
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
async function executeDeleteMutation(deleteMutation, id, idVariableName, model) {
|
|
311
|
+
try {
|
|
312
|
+
const result = await deleteMutation({
|
|
313
|
+
variables: {
|
|
314
|
+
[idVariableName]: id
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
if (result.errors) {
|
|
318
|
+
return {
|
|
319
|
+
success: false,
|
|
320
|
+
message: result.errors.map((err) => err.message).join(", ")
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
return {
|
|
324
|
+
success: true,
|
|
325
|
+
message: `${toReadableText(model.name)} deleted successfully!`
|
|
326
|
+
};
|
|
327
|
+
} catch (error) {
|
|
328
|
+
return {
|
|
329
|
+
success: false,
|
|
330
|
+
message: error instanceof Error ? error.message : "An unexpected error occurred"
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
function AdminDataEditPageContent({ model, id, basePath, formTheme, displayFieldConfig }) {
|
|
105
335
|
const navigate = useNavigate();
|
|
106
336
|
const { sdk, databaseModels } = useAdminDataContext();
|
|
107
337
|
const [submissionState, setSubmissionState] = useState({ status: "idle" });
|
|
@@ -195,216 +425,86 @@ function AdminDataEditPageContent({ model, id, basePath, formTheme }) {
|
|
|
195
425
|
}
|
|
196
426
|
}, [deleteState.status]);
|
|
197
427
|
if (!documents || !QUERY || !UPDATE_MUTATION || !DELETE_MUTATION) {
|
|
198
|
-
return /* @__PURE__ */ jsx(
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
children: "Return to Data Browser"
|
|
208
|
-
}
|
|
209
|
-
) })
|
|
210
|
-
] }) }) }) });
|
|
428
|
+
return /* @__PURE__ */ jsx(
|
|
429
|
+
AdminDataStateMessage,
|
|
430
|
+
{
|
|
431
|
+
type: "schema-error",
|
|
432
|
+
title: "GraphQL Schema Error",
|
|
433
|
+
message: "Unable to load GraphQL documents for this model. Please ensure the API server is running and the GraphQL schema is up to date.",
|
|
434
|
+
basePath
|
|
435
|
+
}
|
|
436
|
+
);
|
|
211
437
|
}
|
|
212
438
|
if (error) {
|
|
213
|
-
return /* @__PURE__ */ jsx(
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
),
|
|
226
|
-
/* @__PURE__ */ jsx(
|
|
227
|
-
Link,
|
|
228
|
-
{
|
|
229
|
-
to: `${basePath}/${toKebabCase(model.pluralName)}`,
|
|
230
|
-
className: "w-full flex justify-center py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-web",
|
|
231
|
-
children: "Back to List"
|
|
232
|
-
}
|
|
233
|
-
)
|
|
234
|
-
] })
|
|
235
|
-
] }) }) }) });
|
|
439
|
+
return /* @__PURE__ */ jsx(
|
|
440
|
+
AdminDataStateMessage,
|
|
441
|
+
{
|
|
442
|
+
type: "error",
|
|
443
|
+
title: "Error Loading Data",
|
|
444
|
+
message: error.message,
|
|
445
|
+
basePath,
|
|
446
|
+
onRetry: () => refetch(),
|
|
447
|
+
backLinkText: "Back to List",
|
|
448
|
+
backLinkPath: `${basePath}/${toKebabCase(model.pluralName)}`
|
|
449
|
+
}
|
|
450
|
+
);
|
|
236
451
|
}
|
|
237
452
|
if (loading) {
|
|
238
|
-
return /* @__PURE__ */ jsx(
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
"Loading
|
|
243
|
-
toReadableText(model.name)
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
453
|
+
return /* @__PURE__ */ jsx(
|
|
454
|
+
AdminDataStateMessage,
|
|
455
|
+
{
|
|
456
|
+
type: "loading",
|
|
457
|
+
title: "Loading...",
|
|
458
|
+
message: `Loading ${toReadableText(model.name)} data...`,
|
|
459
|
+
basePath
|
|
460
|
+
}
|
|
461
|
+
);
|
|
247
462
|
}
|
|
248
463
|
const item = data == null ? void 0 : data[responseFieldName];
|
|
249
464
|
if (!item) {
|
|
250
|
-
return /* @__PURE__ */ jsx(
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
"
|
|
255
|
-
toReadableText(model.name)
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
Link,
|
|
260
|
-
{
|
|
261
|
-
to: `${basePath}/${toKebabCase(model.pluralName)}`,
|
|
262
|
-
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",
|
|
263
|
-
children: "Back to List"
|
|
264
|
-
}
|
|
265
|
-
) })
|
|
266
|
-
] }) }) }) });
|
|
267
|
-
}
|
|
268
|
-
const formFields = buildFormFields(sdk, model, "update", item, submissionState.status === "loading", basePath, databaseModels);
|
|
269
|
-
const initialValues = {};
|
|
270
|
-
if (item) {
|
|
271
|
-
const editableFields = model.fields.filter((field) => {
|
|
272
|
-
if (field.isId && false) ;
|
|
273
|
-
if (field.isReadOnly || field.isGenerated) return false;
|
|
274
|
-
if (field.isUpdatedAt || field.name === "createdAt") return false;
|
|
275
|
-
if (field.relationName && field.isList) return false;
|
|
276
|
-
return true;
|
|
277
|
-
});
|
|
278
|
-
editableFields.forEach((field) => {
|
|
279
|
-
var _a;
|
|
280
|
-
let value = item[field.name];
|
|
281
|
-
if (field.relationName && !field.isList) {
|
|
282
|
-
const relationFieldName = ((_a = field.relationFromFields) == null ? void 0 : _a[0]) || `${field.name}Id`;
|
|
283
|
-
value = item[relationFieldName];
|
|
284
|
-
if (value === void 0) {
|
|
285
|
-
const relationObject = item[field.name];
|
|
286
|
-
if (relationObject && typeof relationObject === "object" && relationObject.id) {
|
|
287
|
-
value = relationObject.id;
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
if (value && typeof value === "object") {
|
|
291
|
-
value = value.id || "";
|
|
292
|
-
}
|
|
293
|
-
initialValues[relationFieldName] = value || "";
|
|
294
|
-
} else {
|
|
295
|
-
if (field.type.toLowerCase() === "datetime" || field.type.toLowerCase() === "date") {
|
|
296
|
-
if (value !== null && value !== void 0 && value !== "") {
|
|
297
|
-
try {
|
|
298
|
-
const dateValue = value instanceof Date ? value : new Date(value);
|
|
299
|
-
if (field.type.toLowerCase() === "date") {
|
|
300
|
-
value = dateValue.toISOString().split("T")[0];
|
|
301
|
-
} else {
|
|
302
|
-
const isoString = dateValue.toISOString();
|
|
303
|
-
value = isoString.substring(0, 16);
|
|
304
|
-
}
|
|
305
|
-
} catch (e) {
|
|
306
|
-
value = "";
|
|
307
|
-
}
|
|
308
|
-
} else {
|
|
309
|
-
value = "";
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
if (value === null && field.type.toLowerCase() !== "boolean") {
|
|
313
|
-
value = "";
|
|
314
|
-
}
|
|
315
|
-
if (field.type.toLowerCase() === "boolean") {
|
|
316
|
-
value = Boolean(value);
|
|
317
|
-
}
|
|
318
|
-
if (value !== null && typeof value === "object") {
|
|
319
|
-
if (typeof value === "object" && "id" in value) {
|
|
320
|
-
value = value.id;
|
|
321
|
-
} else {
|
|
322
|
-
value = "";
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
initialValues[field.name] = value;
|
|
326
|
-
}
|
|
327
|
-
});
|
|
328
|
-
for (const [key, value] of Object.entries(initialValues)) {
|
|
329
|
-
if (value === void 0) {
|
|
330
|
-
initialValues[key] = "";
|
|
331
|
-
continue;
|
|
332
|
-
}
|
|
333
|
-
if (value === null) {
|
|
334
|
-
continue;
|
|
335
|
-
}
|
|
336
|
-
if (typeof value !== "object") {
|
|
337
|
-
continue;
|
|
338
|
-
}
|
|
339
|
-
if ("id" in value && typeof value.id === "string") {
|
|
340
|
-
initialValues[key] = value.id;
|
|
341
|
-
} else if (value instanceof Date) {
|
|
342
|
-
const field = model.fields.find((f) => f.name === key);
|
|
343
|
-
initialValues[key] = (field == null ? void 0 : field.type.toLowerCase()) === "date" ? value.toISOString().split("T")[0] : value.toISOString();
|
|
344
|
-
} else {
|
|
345
|
-
initialValues[key] = "";
|
|
465
|
+
return /* @__PURE__ */ jsx(
|
|
466
|
+
AdminDataStateMessage,
|
|
467
|
+
{
|
|
468
|
+
type: "not-found",
|
|
469
|
+
title: "Not Found",
|
|
470
|
+
message: `The ${toReadableText(model.name)} you're looking for doesn't exist or has been deleted.`,
|
|
471
|
+
basePath,
|
|
472
|
+
backLinkText: "Back to List",
|
|
473
|
+
backLinkPath: `${basePath}/${toKebabCase(model.pluralName)}`
|
|
346
474
|
}
|
|
347
|
-
|
|
475
|
+
);
|
|
348
476
|
}
|
|
477
|
+
const formFields = buildFormFields(sdk, model, "update", {
|
|
478
|
+
currentItem: item,
|
|
479
|
+
isSubmitting: submissionState.status === "loading",
|
|
480
|
+
basePath,
|
|
481
|
+
databaseModels,
|
|
482
|
+
displayFieldConfig
|
|
483
|
+
});
|
|
484
|
+
const initialValues = extractInitialValues(model, item);
|
|
349
485
|
const handleSubmit = async (formData) => {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
});
|
|
359
|
-
if (result.errors) {
|
|
360
|
-
setSubmissionState({
|
|
361
|
-
status: "error",
|
|
362
|
-
message: result.errors.map((err) => err.message).join(", ")
|
|
363
|
-
});
|
|
364
|
-
window.scrollTo({ top: 0, behavior: "smooth" });
|
|
365
|
-
return;
|
|
366
|
-
}
|
|
367
|
-
setSubmissionState({
|
|
368
|
-
status: "success",
|
|
369
|
-
message: `${toReadableText(model.name)} updated successfully!`
|
|
370
|
-
});
|
|
371
|
-
window.scrollTo({ top: 0, behavior: "smooth" });
|
|
486
|
+
setSubmissionState({ status: "loading" });
|
|
487
|
+
const result = await executeUpdateMutation(updateMutation, formData, model, id, idVariableName);
|
|
488
|
+
setSubmissionState({
|
|
489
|
+
status: result.success ? "success" : "error",
|
|
490
|
+
message: result.message
|
|
491
|
+
});
|
|
492
|
+
window.scrollTo({ top: 0, behavior: "smooth" });
|
|
493
|
+
if (result.success) {
|
|
372
494
|
await refetch();
|
|
373
|
-
} catch (error2) {
|
|
374
|
-
setSubmissionState({
|
|
375
|
-
status: "error",
|
|
376
|
-
message: error2 instanceof Error ? error2.message : "An unexpected error occurred"
|
|
377
|
-
});
|
|
378
|
-
window.scrollTo({ top: 0, behavior: "smooth" });
|
|
379
495
|
}
|
|
380
496
|
};
|
|
381
497
|
const handleDelete = async () => {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
if (result.errors) {
|
|
390
|
-
setDeleteState({
|
|
391
|
-
status: "error",
|
|
392
|
-
message: result.errors.map((err) => err.message).join(", ")
|
|
393
|
-
});
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
396
|
-
setDeleteState({
|
|
397
|
-
status: "success",
|
|
398
|
-
message: `${toReadableText(model.name)} deleted successfully!`
|
|
399
|
-
});
|
|
498
|
+
setDeleteState({ status: "loading" });
|
|
499
|
+
const result = await executeDeleteMutation(deleteMutation, id, idVariableName, model);
|
|
500
|
+
setDeleteState({
|
|
501
|
+
status: result.success ? "success" : "error",
|
|
502
|
+
message: result.message
|
|
503
|
+
});
|
|
504
|
+
if (result.success) {
|
|
400
505
|
setTimeout(() => {
|
|
401
506
|
navigate(`${basePath}/${toKebabCase(model.pluralName)}`);
|
|
402
507
|
}, 1500);
|
|
403
|
-
} catch (error2) {
|
|
404
|
-
setDeleteState({
|
|
405
|
-
status: "error",
|
|
406
|
-
message: error2 instanceof Error ? error2.message : "An unexpected error occurred"
|
|
407
|
-
});
|
|
408
508
|
}
|
|
409
509
|
};
|
|
410
510
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
@@ -476,57 +576,24 @@ function AdminDataEditPageContent({ model, id, basePath, formTheme }) {
|
|
|
476
576
|
)
|
|
477
577
|
] })
|
|
478
578
|
] }),
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
/* @__PURE__ */ jsx("div", { className: "flex-shrink-0", children: /* @__PURE__ */ jsx(ExclamationCircleIcon, { className: "h-6 w-6 text-red-600" }) }),
|
|
482
|
-
/* @__PURE__ */ jsxs("div", { className: "ml-3", children: [
|
|
483
|
-
/* @__PURE__ */ jsxs("h3", { className: "text-lg font-medium text-gray-900 dark:text-gray-100", children: [
|
|
484
|
-
"Delete ",
|
|
485
|
-
toReadableText(model.name)
|
|
486
|
-
] }),
|
|
487
|
-
/* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsxs("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: [
|
|
488
|
-
"Are you sure you want to delete this",
|
|
489
|
-
" ",
|
|
490
|
-
toReadableText(model.name).toLowerCase(),
|
|
491
|
-
"? This action cannot be undone."
|
|
492
|
-
] }) })
|
|
493
|
-
] })
|
|
494
|
-
] }),
|
|
495
|
-
/* @__PURE__ */ jsxs("div", { className: "mt-5 sm:mt-4 sm:flex sm:flex-row-reverse", children: [
|
|
496
|
-
/* @__PURE__ */ jsx(
|
|
497
|
-
"button",
|
|
498
|
-
{
|
|
499
|
-
onClick: handleDelete,
|
|
500
|
-
disabled: deleteState.status === "loading",
|
|
501
|
-
className: "w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm disabled:opacity-50 disabled:cursor-not-allowed",
|
|
502
|
-
children: deleteState.status === "loading" ? "Deleting..." : "Delete"
|
|
503
|
-
}
|
|
504
|
-
),
|
|
505
|
-
/* @__PURE__ */ jsx(
|
|
506
|
-
"button",
|
|
507
|
-
{
|
|
508
|
-
onClick: () => setShowDeleteConfirm(false),
|
|
509
|
-
disabled: deleteState.status === "loading",
|
|
510
|
-
className: "mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-web sm:mt-0 sm:w-auto sm:text-sm disabled:opacity-50 disabled:cursor-not-allowed",
|
|
511
|
-
children: "Cancel"
|
|
512
|
-
}
|
|
513
|
-
)
|
|
514
|
-
] })
|
|
515
|
-
] }) }),
|
|
516
|
-
(submissionState.status !== "idle" || deleteState.status !== "idle") && /* @__PURE__ */ jsx(
|
|
517
|
-
"div",
|
|
579
|
+
/* @__PURE__ */ jsx(
|
|
580
|
+
DeleteConfirmModal,
|
|
518
581
|
{
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
582
|
+
show: showDeleteConfirm,
|
|
583
|
+
modelName: model.name,
|
|
584
|
+
isDeleting: deleteState.status === "loading",
|
|
585
|
+
onConfirm: handleDelete,
|
|
586
|
+
onCancel: () => setShowDeleteConfirm(false)
|
|
587
|
+
}
|
|
588
|
+
),
|
|
589
|
+
/* @__PURE__ */ jsx(
|
|
590
|
+
StatusMessage,
|
|
591
|
+
{
|
|
592
|
+
submissionStatus: submissionState.status,
|
|
593
|
+
deleteStatus: deleteState.status,
|
|
594
|
+
submissionMessage: submissionState.message,
|
|
595
|
+
deleteMessage: deleteState.message,
|
|
596
|
+
modelName: model.name
|
|
530
597
|
}
|
|
531
598
|
),
|
|
532
599
|
/* @__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(
|