@nestledjs/data-browser 0.1.1 → 0.1.4

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.
Files changed (83) hide show
  1. package/index.d.ts +15 -0
  2. package/index.js +64 -0
  3. package/lib/admin-data.d.ts +2 -0
  4. package/lib/components/filters/DateRangeFilter.d.ts +7 -0
  5. package/lib/components/filters/DateRangeFilter.js +105 -0
  6. package/lib/components/filters/NumberRangeFilter.d.ts +8 -0
  7. package/lib/components/filters/NumberRangeFilter.js +115 -0
  8. package/lib/components/filters/RelationComponents.d.ts +32 -0
  9. package/lib/components/filters/RelationComponents.js +265 -0
  10. package/lib/components/filters/RelationFilterField.d.ts +8 -0
  11. package/lib/components/filters/RelationFilterField.js +116 -0
  12. package/lib/components/filters/index.d.ts +4 -0
  13. package/lib/components/index.d.ts +2 -0
  14. package/lib/components/shared/AdminBreadcrumbs.d.ts +13 -0
  15. package/lib/components/shared/AdminBreadcrumbs.js +103 -0
  16. package/lib/components/shared/AdminErrorStates.d.ts +30 -0
  17. package/lib/components/shared/AdminErrorStates.js +214 -0
  18. package/lib/components/shared/AdminStatusDisplay.d.ts +21 -0
  19. package/lib/components/shared/AdminStatusDisplay.js +205 -0
  20. package/lib/components/shared/index.d.ts +3 -0
  21. package/lib/context/AdminDataContext.d.ts +47 -0
  22. package/lib/context/AdminDataContext.js +28 -0
  23. package/lib/hooks/useAdminList.d.ts +5 -0
  24. package/lib/hooks/useAdminList.js +41 -0
  25. package/lib/hooks/useClickOutside.d.ts +2 -0
  26. package/lib/hooks/useClickOutside.js +13 -0
  27. package/lib/hooks/useDebounce.d.ts +1 -0
  28. package/lib/hooks/useDebounce.js +13 -0
  29. package/lib/hooks/useRelationData.d.ts +7 -0
  30. package/lib/hooks/useRelationData.js +64 -0
  31. package/lib/layouts/AdminDataLayout.d.ts +5 -0
  32. package/lib/layouts/AdminDataLayout.js +315 -0
  33. package/lib/pages/AdminDataCreatePage.d.ts +4 -0
  34. package/lib/pages/AdminDataCreatePage.js +487 -0
  35. package/lib/pages/AdminDataEditPage.d.ts +4 -0
  36. package/lib/pages/AdminDataEditPage.js +903 -0
  37. package/lib/pages/AdminDataIndexPage.d.ts +4 -0
  38. package/lib/pages/AdminDataIndexPage.js +91 -0
  39. package/lib/pages/AdminDataListPage.d.ts +9 -0
  40. package/lib/pages/AdminDataListPage.js +1218 -0
  41. package/lib/types/index.d.ts +60 -0
  42. package/lib/types/index.js +16 -0
  43. package/lib/utils/graphql-utils.d.ts +25 -0
  44. package/lib/utils/graphql-utils.js +265 -0
  45. package/lib/utils/secure-storage.d.ts +50 -0
  46. package/lib/utils/secure-storage.js +145 -0
  47. package/lib/utils/string-utils.d.ts +6 -0
  48. package/lib/utils/string-utils.js +27 -0
  49. package/package.json +16 -3
  50. package/.babelrc +0 -12
  51. package/LICENSE +0 -21
  52. package/eslint.config.mjs +0 -12
  53. package/project.json +0 -35
  54. package/src/index.ts +0 -28
  55. package/src/lib/admin-data.spec.tsx +0 -10
  56. package/src/lib/admin-data.tsx +0 -9
  57. package/src/lib/components/filters/DateRangeFilter.tsx +0 -88
  58. package/src/lib/components/filters/NumberRangeFilter.tsx +0 -111
  59. package/src/lib/components/filters/RelationComponents.tsx +0 -176
  60. package/src/lib/components/filters/RelationFilterField.tsx +0 -106
  61. package/src/lib/components/filters/index.ts +0 -5
  62. package/src/lib/components/index.ts +0 -2
  63. package/src/lib/components/shared/AdminBreadcrumbs.tsx +0 -88
  64. package/src/lib/components/shared/AdminErrorStates.tsx +0 -180
  65. package/src/lib/components/shared/AdminStatusDisplay.tsx +0 -292
  66. package/src/lib/components/shared/index.ts +0 -3
  67. package/src/lib/context/AdminDataContext.tsx +0 -74
  68. package/src/lib/hooks/useAdminList.ts +0 -42
  69. package/src/lib/hooks/useClickOutside.ts +0 -21
  70. package/src/lib/hooks/useDebounce.ts +0 -16
  71. package/src/lib/hooks/useRelationData.ts +0 -114
  72. package/src/lib/layouts/AdminDataLayout.tsx +0 -251
  73. package/src/lib/pages/AdminDataCreatePage.tsx +0 -415
  74. package/src/lib/pages/AdminDataEditPage.tsx +0 -777
  75. package/src/lib/pages/AdminDataIndexPage.tsx +0 -50
  76. package/src/lib/pages/AdminDataListPage.tsx +0 -921
  77. package/src/lib/types/index.ts +0 -51
  78. package/src/lib/utils/graphql-utils.ts +0 -538
  79. package/src/lib/utils/secure-storage.ts +0 -305
  80. package/src/lib/utils/string-utils.ts +0 -53
  81. package/tsconfig.json +0 -17
  82. package/tsconfig.lib.json +0 -20
  83. package/vite.config.ts +0 -35
