@nestledjs/data-browser 0.1.12 → 0.1.15
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/RelationFieldWrapper.d.ts +9 -0
- package/lib/components/RelationFieldWrapper.js +63 -0
- package/lib/components/filters/DateRangeFilter.d.ts +1 -1
- package/lib/components/filters/DateRangeFilter.js +9 -9
- package/lib/components/filters/NumberRangeFilter.js +21 -21
- package/lib/components/filters/RelationComponents.d.ts +4 -4
- package/lib/components/filters/RelationComponents.js +16 -16
- package/lib/components/filters/RelationFilterField.js +16 -10
- package/lib/components/index.d.ts +1 -0
- package/lib/components/shared/AdminBreadcrumbs.js +3 -3
- package/lib/components/shared/AdminErrorStates.js +1 -1
- package/lib/components/shared/AdminStatusDisplay.js +1 -1
- package/lib/context/AdminDataContext.d.ts +1 -1
- package/lib/context/AdminDataContext.js +6 -5
- package/lib/hooks/useRelationData.d.ts +1 -1
- package/lib/hooks/useRelationData.js +3 -2
- package/lib/layouts/AdminDataLayout.js +53 -56
- package/lib/pages/AdminDataCreatePage.js +6 -5
- package/lib/pages/AdminDataEditPage.js +6 -5
- package/lib/pages/AdminDataIndexPage.js +14 -13
- package/lib/pages/AdminDataListPage.js +138 -99
- package/lib/utils/graphql-utils.d.ts +1 -1
- package/lib/utils/graphql-utils.js +132 -24
- package/lib/utils/string-utils.js +3 -3
- package/package.json +3 -3
package/index.js
CHANGED
|
@@ -16,6 +16,7 @@ import { RelationFilterField } from "./lib/components/filters/RelationFilterFiel
|
|
|
16
16
|
import { AdminBreadcrumbs } from "./lib/components/shared/AdminBreadcrumbs.js";
|
|
17
17
|
import { AdminEmptyState, AdminErrorState, AdminLoadingState } from "./lib/components/shared/AdminErrorStates.js";
|
|
18
18
|
import { AdminStatusDisplay, AdminUserStatus } from "./lib/components/shared/AdminStatusDisplay.js";
|
|
19
|
+
import { RelationFieldWrapper } from "./lib/components/RelationFieldWrapper.js";
|
|
19
20
|
import { buildFormFields, cleanFormInput, getAdminDocuments, getMutationName } from "./lib/utils/graphql-utils.js";
|
|
20
21
|
import { AdminLocalStorage, SecureAdminLocalStorage } from "./lib/utils/secure-storage.js";
|
|
21
22
|
import { formatFieldName, getItemDisplayName, getSmartSearchFields, kebabCase, normalizeModelNameForDocument, spacedWords } from "./lib/utils/string-utils.js";
|
|
@@ -40,6 +41,7 @@ export {
|
|
|
40
41
|
NumberRangeFilter,
|
|
41
42
|
RelationDropdownButton,
|
|
42
43
|
RelationDropdownContent,
|
|
44
|
+
RelationFieldWrapper,
|
|
43
45
|
RelationFilterField,
|
|
44
46
|
RelationItem,
|
|
45
47
|
RelationItemList,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
interface RelationFieldWrapperProps {
|
|
2
|
+
readonly children?: React.ReactNode;
|
|
3
|
+
readonly relationType?: string;
|
|
4
|
+
readonly initialValue?: string;
|
|
5
|
+
readonly fieldName?: string;
|
|
6
|
+
readonly basePath?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function RelationFieldWrapper({ children, relationType, initialValue, fieldName, basePath }: RelationFieldWrapperProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Link } from "react-router";
|
|
2
|
+
import { useState, useEffect } from "react";
|
|
3
|
+
var _jsxFileName = "/Users/justinhandley/IdeaProjects/nestled_template/libs/data-browser/src/lib/components/RelationFieldWrapper.tsx";
|
|
4
|
+
const toKebabCase = (str) => {
|
|
5
|
+
return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
6
|
+
};
|
|
7
|
+
function RelationFieldWrapper({
|
|
8
|
+
children,
|
|
9
|
+
relationType,
|
|
10
|
+
initialValue,
|
|
11
|
+
fieldName,
|
|
12
|
+
basePath = "/admin/data"
|
|
13
|
+
}) {
|
|
14
|
+
const [currentValue, setCurrentValue] = useState(initialValue || "");
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (initialValue && !currentValue) {
|
|
17
|
+
setCurrentValue(initialValue);
|
|
18
|
+
}
|
|
19
|
+
if (fieldName) {
|
|
20
|
+
const selectElement = document.querySelector(`[name="${fieldName}"]`);
|
|
21
|
+
if ((selectElement == null ? void 0 : selectElement.value) && selectElement.value !== currentValue) {
|
|
22
|
+
setCurrentValue(selectElement.value);
|
|
23
|
+
}
|
|
24
|
+
if (selectElement) {
|
|
25
|
+
const handleChange = (event) => {
|
|
26
|
+
const target = event.target;
|
|
27
|
+
if (target.value !== currentValue) {
|
|
28
|
+
setCurrentValue(target.value);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
selectElement.addEventListener("change", handleChange);
|
|
32
|
+
return () => {
|
|
33
|
+
selectElement.removeEventListener("change", handleChange);
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}, [fieldName, currentValue, initialValue]);
|
|
38
|
+
const showViewLink = !!(relationType && currentValue && currentValue !== "");
|
|
39
|
+
return /* @__PURE__ */ React.createElement("div", { className: "relative", __self: this, __source: {
|
|
40
|
+
fileName: _jsxFileName,
|
|
41
|
+
lineNumber: 64,
|
|
42
|
+
columnNumber: 5
|
|
43
|
+
} }, children, showViewLink && /* @__PURE__ */ React.createElement("div", { className: "mt-1", __self: this, __source: {
|
|
44
|
+
fileName: _jsxFileName,
|
|
45
|
+
lineNumber: 67,
|
|
46
|
+
columnNumber: 9
|
|
47
|
+
} }, /* @__PURE__ */ React.createElement(Link, { to: `${basePath}/${toKebabCase(relationType)}/${currentValue}`, className: "inline-flex items-center text-xs text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300 hover:underline", __self: this, __source: {
|
|
48
|
+
fileName: _jsxFileName,
|
|
49
|
+
lineNumber: 68,
|
|
50
|
+
columnNumber: 11
|
|
51
|
+
} }, /* @__PURE__ */ React.createElement("svg", { className: "h-3 w-3 mr-1", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg", __self: this, __source: {
|
|
52
|
+
fileName: _jsxFileName,
|
|
53
|
+
lineNumber: 72,
|
|
54
|
+
columnNumber: 13
|
|
55
|
+
} }, /* @__PURE__ */ React.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 12a3 3 0 11-6 0 3 3 0 016 0z M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z", __self: this, __source: {
|
|
56
|
+
fileName: _jsxFileName,
|
|
57
|
+
lineNumber: 79,
|
|
58
|
+
columnNumber: 15
|
|
59
|
+
} })), "View ", relationType, " record")));
|
|
60
|
+
}
|
|
61
|
+
export {
|
|
62
|
+
RelationFieldWrapper
|
|
63
|
+
};
|
|
@@ -3,5 +3,5 @@ interface DateRangeFilterProps {
|
|
|
3
3
|
currentValue: any;
|
|
4
4
|
onChange: (value: any) => void;
|
|
5
5
|
}
|
|
6
|
-
export declare function DateRangeFilter({ fieldName, currentValue, onChange }: DateRangeFilterProps): import("react/jsx-runtime").JSX.Element;
|
|
6
|
+
export declare function DateRangeFilter({ fieldName, currentValue, onChange }: Readonly<DateRangeFilterProps>): import("react/jsx-runtime").JSX.Element;
|
|
7
7
|
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { formatFieldName } from "../../utils/string-utils.js";
|
|
2
|
-
var _jsxFileName = "/Users/justinhandley/IdeaProjects/nestled_template/libs/
|
|
2
|
+
var _jsxFileName = "/Users/justinhandley/IdeaProjects/nestled_template/libs/data-browser/src/lib/components/filters/DateRangeFilter.tsx";
|
|
3
3
|
function DateRangeFilter({
|
|
4
4
|
fieldName,
|
|
5
5
|
currentValue,
|
|
@@ -55,29 +55,29 @@ function DateRangeFilter({
|
|
|
55
55
|
fileName: _jsxFileName,
|
|
56
56
|
lineNumber: 60,
|
|
57
57
|
columnNumber: 9
|
|
58
|
-
} }, /* @__PURE__ */ React.createElement("label", { className: "block text-xs text-gray-500 mb-1", __self: this, __source: {
|
|
58
|
+
} }, /* @__PURE__ */ React.createElement("label", { htmlFor: `${fieldName}-from`, className: "block text-xs text-gray-500 mb-1", __self: this, __source: {
|
|
59
59
|
fileName: _jsxFileName,
|
|
60
60
|
lineNumber: 61,
|
|
61
61
|
columnNumber: 11
|
|
62
|
-
} }, "From"), /* @__PURE__ */ React.createElement("input", { type: "date", value: fromDate, onChange: (e) => handleFromChange(e.target.value), className: "w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-1 focus:ring-green-web focus:border-green-web", __self: this, __source: {
|
|
62
|
+
} }, "From"), /* @__PURE__ */ React.createElement("input", { id: `${fieldName}-from`, type: "date", value: fromDate, onChange: (e) => handleFromChange(e.target.value), className: "w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-1 focus:ring-green-web focus:border-green-web", __self: this, __source: {
|
|
63
63
|
fileName: _jsxFileName,
|
|
64
64
|
lineNumber: 62,
|
|
65
65
|
columnNumber: 11
|
|
66
66
|
} })), /* @__PURE__ */ React.createElement("div", { __self: this, __source: {
|
|
67
67
|
fileName: _jsxFileName,
|
|
68
|
-
lineNumber:
|
|
68
|
+
lineNumber: 70,
|
|
69
69
|
columnNumber: 9
|
|
70
|
-
} }, /* @__PURE__ */ React.createElement("label", { className: "block text-xs text-gray-500 mb-1", __self: this, __source: {
|
|
70
|
+
} }, /* @__PURE__ */ React.createElement("label", { htmlFor: `${fieldName}-to`, className: "block text-xs text-gray-500 mb-1", __self: this, __source: {
|
|
71
71
|
fileName: _jsxFileName,
|
|
72
|
-
lineNumber:
|
|
72
|
+
lineNumber: 71,
|
|
73
73
|
columnNumber: 11
|
|
74
|
-
} }, "To"), /* @__PURE__ */ React.createElement("input", { type: "date", value: toDate, onChange: (e) => handleToChange(e.target.value), className: "w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-1 focus:ring-green-web focus:border-green-web", __self: this, __source: {
|
|
74
|
+
} }, "To"), /* @__PURE__ */ React.createElement("input", { id: `${fieldName}-to`, type: "date", value: toDate, onChange: (e) => handleToChange(e.target.value), className: "w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-1 focus:ring-green-web focus:border-green-web", __self: this, __source: {
|
|
75
75
|
fileName: _jsxFileName,
|
|
76
|
-
lineNumber:
|
|
76
|
+
lineNumber: 72,
|
|
77
77
|
columnNumber: 11
|
|
78
78
|
} }))), (fromDate || toDate) && /* @__PURE__ */ React.createElement("div", { className: "text-xs text-gray-500", __self: this, __source: {
|
|
79
79
|
fileName: _jsxFileName,
|
|
80
|
-
lineNumber:
|
|
80
|
+
lineNumber: 82,
|
|
81
81
|
columnNumber: 9
|
|
82
82
|
} }, fromDate && toDate && `${fromDate} to ${toDate}`, fromDate && !toDate && `From ${fromDate}`, !fromDate && toDate && `Until ${toDate}`));
|
|
83
83
|
}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { formatFieldName } from "../../utils/string-utils.js";
|
|
2
|
-
var _jsxFileName = "/Users/justinhandley/IdeaProjects/nestled_template/libs/
|
|
2
|
+
var _jsxFileName = "/Users/justinhandley/IdeaProjects/nestled_template/libs/data-browser/src/lib/components/filters/NumberRangeFilter.tsx";
|
|
3
3
|
function NumberRangeFilter({
|
|
4
4
|
fieldName,
|
|
5
5
|
fieldType,
|
|
6
6
|
currentValue,
|
|
7
7
|
onChange
|
|
8
8
|
}) {
|
|
9
|
-
const minValue = (currentValue == null ? void 0 : currentValue.gte)
|
|
10
|
-
const maxValue = (currentValue == null ? void 0 : currentValue.lte)
|
|
9
|
+
const minValue = (currentValue == null ? void 0 : currentValue.gte) === void 0 ? "" : currentValue.gte.toString();
|
|
10
|
+
const maxValue = (currentValue == null ? void 0 : currentValue.lte) === void 0 ? "" : currentValue.lte.toString();
|
|
11
11
|
const parseNumber = (value) => {
|
|
12
12
|
if (!value) return void 0;
|
|
13
13
|
if (fieldType === "int" || fieldType === "bigint") {
|
|
14
|
-
const parsed = parseInt(value, 10);
|
|
15
|
-
return isNaN(parsed) ? void 0 : parsed;
|
|
14
|
+
const parsed = Number.parseInt(value, 10);
|
|
15
|
+
return Number.isNaN(parsed) ? void 0 : parsed;
|
|
16
16
|
} else if (fieldType === "float" || fieldType === "decimal") {
|
|
17
|
-
const parsed = parseFloat(value);
|
|
18
|
-
return isNaN(parsed) ? void 0 : parsed;
|
|
17
|
+
const parsed = Number.parseFloat(value);
|
|
18
|
+
return Number.isNaN(parsed) ? void 0 : parsed;
|
|
19
19
|
}
|
|
20
20
|
return void 0;
|
|
21
21
|
};
|
|
@@ -24,10 +24,10 @@ function NumberRangeFilter({
|
|
|
24
24
|
...currentValue
|
|
25
25
|
};
|
|
26
26
|
const parsedValue = parseNumber(value);
|
|
27
|
-
if (parsedValue
|
|
28
|
-
newValue.gte = parsedValue;
|
|
29
|
-
} else {
|
|
27
|
+
if (parsedValue === void 0) {
|
|
30
28
|
delete newValue.gte;
|
|
29
|
+
} else {
|
|
30
|
+
newValue.gte = parsedValue;
|
|
31
31
|
}
|
|
32
32
|
if (newValue.gte === void 0 && newValue.lte === void 0) {
|
|
33
33
|
onChange(void 0);
|
|
@@ -40,10 +40,10 @@ function NumberRangeFilter({
|
|
|
40
40
|
...currentValue
|
|
41
41
|
};
|
|
42
42
|
const parsedValue = parseNumber(value);
|
|
43
|
-
if (parsedValue
|
|
44
|
-
newValue.lte = parsedValue;
|
|
45
|
-
} else {
|
|
43
|
+
if (parsedValue === void 0) {
|
|
46
44
|
delete newValue.lte;
|
|
45
|
+
} else {
|
|
46
|
+
newValue.lte = parsedValue;
|
|
47
47
|
}
|
|
48
48
|
if (newValue.gte === void 0 && newValue.lte === void 0) {
|
|
49
49
|
onChange(void 0);
|
|
@@ -68,29 +68,29 @@ function NumberRangeFilter({
|
|
|
68
68
|
fileName: _jsxFileName,
|
|
69
69
|
lineNumber: 79,
|
|
70
70
|
columnNumber: 9
|
|
71
|
-
} }, /* @__PURE__ */ React.createElement("label", { className: "block text-xs text-gray-500 mb-1", __self: this, __source: {
|
|
71
|
+
} }, /* @__PURE__ */ React.createElement("label", { htmlFor: `${fieldName}-min`, className: "block text-xs text-gray-500 mb-1", __self: this, __source: {
|
|
72
72
|
fileName: _jsxFileName,
|
|
73
73
|
lineNumber: 80,
|
|
74
74
|
columnNumber: 11
|
|
75
|
-
} }, "Min"), /* @__PURE__ */ React.createElement("input", { type: "number", step, value: minValue, onChange: (e) => handleMinChange(e.target.value), placeholder: "No minimum", className: "w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-1 focus:ring-green-web focus:border-green-web", __self: this, __source: {
|
|
75
|
+
} }, "Min"), /* @__PURE__ */ React.createElement("input", { id: `${fieldName}-min`, type: "number", step, value: minValue, onChange: (e) => handleMinChange(e.target.value), placeholder: "No minimum", className: "w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-1 focus:ring-green-web focus:border-green-web", __self: this, __source: {
|
|
76
76
|
fileName: _jsxFileName,
|
|
77
77
|
lineNumber: 81,
|
|
78
78
|
columnNumber: 11
|
|
79
79
|
} })), /* @__PURE__ */ React.createElement("div", { __self: this, __source: {
|
|
80
80
|
fileName: _jsxFileName,
|
|
81
|
-
lineNumber:
|
|
81
|
+
lineNumber: 91,
|
|
82
82
|
columnNumber: 9
|
|
83
|
-
} }, /* @__PURE__ */ React.createElement("label", { className: "block text-xs text-gray-500 mb-1", __self: this, __source: {
|
|
83
|
+
} }, /* @__PURE__ */ React.createElement("label", { htmlFor: `${fieldName}-max`, className: "block text-xs text-gray-500 mb-1", __self: this, __source: {
|
|
84
84
|
fileName: _jsxFileName,
|
|
85
|
-
lineNumber:
|
|
85
|
+
lineNumber: 92,
|
|
86
86
|
columnNumber: 11
|
|
87
|
-
} }, "Max"), /* @__PURE__ */ React.createElement("input", { type: "number", step, value: maxValue, onChange: (e) => handleMaxChange(e.target.value), placeholder: "No maximum", className: "w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-1 focus:ring-green-web focus:border-green-web", __self: this, __source: {
|
|
87
|
+
} }, "Max"), /* @__PURE__ */ React.createElement("input", { id: `${fieldName}-max`, type: "number", step, value: maxValue, onChange: (e) => handleMaxChange(e.target.value), placeholder: "No maximum", className: "w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-1 focus:ring-green-web focus:border-green-web", __self: this, __source: {
|
|
88
88
|
fileName: _jsxFileName,
|
|
89
|
-
lineNumber:
|
|
89
|
+
lineNumber: 93,
|
|
90
90
|
columnNumber: 11
|
|
91
91
|
} }))), (minValue || maxValue) && /* @__PURE__ */ React.createElement("div", { className: "text-xs text-gray-500", __self: this, __source: {
|
|
92
92
|
fileName: _jsxFileName,
|
|
93
|
-
lineNumber:
|
|
93
|
+
lineNumber: 105,
|
|
94
94
|
columnNumber: 9
|
|
95
95
|
} }, minValue && maxValue && `${minValue} to ${maxValue}`, minValue && !maxValue && `≥ ${minValue}`, !minValue && maxValue && `≤ ${maxValue}`));
|
|
96
96
|
}
|
|
@@ -9,10 +9,10 @@ export declare function RelationSearchInput({ searchTerm, onSearchChange, relate
|
|
|
9
9
|
onSearchChange: (value: string) => void;
|
|
10
10
|
relatedModelName: string;
|
|
11
11
|
}>): import("react/jsx-runtime").JSX.Element;
|
|
12
|
-
export declare function RelationItem({ item, onSelect }: {
|
|
12
|
+
export declare function RelationItem({ item, onSelect }: Readonly<{
|
|
13
13
|
item: any;
|
|
14
14
|
onSelect: (item: any) => void;
|
|
15
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
}>): import("react/jsx-runtime").JSX.Element;
|
|
16
16
|
export declare function RelationItemList({ items, loading, error, onSelect, onClear }: Readonly<{
|
|
17
17
|
items: any[];
|
|
18
18
|
loading: boolean;
|
|
@@ -20,7 +20,7 @@ export declare function RelationItemList({ items, loading, error, onSelect, onCl
|
|
|
20
20
|
onSelect: (item: any) => void;
|
|
21
21
|
onClear: () => void;
|
|
22
22
|
}>): import("react/jsx-runtime").JSX.Element;
|
|
23
|
-
export declare function RelationDropdownContent({ searchTerm, onSearchChange, relatedModelName, items, loading, error, onSelect, onClear }: {
|
|
23
|
+
export declare function RelationDropdownContent({ searchTerm, onSearchChange, relatedModelName, items, loading, error, onSelect, onClear }: Readonly<{
|
|
24
24
|
searchTerm: string;
|
|
25
25
|
onSearchChange: (value: string) => void;
|
|
26
26
|
relatedModelName: string;
|
|
@@ -29,4 +29,4 @@ export declare function RelationDropdownContent({ searchTerm, onSearchChange, re
|
|
|
29
29
|
error?: any;
|
|
30
30
|
onSelect: (item: any) => void;
|
|
31
31
|
onClear: () => void;
|
|
32
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
32
|
+
}>): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getItemDisplayName } from "../../utils/string-utils.js";
|
|
2
|
-
var _jsxFileName = "/Users/justinhandley/IdeaProjects/nestled_template/libs/
|
|
2
|
+
var _jsxFileName = "/Users/justinhandley/IdeaProjects/nestled_template/libs/data-browser/src/lib/components/filters/RelationComponents.tsx";
|
|
3
3
|
function RelationDropdownButton({
|
|
4
4
|
currentItem,
|
|
5
5
|
relatedModelName,
|
|
@@ -64,7 +64,7 @@ function RelationItemList({
|
|
|
64
64
|
fileName: _jsxFileName,
|
|
65
65
|
lineNumber: 95,
|
|
66
66
|
columnNumber: 15
|
|
67
|
-
} }, "Clear selection"), error
|
|
67
|
+
} }, "Clear selection"), error && /* @__PURE__ */ React.createElement("div", { className: "px-3 py-2 text-sm text-red-600", __self: this, __source: {
|
|
68
68
|
fileName: _jsxFileName,
|
|
69
69
|
lineNumber: 104,
|
|
70
70
|
columnNumber: 9
|
|
@@ -84,33 +84,33 @@ function RelationItemList({
|
|
|
84
84
|
fileName: _jsxFileName,
|
|
85
85
|
lineNumber: 111,
|
|
86
86
|
columnNumber: 11
|
|
87
|
-
} }, error.networkError ? "Network error" : "Please try again"))
|
|
87
|
+
} }, error.networkError ? "Network error" : "Please try again")), !error && loading && /* @__PURE__ */ React.createElement("div", { className: "px-3 py-2 text-sm text-gray-500", __self: this, __source: {
|
|
88
88
|
fileName: _jsxFileName,
|
|
89
|
-
lineNumber:
|
|
89
|
+
lineNumber: 117,
|
|
90
90
|
columnNumber: 9
|
|
91
91
|
} }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center", __self: this, __source: {
|
|
92
92
|
fileName: _jsxFileName,
|
|
93
|
-
lineNumber:
|
|
93
|
+
lineNumber: 118,
|
|
94
94
|
columnNumber: 11
|
|
95
95
|
} }, /* @__PURE__ */ React.createElement("svg", { className: "animate-spin -ml-1 mr-2 h-4 w-4 text-gray-500", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", __self: this, __source: {
|
|
96
96
|
fileName: _jsxFileName,
|
|
97
|
-
lineNumber:
|
|
97
|
+
lineNumber: 119,
|
|
98
98
|
columnNumber: 13
|
|
99
99
|
} }, /* @__PURE__ */ React.createElement("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4", __self: this, __source: {
|
|
100
100
|
fileName: _jsxFileName,
|
|
101
|
-
lineNumber:
|
|
101
|
+
lineNumber: 120,
|
|
102
102
|
columnNumber: 15
|
|
103
103
|
} }), /* @__PURE__ */ React.createElement("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z", __self: this, __source: {
|
|
104
104
|
fileName: _jsxFileName,
|
|
105
|
-
lineNumber:
|
|
105
|
+
lineNumber: 121,
|
|
106
106
|
columnNumber: 15
|
|
107
|
-
} })), "Loading..."))
|
|
107
|
+
} })), "Loading...")), !error && !loading && items.length === 0 && /* @__PURE__ */ React.createElement("div", { className: "px-3 py-2 text-sm text-gray-500", __self: this, __source: {
|
|
108
108
|
fileName: _jsxFileName,
|
|
109
|
-
lineNumber:
|
|
110
|
-
columnNumber:
|
|
111
|
-
} }, "No items found")
|
|
109
|
+
lineNumber: 128,
|
|
110
|
+
columnNumber: 9
|
|
111
|
+
} }, "No items found"), !error && !loading && items.length > 0 && items.map((item) => /* @__PURE__ */ React.createElement(RelationItem, { key: item.id, item, onSelect, __self: this, __source: {
|
|
112
112
|
fileName: _jsxFileName,
|
|
113
|
-
lineNumber:
|
|
113
|
+
lineNumber: 132,
|
|
114
114
|
columnNumber: 11
|
|
115
115
|
} })));
|
|
116
116
|
}
|
|
@@ -126,15 +126,15 @@ function RelationDropdownContent({
|
|
|
126
126
|
}) {
|
|
127
127
|
return /* @__PURE__ */ React.createElement("div", { className: "absolute z-10 w-full mt-1 bg-white border border-gray-300 rounded-md shadow-lg", __self: this, __source: {
|
|
128
128
|
fileName: _jsxFileName,
|
|
129
|
-
lineNumber:
|
|
129
|
+
lineNumber: 164,
|
|
130
130
|
columnNumber: 5
|
|
131
131
|
} }, /* @__PURE__ */ React.createElement(RelationSearchInput, { searchTerm, onSearchChange, relatedModelName, __self: this, __source: {
|
|
132
132
|
fileName: _jsxFileName,
|
|
133
|
-
lineNumber:
|
|
133
|
+
lineNumber: 165,
|
|
134
134
|
columnNumber: 7
|
|
135
135
|
} }), /* @__PURE__ */ React.createElement(RelationItemList, { items, loading, error, onSelect, onClear, __self: this, __source: {
|
|
136
136
|
fileName: _jsxFileName,
|
|
137
|
-
lineNumber:
|
|
137
|
+
lineNumber: 170,
|
|
138
138
|
columnNumber: 7
|
|
139
139
|
} }));
|
|
140
140
|
}
|
|
@@ -3,7 +3,7 @@ import { useClickOutside } from "../../hooks/useClickOutside.js";
|
|
|
3
3
|
import { useRelationData } from "../../hooks/useRelationData.js";
|
|
4
4
|
import { formatFieldName } from "../../utils/string-utils.js";
|
|
5
5
|
import { RelationDropdownButton, RelationDropdownContent } from "./RelationComponents.js";
|
|
6
|
-
var _jsxFileName = "/Users/justinhandley/IdeaProjects/nestled_template/libs/
|
|
6
|
+
var _jsxFileName = "/Users/justinhandley/IdeaProjects/nestled_template/libs/data-browser/src/lib/components/filters/RelationFilterField.tsx";
|
|
7
7
|
function RelationFilterField({
|
|
8
8
|
fieldName,
|
|
9
9
|
relatedModelName,
|
|
@@ -22,7 +22,13 @@ function RelationFilterField({
|
|
|
22
22
|
loading,
|
|
23
23
|
error: relationError,
|
|
24
24
|
hasDocument
|
|
25
|
-
} = useRelationData(
|
|
25
|
+
} = useRelationData(
|
|
26
|
+
relatedModelName,
|
|
27
|
+
searchTerm,
|
|
28
|
+
isOpen,
|
|
29
|
+
currentValue == null ? void 0 : currentValue.id
|
|
30
|
+
// Pass current value ID so it can be loaded even when dropdown is closed
|
|
31
|
+
);
|
|
26
32
|
const currentItem = useMemo(() => (currentValue == null ? void 0 : currentValue.id) ? relatedItems.find((item) => item.id === currentValue.id) : null, [currentValue == null ? void 0 : currentValue.id, relatedItems]);
|
|
27
33
|
const handleSelect = useCallback((item) => {
|
|
28
34
|
onChange({
|
|
@@ -45,39 +51,39 @@ function RelationFilterField({
|
|
|
45
51
|
if (!hasDocument) {
|
|
46
52
|
return /* @__PURE__ */ React.createElement("div", { className: "space-y-1", __self: this, __source: {
|
|
47
53
|
fileName: _jsxFileName,
|
|
48
|
-
lineNumber:
|
|
54
|
+
lineNumber: 68,
|
|
49
55
|
columnNumber: 7
|
|
50
56
|
} }, /* @__PURE__ */ React.createElement("label", { className: "block text-sm font-medium text-gray-700", __self: this, __source: {
|
|
51
57
|
fileName: _jsxFileName,
|
|
52
|
-
lineNumber:
|
|
58
|
+
lineNumber: 69,
|
|
53
59
|
columnNumber: 9
|
|
54
60
|
} }, formatFieldName(fieldName), " ID"), /* @__PURE__ */ React.createElement("input", { type: "text", value: (currentValue == null ? void 0 : currentValue.id) || "", onChange: (e) => onChange(e.target.value ? {
|
|
55
61
|
id: e.target.value
|
|
56
62
|
} : void 0), placeholder: "Enter ID...", className: "w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-web focus:border-green-web text-sm", __self: this, __source: {
|
|
57
63
|
fileName: _jsxFileName,
|
|
58
|
-
lineNumber:
|
|
64
|
+
lineNumber: 72,
|
|
59
65
|
columnNumber: 9
|
|
60
66
|
} }));
|
|
61
67
|
}
|
|
62
68
|
return /* @__PURE__ */ React.createElement("div", { className: "space-y-1", ref: dropdownRef, __self: this, __source: {
|
|
63
69
|
fileName: _jsxFileName,
|
|
64
|
-
lineNumber:
|
|
70
|
+
lineNumber: 84,
|
|
65
71
|
columnNumber: 5
|
|
66
72
|
} }, /* @__PURE__ */ React.createElement("label", { className: "block text-sm font-medium text-gray-700", __self: this, __source: {
|
|
67
73
|
fileName: _jsxFileName,
|
|
68
|
-
lineNumber:
|
|
74
|
+
lineNumber: 85,
|
|
69
75
|
columnNumber: 7
|
|
70
76
|
} }, formatFieldName(fieldName)), /* @__PURE__ */ React.createElement("div", { className: "relative", __self: this, __source: {
|
|
71
77
|
fileName: _jsxFileName,
|
|
72
|
-
lineNumber:
|
|
78
|
+
lineNumber: 88,
|
|
73
79
|
columnNumber: 7
|
|
74
80
|
} }, /* @__PURE__ */ React.createElement(RelationDropdownButton, { currentItem, relatedModelName, isOpen, onClick: handleToggleOpen, __self: this, __source: {
|
|
75
81
|
fileName: _jsxFileName,
|
|
76
|
-
lineNumber:
|
|
82
|
+
lineNumber: 89,
|
|
77
83
|
columnNumber: 9
|
|
78
84
|
} }), isOpen && /* @__PURE__ */ React.createElement(RelationDropdownContent, { searchTerm, onSearchChange: setSearchTerm, relatedModelName, items: relatedItems, loading, error: relationError, onSelect: handleSelect, onClear: handleClear, __self: this, __source: {
|
|
79
85
|
fileName: _jsxFileName,
|
|
80
|
-
lineNumber:
|
|
86
|
+
lineNumber: 97,
|
|
81
87
|
columnNumber: 11
|
|
82
88
|
} })));
|
|
83
89
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Link } from "react-router";
|
|
3
3
|
import { HomeIcon, ChevronRightIcon } from "@heroicons/react/24/outline";
|
|
4
|
-
var _jsxFileName = "/Users/justinhandley/IdeaProjects/nestled_template/libs/
|
|
4
|
+
var _jsxFileName = "/Users/justinhandley/IdeaProjects/nestled_template/libs/data-browser/src/lib/components/shared/AdminBreadcrumbs.tsx";
|
|
5
5
|
function AdminBreadcrumbs({
|
|
6
6
|
items,
|
|
7
7
|
showHome = true,
|
|
@@ -46,11 +46,11 @@ function AdminBreadcrumbs({
|
|
|
46
46
|
};
|
|
47
47
|
return /* @__PURE__ */ React.createElement("nav", { className: `flex ${className}`, "aria-label": "Breadcrumb", __self: this, __source: {
|
|
48
48
|
fileName: _jsxFileName,
|
|
49
|
-
lineNumber:
|
|
49
|
+
lineNumber: 74,
|
|
50
50
|
columnNumber: 5
|
|
51
51
|
} }, /* @__PURE__ */ React.createElement("ol", { className: "flex items-center space-x-4", __self: this, __source: {
|
|
52
52
|
fileName: _jsxFileName,
|
|
53
|
-
lineNumber:
|
|
53
|
+
lineNumber: 75,
|
|
54
54
|
columnNumber: 7
|
|
55
55
|
} }, renderHomeLink(), items.map((item, index) => {
|
|
56
56
|
index === items.length - 1;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { XCircleIcon, ExclamationCircleIcon, ExclamationTriangleIcon } from "@heroicons/react/24/outline";
|
|
3
|
-
var _jsxFileName = "/Users/justinhandley/IdeaProjects/nestled_template/libs/
|
|
3
|
+
var _jsxFileName = "/Users/justinhandley/IdeaProjects/nestled_template/libs/data-browser/src/lib/components/shared/AdminErrorStates.tsx";
|
|
4
4
|
const getSeverityConfig = (severity) => {
|
|
5
5
|
const configs = {
|
|
6
6
|
warning: {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { QuestionMarkCircleIcon, PauseCircleIcon, CheckCircleIcon, XCircleIcon, ExclamationTriangleIcon, ClockIcon } from "@heroicons/react/24/solid";
|
|
3
|
-
var _jsxFileName = "/Users/justinhandley/IdeaProjects/nestled_template/libs/
|
|
3
|
+
var _jsxFileName = "/Users/justinhandley/IdeaProjects/nestled_template/libs/data-browser/src/lib/components/shared/AdminStatusDisplay.tsx";
|
|
4
4
|
const getStatusConfig = (status) => {
|
|
5
5
|
const configs = {
|
|
6
6
|
active: {
|
|
@@ -39,7 +39,7 @@ export interface AdminDataProviderProps {
|
|
|
39
39
|
* </AdminDataProvider>
|
|
40
40
|
* ```
|
|
41
41
|
*/
|
|
42
|
-
export declare function AdminDataProvider({ children, sdk, databaseModels, basePath, formTheme }: AdminDataProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
42
|
+
export declare function AdminDataProvider({ children, sdk, databaseModels, basePath, formTheme }: Readonly<AdminDataProviderProps>): import("react/jsx-runtime").JSX.Element;
|
|
43
43
|
/**
|
|
44
44
|
* Hook to access the admin data context
|
|
45
45
|
* @throws Error if used outside of AdminDataProvider
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React, { createContext, useContext } from "react";
|
|
2
|
-
var _jsxFileName = "/Users/justinhandley/IdeaProjects/nestled_template/libs/
|
|
1
|
+
import React, { createContext, useMemo, useContext } from "react";
|
|
2
|
+
var _jsxFileName = "/Users/justinhandley/IdeaProjects/nestled_template/libs/data-browser/src/lib/context/AdminDataContext.tsx";
|
|
3
3
|
const AdminDataContext = createContext(null);
|
|
4
4
|
function AdminDataProvider({
|
|
5
5
|
children,
|
|
@@ -8,14 +8,15 @@ function AdminDataProvider({
|
|
|
8
8
|
basePath = "/admin/data",
|
|
9
9
|
formTheme
|
|
10
10
|
}) {
|
|
11
|
-
|
|
11
|
+
const value = useMemo(() => ({
|
|
12
12
|
sdk,
|
|
13
13
|
databaseModels,
|
|
14
14
|
basePath,
|
|
15
15
|
formTheme
|
|
16
|
-
},
|
|
16
|
+
}), [sdk, databaseModels, basePath, formTheme]);
|
|
17
|
+
return /* @__PURE__ */ React.createElement(AdminDataContext.Provider, { value, __self: this, __source: {
|
|
17
18
|
fileName: _jsxFileName,
|
|
18
|
-
lineNumber:
|
|
19
|
+
lineNumber: 60,
|
|
19
20
|
columnNumber: 5
|
|
20
21
|
} }, children);
|
|
21
22
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare function useRelationData(relatedModelName: string, searchTerm: string, isOpen: boolean): {
|
|
1
|
+
export declare function useRelationData(relatedModelName: string, searchTerm: string, isOpen: boolean, currentValueId?: string): {
|
|
2
2
|
relatedModel: any;
|
|
3
3
|
relatedItems: any[];
|
|
4
4
|
loading: boolean;
|
|
@@ -4,7 +4,7 @@ import { useAdminDataContext } from "../context/AdminDataContext.js";
|
|
|
4
4
|
import { getAdminDocuments } from "../utils/graphql-utils.js";
|
|
5
5
|
import { getSmartSearchFields } from "../utils/string-utils.js";
|
|
6
6
|
import { useDebounce } from "./useDebounce.js";
|
|
7
|
-
function useRelationData(relatedModelName, searchTerm, isOpen) {
|
|
7
|
+
function useRelationData(relatedModelName, searchTerm, isOpen, currentValueId) {
|
|
8
8
|
const { sdk, databaseModels } = useAdminDataContext();
|
|
9
9
|
const debouncedSearchTerm = useDebounce(searchTerm, 300);
|
|
10
10
|
const relatedModel = useMemo(
|
|
@@ -45,7 +45,8 @@ function useRelationData(relatedModelName, searchTerm, isOpen) {
|
|
|
45
45
|
error: relationError
|
|
46
46
|
} = useQuery(relatedDocuments.listQuery, {
|
|
47
47
|
variables: queryVariables,
|
|
48
|
-
skip
|
|
48
|
+
// Don't skip if we have a currentValueId to display, even if dropdown is closed
|
|
49
|
+
skip: !relatedDocuments.listQuery || !isOpen && !currentValueId,
|
|
49
50
|
errorPolicy: "all",
|
|
50
51
|
// Continue processing even if there are GraphQL errors
|
|
51
52
|
notifyOnNetworkStatusChange: true,
|