@@ -0,0 +1,60 @@
1
+ export interface AdminListState {
2
+ search: string | null;
3
+ debouncedSearch: string | null;
4
+ skip: number;
5
+ pageSize: number;
6
+ sort: {
7
+ orderBy: string;
8
+ orderDirection: string;
9
+ };
10
+ visibleColumns: string[];
11
+ showColumnSelector: boolean;
12
+ searchFields: string[];
13
+ showSearchFieldSelector: boolean;
14
+ filters: Record<string, unknown>;
15
+ showFilters: boolean;
16
+ }
17
+ export type AdminListAction = {
18
+ type: 'SET_SEARCH';
19
+ payload: string | null;
20
+ } | {
21
+ type: 'SET_DEBOUNCED_SEARCH';
22
+ payload: string | null;
23
+ } | {
24
+ type: 'SET_SKIP';
25
+ payload: number;
26
+ } | {
27
+ type: 'SET_PAGE_SIZE';
28
+ payload: number;
29
+ } | {
30
+ type: 'SET_SORT';
31
+ payload: {
32
+ orderBy: string;
33
+ orderDirection: string;
34
+ };
35
+ } | {
36
+ type: 'SET_VISIBLE_COLUMNS';
37
+ payload: string[];
38
+ } | {
39
+ type: 'TOGGLE_COLUMN_SELECTOR';
40
+ } | {
41
+ type: 'SET_SEARCH_FIELDS';
42
+ payload: string[];
43
+ } | {
44
+ type: 'TOGGLE_SEARCH_FIELD_SELECTOR';
45
+ } | {
46
+ type: 'SET_FILTERS';
47
+ payload: Record<string, unknown>;
48
+ } | {
49
+ type: 'TOGGLE_FILTERS';
50
+ } | {
51
+ type: 'RESET_PAGINATION';
52
+ } | {
53
+ type: 'RESET_FILTERS';
54
+ };
55
+ export declare const initialState: AdminListState;
56
+ export interface DatabaseModel {
57
+ name: string;
58
+ fields: any[];
59
+ pluralModelPropertyName?: string;
60
+ }
@@ -0,0 +1,16 @@
1
+ const e = {
2
+ search: "",
3
+ debouncedSearch: "",
4
+ skip: 0,
5
+ pageSize: 20,
6
+ sort: { orderBy: "id", orderDirection: "desc" },
7
+ visibleColumns: [],
8
+ showColumnSelector: !1,
9
+ searchFields: [],
10
+ showSearchFieldSelector: !1,
11
+ filters: {},
12
+ showFilters: !1
13
+ };
14
+ export {
15
+ e as initialState
16
+ };
@@ -0,0 +1,25 @@
1
+ import { FormField } from '@nestledjs/forms';
2
+ import { DatabaseModel } from '../types';
3
+ /**
4
+ * Get GraphQL documents for admin CRUD operations
5
+ */
6
+ export declare function getAdminDocuments(sdk: any, model: DatabaseModel): {
7
+ query: any;
8
+ listQuery: any;
9
+ update: any;
10
+ delete: any;
11
+ create: any;
12
+ };
13
+ /**
14
+ * Get the GraphQL mutation name for a given operation
15
+ */
16
+ export declare function getMutationName(model: DatabaseModel, operation: 'create' | 'update' | 'delete'): string;
17
+ /**
18
+ * Build form fields for a model and operation
19
+ */
20
+ export declare function buildFormFields(sdk: any, model: DatabaseModel, operation: 'create' | 'update', currentItem?: any, isSubmitting?: boolean): FormField[];
21
+ /**
22
+ * Clean form input data for GraphQL mutations
23
+ * Removes Apollo metadata and system fields
24
+ */
25
+ export declare function cleanFormInput(input: Record<string, unknown>, model?: DatabaseModel): Record<string, unknown>;
@@ -0,0 +1,265 @@
1
+ import { FormFieldClass as u } from "@nestledjs/forms";
2
+ import { getPluralName as j } from "@nestledjs/helpers";
3
+ function S(t, a) {
4
+ try {
5
+ const n = t[a];
6
+ if (!n || typeof n != "object")
7
+ return null;
8
+ const r = Object.values(n).filter((l) => typeof l == "string");
9
+ if (r.length === 0) {
10
+ const l = Object.keys(n).filter((b) => isNaN(Number(b)));
11
+ return l.length > 0 ? l : null;
12
+ }
13
+ return r;
14
+ } catch (n) {
15
+ return console.warn(`Failed to get enum values for type ${a}:`, n), null;
16
+ }
17
+ }
18
+ function O(t) {
19
+ return t === t.toUpperCase() && t.length > 1 ? t.charAt(0).toUpperCase() + t.slice(1).toLowerCase() : t;
20
+ }
21
+ function G(t, a) {
22
+ if (!a || !a.name)
23
+ throw new Error("Invalid model provided to getAdminDocuments");
24
+ const n = O(a.name), r = O(j(a.name)), l = `__Admin${n}Document`, b = `__Admin${r}Document`, g = `__AdminUpdate${n}Document`, i = `__AdminDelete${n}Document`, m = `__AdminCreate${n}Document`, c = {
25
+ query: t[l],
26
+ // For single item
27
+ listQuery: t[b],
28
+ // For lists
29
+ update: t[g],
30
+ delete: t[i],
31
+ create: t[m]
32
+ }, e = [];
33
+ if (c.query || e.push(l), c.listQuery || e.push(b), c.create || e.push(m), c.update || e.push(g), c.delete || e.push(i), e.length > 0)
34
+ throw console.error(`[GraphQL Documents] Missing documents for model "${a.name}":`, {
35
+ model: a.name,
36
+ normalizedModelName: n,
37
+ normalizedPluralName: r,
38
+ missingDocuments: e,
39
+ availableDocuments: Object.keys(t).filter(
40
+ (h) => h.includes("Admin") && h.includes("Document")
41
+ )
42
+ }), new Error(
43
+ `Missing GraphQL documents for model "${a.name}": ${e.join(", ")}. Please ensure the API server is running and the GraphQL schema is up to date.`
44
+ );
45
+ return c;
46
+ }
47
+ function z(t, a) {
48
+ const n = t.name.charAt(0).toLowerCase() + t.name.slice(1);
49
+ switch (a) {
50
+ case "create":
51
+ return `create${t.name}`;
52
+ case "update":
53
+ return `update${t.name}`;
54
+ case "delete":
55
+ return `delete${t.name}`;
56
+ default:
57
+ return n;
58
+ }
59
+ }
60
+ function I(t, a, n, r, l) {
61
+ const g = a.fields.filter((e) => !(n === "create" && e.isId || e.isReadOnly || e.isGenerated || e.isUpdatedAt || e.name === "createdAt" || e.relationName && e.isList)).map((e) => {
62
+ var $;
63
+ const h = e.name.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/^./, (d) => d.toUpperCase());
64
+ let o = r && n === "update" ? r[e.name] : void 0;
65
+ if ((e.type.toLowerCase() === "datetime" || e.type.toLowerCase() === "date") && (o instanceof Date || o && typeof o == "string"))
66
+ try {
67
+ const d = o instanceof Date ? o : new Date(o);
68
+ e.type.toLowerCase() === "date" ? o = d.toISOString().split("T")[0] : o = d.toISOString().substring(0, 16);
69
+ } catch (d) {
70
+ console.warn(`Failed to convert date value for field ${e.name}:`, d), o = "";
71
+ }
72
+ if (o && typeof o == "object" && !Array.isArray(o) && e.relationName) {
73
+ const d = o;
74
+ d.id && typeof d.id == "string" ? o = d.id : o = "";
75
+ }
76
+ o === null && e.type.toLowerCase() !== "boolean" && (o = "");
77
+ const _ = !!e.isList ? !1 : !e.isOptional, p = {
78
+ label: h,
79
+ required: _,
80
+ ...o !== void 0 && { value: o }
81
+ };
82
+ switch (e.type.toLowerCase()) {
83
+ case "string":
84
+ return e.name.toLowerCase().includes("email") ? u.email(e.name, p) : e.name.toLowerCase().includes("description") || e.name.toLowerCase().includes("content") || e.name.toLowerCase().includes("notes") ? u.textArea(e.name, p) : u.text(e.name, p);
85
+ case "int":
86
+ case "bigint":
87
+ return u.text(e.name, p);
88
+ case "float":
89
+ case "decimal":
90
+ return u.text(e.name, p);
91
+ case "boolean": {
92
+ const f = r && n === "update" ? !!r[e.name] : !1;
93
+ return u.checkbox(e.name, {
94
+ ...p,
95
+ required: !1,
96
+ ...n === "update" && { value: f }
97
+ });
98
+ }
99
+ case "datetime":
100
+ return u.dateTimePicker(e.name, p);
101
+ case "date":
102
+ return u.datePicker(e.name, p);
103
+ default:
104
+ const d = S(t, e.type);
105
+ if (d) {
106
+ const f = d.map((s) => ({
107
+ value: s,
108
+ label: s.replace(/_/g, " ").toLowerCase().replace(/^./, (C) => C.toUpperCase())
109
+ }));
110
+ return u.select(e.name, {
111
+ ...p,
112
+ options: f
113
+ });
114
+ }
115
+ if (e.relationName && !e.isList) {
116
+ const f = (($ = e.relationFromFields) == null ? void 0 : $[0]) || e.name;
117
+ let s = r && n === "update" ? r[f] : void 0;
118
+ s && typeof s == "object" && s.id && (s = s.id), s === null && (s = "");
119
+ const C = `__Admin${e.type}sDocument`, F = `${e.type}sDocument`, N = t[C] || t[F];
120
+ if (N) {
121
+ let w = "id";
122
+ e.type === "Course" ? w = "title" : e.type === "User" || e.type === "Program" ? w = "name" : w = "title";
123
+ let A = [];
124
+ if (r && n === "update") {
125
+ const y = r[e.name];
126
+ if (y && typeof y == "object" && y.id) {
127
+ const L = y[w] || y.title || y.name || y.id;
128
+ A = [
129
+ {
130
+ value: y.id,
131
+ label: L
132
+ }
133
+ ];
134
+ }
135
+ }
136
+ return u.searchSelectApollo(f, {
137
+ label: h,
138
+ // Remove "ID" suffix - just use the field name
139
+ required: p.required,
140
+ dataType: e.type.toLowerCase() + "s",
141
+ // e.g., Course → courses
142
+ document: N,
143
+ searchFields: [w],
144
+ // For searching
145
+ selectOptionsFunction: (y) => {
146
+ const L = y.map((D) => ({
147
+ value: D.id,
148
+ label: D[w] || D.title || D.name || D.id
149
+ })), v = [...A];
150
+ return L.forEach((D) => {
151
+ v.find((x) => x.value === D.value) || v.push(D);
152
+ }), v;
153
+ },
154
+ ...A.length > 0 && { initialOptions: A },
155
+ // Provide initial options if available
156
+ ...s !== void 0 && { value: s }
157
+ });
158
+ } else
159
+ return console.warn(
160
+ `GraphQL document ${C} or ${F} not found for relation ${e.type}. Using text input instead.`
161
+ ), u.text(f, {
162
+ label: `${h} ID`,
163
+ required: p.required,
164
+ helpText: "Enter the ID of the related record",
165
+ ...s !== void 0 && { value: s }
166
+ });
167
+ }
168
+ if (e.kind === "enum" && e.enumValues) {
169
+ const f = e.enumValues.map((s) => ({
170
+ value: s,
171
+ label: s.replace(/_/g, " ").toLowerCase().replace(/^./, (C) => C.toUpperCase())
172
+ }));
173
+ return u.select(e.name, {
174
+ ...p,
175
+ options: f
176
+ });
177
+ }
178
+ return u.text(e.name, p);
179
+ }
180
+ }), c = l ? n === "create" ? "Creating..." : "Updating..." : n === "create" ? "Create" : "Update";
181
+ return g.push(
182
+ u.button("submit", {
183
+ text: c,
184
+ type: "submit",
185
+ variant: "primary",
186
+ disabled: l
187
+ })
188
+ ), g;
189
+ }
190
+ const V = /* @__PURE__ */ new Set([
191
+ "__typename",
192
+ // Apollo type annotation
193
+ "id",
194
+ // Usually passed as separate variable
195
+ "createdAt",
196
+ // Auto-managed timestamp
197
+ "updatedAt",
198
+ // Auto-managed timestamp
199
+ "_count",
200
+ // Prisma count metadata
201
+ "_meta"
202
+ // Prisma metadata
203
+ ]);
204
+ function U(t, a) {
205
+ return V.has(t) || a === void 0;
206
+ }
207
+ function q(t, a) {
208
+ if (t === "")
209
+ return null;
210
+ if ((a == null ? void 0 : a.type.toLowerCase()) === "datetime" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/.test(t))
211
+ try {
212
+ return new Date(t).toISOString();
213
+ } catch {
214
+ return console.warn(`Failed to convert datetime-local value: ${t}`), t;
215
+ }
216
+ if (t === "true") return !0;
217
+ if (t === "false") return !1;
218
+ if (/^\d+(\.\d+)?$/.test(t)) {
219
+ const r = Number(t);
220
+ if (!isNaN(r))
221
+ return r;
222
+ }
223
+ return t;
224
+ }
225
+ function T(t, a) {
226
+ const n = P(t, a);
227
+ return Object.keys(n).length > 0 ? n : null;
228
+ }
229
+ function P(t, a) {
230
+ var l, b, g;
231
+ const n = {}, r = new Set(
232
+ ((b = (l = a == null ? void 0 : a.fields) == null ? void 0 : l.filter((i) => i.type.toLowerCase() === "boolean")) == null ? void 0 : b.map((i) => i.name)) || []
233
+ );
234
+ for (const [i, m] of Object.entries(t)) {
235
+ if (r.has(i) && m === void 0) {
236
+ n[i] = !1;
237
+ continue;
238
+ }
239
+ if (!U(i, m)) {
240
+ if (typeof m == "string") {
241
+ const c = (g = a == null ? void 0 : a.fields) == null ? void 0 : g.find((h) => h.name === i), e = q(m, c);
242
+ e !== null && (n[i] = e);
243
+ continue;
244
+ }
245
+ if (typeof m == "object" && m !== null && !Array.isArray(m)) {
246
+ const c = m;
247
+ if (c.value !== void 0 && c.label !== void 0 && typeof c.value == "string") {
248
+ n[i] = c.value;
249
+ continue;
250
+ }
251
+ const e = T(c, a);
252
+ e !== null && (n[i] = e);
253
+ continue;
254
+ }
255
+ n[i] = m;
256
+ }
257
+ }
258
+ return n;
259
+ }
260
+ export {
261
+ I as buildFormFields,
262
+ P as cleanFormInput,
263
+ G as getAdminDocuments,
264
+ z as getMutationName
265
+ };
@@ -0,0 +1,50 @@
1
+ interface AdminConfig {
2
+ version: string;
3
+ models: Record<string, {
4
+ visibleColumns?: string[];
5
+ searchFields?: string[];
6
+ sortPreference?: {
7
+ orderBy: string;
8
+ orderDirection: string;
9
+ };
10
+ }>;
11
+ }
12
+ export declare const SecureAdminLocalStorage: {
13
+ getConfig: () => AdminConfig;
14
+ setConfig: (config: AdminConfig) => boolean;
15
+ getColumnVisibility: (modelName: string) => string[] | null;
16
+ setColumnVisibility: (modelName: string, visibleColumns: string[]) => boolean;
17
+ getSortPreference: (modelName: string) => {
18
+ orderBy: string;
19
+ orderDirection: string;
20
+ } | null;
21
+ setSortPreference: (modelName: string, sortPreference: {
22
+ orderBy: string;
23
+ orderDirection: string;
24
+ }) => boolean;
25
+ getSearchFields: (modelName: string) => string[] | null;
26
+ setSearchFields: (modelName: string, searchFields: string[]) => boolean;
27
+ exportConfig: () => string | null;
28
+ importConfig: (configJson: string) => boolean;
29
+ clearConfig: () => boolean;
30
+ };
31
+ export declare const AdminLocalStorage: {
32
+ getConfig: () => AdminConfig;
33
+ setConfig: (config: AdminConfig) => boolean;
34
+ getColumnVisibility: (modelName: string) => string[] | null;
35
+ setColumnVisibility: (modelName: string, visibleColumns: string[]) => boolean;
36
+ getSortPreference: (modelName: string) => {
37
+ orderBy: string;
38
+ orderDirection: string;
39
+ } | null;
40
+ setSortPreference: (modelName: string, sortPreference: {
41
+ orderBy: string;
42
+ orderDirection: string;
43
+ }) => boolean;
44
+ getSearchFields: (modelName: string) => string[] | null;
45
+ setSearchFields: (modelName: string, searchFields: string[]) => boolean;
46
+ exportConfig: () => string | null;
47
+ importConfig: (configJson: string) => boolean;
48
+ clearConfig: () => boolean;
49
+ };
50
+ export type { AdminConfig };
@@ -0,0 +1,145 @@
1
+ const c = "mi-admin-config", a = "1.0";
2
+ const s = (e) => typeof e != "string" ? "" : e.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "").replace(/javascript:/gi, "").replace(/on\w+\s*=/gi, "").trim().substring(0, 1e3), f = (e) => Array.isArray(e) ? e.slice(0, 100).map((o) => s(o)).filter((o) => o.length > 0) : [], g = (e) => {
3
+ if (!e || typeof e != "object") return null;
4
+ const o = e, n = s(o.orderBy), t = s(o.orderDirection);
5
+ return !["asc", "desc"].includes(t) || !/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(n) ? null : { orderBy: n, orderDirection: t };
6
+ }, d = (e) => {
7
+ if (!e || typeof e != "object") return !1;
8
+ const o = e;
9
+ if (o.version !== a || !o.models || typeof o.models != "object") return !1;
10
+ const n = o.models;
11
+ for (const [t, r] of Object.entries(n)) {
12
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(t)) return !1;
13
+ if (r && typeof r == "object") {
14
+ const l = r;
15
+ if (l.visibleColumns !== void 0 && !Array.isArray(l.visibleColumns)) return !1;
16
+ if (l.sortPreference !== void 0) {
17
+ const u = g(l.sortPreference);
18
+ if (l.sortPreference !== null && u === null) return !1;
19
+ }
20
+ if (l.searchFields !== void 0 && !Array.isArray(l.searchFields)) return !1;
21
+ }
22
+ }
23
+ return !0;
24
+ }, i = {
25
+ // Get the full admin config with validation
26
+ getConfig: () => {
27
+ try {
28
+ const e = localStorage.getItem(c);
29
+ if (!e)
30
+ return { version: a, models: {} };
31
+ if (e.length > 5e4)
32
+ return console.warn("[AdminLocalStorage] Config exceeds size limit, resetting"), localStorage.removeItem(c), { version: a, models: {} };
33
+ const o = JSON.parse(e);
34
+ return d(o) ? o : (console.warn("[AdminLocalStorage] Invalid config detected, resetting"), localStorage.removeItem(c), { version: a, models: {} });
35
+ } catch (e) {
36
+ console.warn("[AdminLocalStorage] Failed to load config:", e);
37
+ try {
38
+ localStorage.removeItem(c);
39
+ } catch {
40
+ }
41
+ return { version: a, models: {} };
42
+ }
43
+ },
44
+ // Save the full admin config with validation
45
+ setConfig: (e) => {
46
+ try {
47
+ if (!d(e))
48
+ return console.warn("[AdminLocalStorage] Invalid config provided"), !1;
49
+ const o = JSON.stringify(e);
50
+ return o.length > 5e4 ? (console.warn("[AdminLocalStorage] Config too large to store"), !1) : (localStorage.setItem(c, o), !0);
51
+ } catch (o) {
52
+ return console.warn("[AdminLocalStorage] Failed to save config:", o), !1;
53
+ }
54
+ },
55
+ // Get visible columns for a specific model with sanitization
56
+ getColumnVisibility: (e) => {
57
+ var r;
58
+ const o = s(e);
59
+ if (!o) return null;
60
+ const t = (r = i.getConfig().models[o]) == null ? void 0 : r.visibleColumns;
61
+ return t ? f(t) : null;
62
+ },
63
+ // Set visible columns for a specific model with validation
64
+ setColumnVisibility: (e, o) => {
65
+ const n = s(e), t = f(o);
66
+ if (!n) return !1;
67
+ const r = i.getConfig();
68
+ return r.models[n] || (r.models[n] = {}), r.models[n].visibleColumns = t, i.setConfig(r);
69
+ },
70
+ // Get sort preference for a specific model with validation
71
+ getSortPreference: (e) => {
72
+ var r;
73
+ const o = s(e);
74
+ if (!o) return null;
75
+ const t = (r = i.getConfig().models[o]) == null ? void 0 : r.sortPreference;
76
+ return t ? g(t) : null;
77
+ },
78
+ // Set sort preference for a specific model with validation
79
+ setSortPreference: (e, o) => {
80
+ const n = s(e), t = g(o);
81
+ if (!n || !t) return !1;
82
+ const r = i.getConfig();
83
+ return r.models[n] || (r.models[n] = {}), r.models[n].sortPreference = t, i.setConfig(r);
84
+ },
85
+ // Get search fields for a specific model with sanitization
86
+ getSearchFields: (e) => {
87
+ var r;
88
+ const o = s(e);
89
+ if (!o) return null;
90
+ const t = (r = i.getConfig().models[o]) == null ? void 0 : r.searchFields;
91
+ return t ? f(t) : null;
92
+ },
93
+ // Set search fields for a specific model with validation
94
+ setSearchFields: (e, o) => {
95
+ const n = s(e), t = f(o);
96
+ if (!n) return !1;
97
+ const r = i.getConfig();
98
+ return r.models[n] || (r.models[n] = {}), r.models[n].searchFields = t, i.setConfig(r);
99
+ },
100
+ // Export config as JSON string with validation
101
+ exportConfig: () => {
102
+ try {
103
+ const e = i.getConfig();
104
+ return d(e) ? JSON.stringify(e, null, 2) : (console.warn("[AdminLocalStorage] Cannot export invalid config"), null);
105
+ } catch (e) {
106
+ return console.warn("[AdminLocalStorage] Failed to export config:", e), null;
107
+ }
108
+ },
109
+ // Import config from JSON string with comprehensive security validation
110
+ importConfig: (e) => {
111
+ try {
112
+ const o = s(e);
113
+ if (!o || o.length > 5e4)
114
+ return console.warn("[AdminLocalStorage] Invalid or oversized config JSON"), !1;
115
+ const n = JSON.parse(o);
116
+ if (!d(n))
117
+ return console.warn("[AdminLocalStorage] Failed config validation during import"), !1;
118
+ const t = JSON.stringify(n), r = [
119
+ /<script/i,
120
+ /javascript:/i,
121
+ /on\w+\s*=/i,
122
+ /eval\s*\(/i,
123
+ /function\s*\(/i
124
+ ];
125
+ for (const l of r)
126
+ if (l.test(t))
127
+ return console.warn("[AdminLocalStorage] Suspicious content detected in config"), !1;
128
+ return i.setConfig(n);
129
+ } catch (o) {
130
+ return console.warn("[AdminLocalStorage] Failed to import config:", o), !1;
131
+ }
132
+ },
133
+ // Clear all stored data (for security/privacy)
134
+ clearConfig: () => {
135
+ try {
136
+ return localStorage.removeItem(c), !0;
137
+ } catch (e) {
138
+ return console.warn("[AdminLocalStorage] Failed to clear config:", e), !1;
139
+ }
140
+ }
141
+ }, m = i;
142
+ export {
143
+ m as AdminLocalStorage,
144
+ i as SecureAdminLocalStorage
145
+ };
@@ -0,0 +1,6 @@
1
+ export declare function kebabCase(name: string): string;
2
+ export declare function spacedWords(name: string): string;
3
+ export declare function formatFieldName(fieldName: string): string;
4
+ export declare function normalizeModelNameForDocument(modelName: string): string;
5
+ export declare function getItemDisplayName(item: any): string;
6
+ export declare function getSmartSearchFields(availableFields: string[]): string[];
@@ -0,0 +1,27 @@
1
+ function n(r) {
2
+ return r.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/([A-Z])([A-Z][a-z])/g, "$1-$2").toLowerCase();
3
+ }
4
+ function s(r) {
5
+ return r.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/([A-Z])([A-Z][a-z])/g, "$1 $2");
6
+ }
7
+ function c(r) {
8
+ return r.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/^./, (e) => e.toUpperCase());
9
+ }
10
+ function i(r) {
11
+ return r === r.toUpperCase() && r.length > 1 ? r.charAt(0).toUpperCase() + r.slice(1).toLowerCase() : r;
12
+ }
13
+ function u(r) {
14
+ return r.name ? r.name : r.title ? r.title : r.firstName && r.lastName ? `${r.firstName} ${r.lastName}` : r.firstName ? r.firstName : r.email ? r.email : r.id;
15
+ }
16
+ function f(r) {
17
+ const t = ["name", "title", "email", "firstName", "lastName", "subject"].filter((a) => r.includes(a));
18
+ return t.length >= 1 ? t.slice(0, 2) : r.slice(0, Math.min(2, r.length));
19
+ }
20
+ export {
21
+ c as formatFieldName,
22
+ u as getItemDisplayName,
23
+ f as getSmartSearchFields,
24
+ n as kebabCase,
25
+ i as normalizeModelNameForDocument,
26
+ s as spacedWords
27
+ };
package/package.json CHANGED
@@ -1,9 +1,23 @@
1
1
  {
2
2
  "name": "@nestledjs/data-browser",
3
- "version": "0.1.1",
3
+ "version": "0.1.4",
4
4
  "description": "Universal admin data browser for Nestled framework projects with full CRUD operations",
5
5
  "main": "./index.js",
6
+ "module": "./index.js",
6
7
  "types": "./index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./index.d.ts",
11
+ "import": "./index.js",
12
+ "default": "./index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "index.js",
17
+ "index.d.ts",
18
+ "lib/**/*",
19
+ "README.md"
20
+ ],
7
21
  "keywords": [
8
22
  "nestled",
9
23
  "admin",
@@ -25,7 +39,7 @@
25
39
  "@heroicons/react": "^2.0.0",
26
40
  "@nestledjs/forms": "^0.5.0",
27
41
  "@nestledjs/helpers": "^0.1.0",
28
- "@nestledjs/shared-components": "^0.1.1",
42
+ "@nestledjs/shared-components": "^0.1.4",
29
43
  "react": "^19.0.0",
30
44
  "react-router": "^7.0.0"
31
45
  },
@@ -39,6 +53,5 @@
39
53
  "access": "public",
40
54
  "registry": "https://registry.npmjs.org/"
41
55
  },
42
- "module": "./index.js",
43
56
  "type": "module"
44
57
  }
package/.babelrc DELETED
@@ -1,12 +0,0 @@
1
- {
2
- "presets": [
3
- [
4
- "@nx/react/babel",
5
- {
6
- "runtime": "automatic",
7
- "useBuiltIns": "usage"
8
- }
9
- ]
10
- ],
11
- "plugins": []
12
- }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 Nestled Team
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
package/eslint.config.mjs DELETED
@@ -1,12 +0,0 @@
1
- import nx from '@nx/eslint-plugin'
2
- import baseConfig from '../../eslint.config.mjs'
3
-
4
- export default [
5
- ...baseConfig,
6
- ...nx.configs['flat/react'],
7
- {
8
- files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
9
- // Override or add rules here
10
- rules: {},
11
- },
12
- ]