@canmingir/link 1.2.10 → 1.2.12
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/package.json +3 -2
- package/src/config/schemas.js +6 -0
- package/src/http/index.js +21 -10
- package/src/layouts/auth/modern.jsx +26 -10
- package/src/layouts/common/ProjectBar/index.jsx +17 -4
- package/src/lib/Flow/core/Flow.jsx +79 -0
- package/src/lib/Flow/core/FlowNode.jsx +68 -0
- package/src/lib/Flow/core/FlowViewport.jsx +259 -0
- package/src/lib/Flow/graph/FloatingGraph.jsx +44 -0
- package/src/lib/Flow/hooks/useGraphOperations.js +362 -0
- package/src/lib/Flow/hooks/useNodeStyle.js +56 -0
- package/src/lib/Flow/index.js +1 -1
- package/src/lib/Flow/nodes/DefaultCard.jsx +107 -0
- package/src/lib/Flow/nodes/DraggableNode.jsx +162 -0
- package/src/lib/Flow/nodes/FlowNodeView.jsx +214 -0
- package/src/lib/Flow/selection/SelectionContext.jsx +259 -0
- package/src/lib/Flow/selection/SelectionOverlay.jsx +31 -0
- package/src/lib/Flow/utils/flowUtils.js +268 -0
- package/src/lib/index.js +1 -1
- package/src/pages/LoginPage.jsx +15 -1
- package/src/widgets/Login/CognitoLogin.jsx +45 -0
- package/src/widgets/Login/DemoLogin.jsx +178 -0
- package/src/widgets/Login/cognitoAuth.jsx +44 -0
- package/src/lib/Flow/DraggableNode.jsx +0 -128
- package/src/lib/Flow/Flow.jsx +0 -40
- package/src/lib/Flow/FlowNode.jsx +0 -519
- package/src/lib/Flow/SelectionContext.jsx +0 -123
- package/src/lib/Flow/flowUtils.js +0 -111
- /package/src/lib/Flow/{DynamicConnector.jsx → connectors/DynamicConnector.jsx} +0 -0
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
export function assertLinkedGraph(data) {
|
|
2
|
+
if (!data || typeof data !== "object") {
|
|
3
|
+
throw new Error(
|
|
4
|
+
"FlowChart expected a linked graph object: { nodes, roots? }."
|
|
5
|
+
);
|
|
6
|
+
}
|
|
7
|
+
const { nodes, roots } = data;
|
|
8
|
+
if (!nodes || typeof nodes !== "object") {
|
|
9
|
+
throw new Error("FlowChart expected data.nodes to be an object.");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let useRoots = Array.isArray(roots) ? [...roots] : null;
|
|
13
|
+
if (!useRoots || useRoots.length === 0) {
|
|
14
|
+
useRoots = Object.keys(nodes).filter((id) => !nodes[id]?.previous);
|
|
15
|
+
}
|
|
16
|
+
if (useRoots.length === 0) {
|
|
17
|
+
const first = Object.keys(nodes)[0];
|
|
18
|
+
if (first) useRoots = [first];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
for (const r of useRoots) {
|
|
22
|
+
if (!nodes[r]) throw new Error(`Root id "${r}" not found in data.nodes.`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return { nodesById: nodes, roots: useRoots };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function buildTreeFromLinked(rootId, nodesById) {
|
|
29
|
+
if (!rootId || !nodesById?.[rootId]) return null;
|
|
30
|
+
const seen = new Set();
|
|
31
|
+
|
|
32
|
+
const cloneNode = (node) => {
|
|
33
|
+
if (!node) return null;
|
|
34
|
+
const { next, previous, children, ...rest } = node;
|
|
35
|
+
return { ...rest, id: node.id, previous, next, children: [] };
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const dfs = (id) => {
|
|
39
|
+
if (!id || seen.has(id) || !nodesById[id]) return null;
|
|
40
|
+
seen.add(id);
|
|
41
|
+
|
|
42
|
+
const node = nodesById[id];
|
|
43
|
+
const out = cloneNode(node);
|
|
44
|
+
|
|
45
|
+
const nextArr = Array.isArray(node.next)
|
|
46
|
+
? node.next
|
|
47
|
+
: node.next != null
|
|
48
|
+
? [node.next]
|
|
49
|
+
: [];
|
|
50
|
+
|
|
51
|
+
for (const nxt of nextArr) {
|
|
52
|
+
const nextId = typeof nxt === "string" ? nxt : nxt?.id;
|
|
53
|
+
if (!nextId || !nodesById[nextId]) continue;
|
|
54
|
+
|
|
55
|
+
const target = nodesById[nextId];
|
|
56
|
+
if (target.previous == null || target.previous === id) {
|
|
57
|
+
const built = dfs(nextId);
|
|
58
|
+
if (built) out.children.push(built);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
return dfs(rootId);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const getContentParts = (n) => {
|
|
68
|
+
const entries = Object.entries(n).filter(
|
|
69
|
+
([key]) =>
|
|
70
|
+
key !== "children" && key !== "id" && key !== "previous" && key !== "next"
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
if (entries.length === 0) {
|
|
74
|
+
return {
|
|
75
|
+
title: "(empty)",
|
|
76
|
+
subtitle: null,
|
|
77
|
+
metaEntries: [],
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const preferredTitleKeys = ["label", "title", "name"];
|
|
82
|
+
const titleEntry =
|
|
83
|
+
entries.find(([key]) => preferredTitleKeys.includes(key)) || entries[0];
|
|
84
|
+
const [titleKey, rawTitle] = titleEntry;
|
|
85
|
+
const title = String(rawTitle);
|
|
86
|
+
let remaining = entries.filter(([key]) => key !== titleKey);
|
|
87
|
+
|
|
88
|
+
const preferredSubtitleKeys = ["description", "role", "type", "status"];
|
|
89
|
+
const subtitleEntry =
|
|
90
|
+
remaining.find(([key]) => preferredSubtitleKeys.includes(key)) || null;
|
|
91
|
+
|
|
92
|
+
let subtitle = null;
|
|
93
|
+
if (subtitleEntry) {
|
|
94
|
+
const [subtitleKey, raw] = subtitleEntry;
|
|
95
|
+
subtitle = String(raw);
|
|
96
|
+
remaining = remaining.filter(([key]) => key !== subtitleKey);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const metaEntries = remaining
|
|
100
|
+
.filter(([, value]) => {
|
|
101
|
+
const t = typeof value;
|
|
102
|
+
return (
|
|
103
|
+
(t === "string" || t === "number" || t === "boolean") &&
|
|
104
|
+
value !== "" &&
|
|
105
|
+
value !== null
|
|
106
|
+
);
|
|
107
|
+
})
|
|
108
|
+
.map(([k, v]) => [k, v]);
|
|
109
|
+
|
|
110
|
+
return { title, subtitle, metaEntries };
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export const toNextArray = (next) => {
|
|
114
|
+
if (!next) return [];
|
|
115
|
+
return Array.isArray(next) ? next : [next];
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export const setNextProperty = (node, nextIds) => {
|
|
119
|
+
if (!nextIds || nextIds.length === 0) {
|
|
120
|
+
delete node.next;
|
|
121
|
+
} else if (nextIds.length === 1) {
|
|
122
|
+
node.next = nextIds[0];
|
|
123
|
+
} else {
|
|
124
|
+
node.next = nextIds;
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
export const addToNext = (node, childIds) => {
|
|
129
|
+
const current = toNextArray(node.next);
|
|
130
|
+
const newIds = childIds.filter((id) => !current.includes(id));
|
|
131
|
+
setNextProperty(node, [...current, ...newIds]);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export const removeFromNext = (node, childIds) => {
|
|
135
|
+
const removeSet = new Set(childIds);
|
|
136
|
+
const filtered = toNextArray(node.next).filter((id) => !removeSet.has(id));
|
|
137
|
+
setNextProperty(node, filtered);
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
export const cleanupReferences = (nodes, removedIds) => {
|
|
141
|
+
const removeSet = new Set(removedIds);
|
|
142
|
+
Object.values(nodes).forEach((node) => {
|
|
143
|
+
if (removeSet.has(node.next)) {
|
|
144
|
+
delete node.next;
|
|
145
|
+
} else if (Array.isArray(node.next)) {
|
|
146
|
+
const filtered = node.next.filter((n) => !removeSet.has(n));
|
|
147
|
+
setNextProperty(node, filtered);
|
|
148
|
+
}
|
|
149
|
+
if (removeSet.has(node.previous)) {
|
|
150
|
+
delete node.previous;
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
export const hexToRgba = (hex, alpha) => {
|
|
156
|
+
const r = parseInt(hex.slice(1, 3), 16);
|
|
157
|
+
const g = parseInt(hex.slice(3, 5), 16);
|
|
158
|
+
const b = parseInt(hex.slice(5, 7), 16);
|
|
159
|
+
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
export const buildDetachedTree = (rootId, nodesById) => {
|
|
163
|
+
if (!rootId || !nodesById?.[rootId]) return null;
|
|
164
|
+
const seen = new Set();
|
|
165
|
+
|
|
166
|
+
const buildNode = (id) => {
|
|
167
|
+
if (!id || seen.has(id) || !nodesById[id]) return null;
|
|
168
|
+
seen.add(id);
|
|
169
|
+
|
|
170
|
+
const node = nodesById[id];
|
|
171
|
+
const { next, previous, ...rest } = node;
|
|
172
|
+
const result = { ...rest, id, children: [] };
|
|
173
|
+
|
|
174
|
+
const nextIds = Array.isArray(next) ? next : next != null ? [next] : [];
|
|
175
|
+
|
|
176
|
+
nextIds.forEach((nxt) => {
|
|
177
|
+
const nextId = typeof nxt === "string" ? nxt : nxt?.id;
|
|
178
|
+
if (!nextId || !nodesById[nextId]) return;
|
|
179
|
+
|
|
180
|
+
const child = buildNode(nextId);
|
|
181
|
+
if (child) result.children.push(child);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
return result;
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
return buildNode(rootId);
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
export const getSelectedInStructure = (structure, selectedIds) =>
|
|
191
|
+
selectedIds.filter((id) => structure.nodes?.[id]);
|
|
192
|
+
|
|
193
|
+
export const getRootsToConnect = (structure, selectedIds) => {
|
|
194
|
+
const selectedSet = new Set(selectedIds);
|
|
195
|
+
const roots = [];
|
|
196
|
+
|
|
197
|
+
selectedIds.forEach((id) => {
|
|
198
|
+
let current = structure.nodes[id];
|
|
199
|
+
if (!current) return;
|
|
200
|
+
|
|
201
|
+
let rootId = id;
|
|
202
|
+
|
|
203
|
+
while (current.previous && structure.nodes[current.previous]) {
|
|
204
|
+
if (selectedSet.has(current.previous)) {
|
|
205
|
+
rootId = current.previous;
|
|
206
|
+
current = structure.nodes[current.previous];
|
|
207
|
+
} else break;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (!current.previous && !roots.includes(rootId)) {
|
|
211
|
+
roots.push(rootId);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
if (roots.length > 0) return roots;
|
|
216
|
+
|
|
217
|
+
return selectedIds.filter((id) => {
|
|
218
|
+
const node = structure.nodes[id];
|
|
219
|
+
return node && (!node.previous || !structure.nodes[node.previous]);
|
|
220
|
+
});
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
export const collectSubtree = (structure, roots, selectedIds) => {
|
|
224
|
+
const selectedSet = new Set(selectedIds);
|
|
225
|
+
const collected = new Set();
|
|
226
|
+
|
|
227
|
+
const dfs = (id) => {
|
|
228
|
+
if (collected.has(id) || !structure.nodes[id]) return;
|
|
229
|
+
collected.add(id);
|
|
230
|
+
|
|
231
|
+
toNextArray(structure.nodes[id].next).forEach((childId) => {
|
|
232
|
+
if (structure.nodes[childId] && selectedSet.has(childId)) {
|
|
233
|
+
dfs(childId);
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
roots.forEach(dfs);
|
|
239
|
+
return collected;
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
export const splitFloatingStructure = (structure, removedIds) => {
|
|
243
|
+
const remainingNodes = {};
|
|
244
|
+
|
|
245
|
+
Object.entries(structure.nodes).forEach(([id, node]) => {
|
|
246
|
+
if (!removedIds.has(id)) {
|
|
247
|
+
remainingNodes[id] = node;
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
cleanupReferences(remainingNodes, [...removedIds]);
|
|
252
|
+
|
|
253
|
+
const remainingRoots =
|
|
254
|
+
structure.roots?.filter((r) => !removedIds.has(r)) ?? [];
|
|
255
|
+
|
|
256
|
+
const roots =
|
|
257
|
+
remainingRoots.length > 0
|
|
258
|
+
? remainingRoots
|
|
259
|
+
: Object.keys(remainingNodes).filter(
|
|
260
|
+
(id) => !remainingNodes[id].previous
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
return {
|
|
264
|
+
...structure,
|
|
265
|
+
nodes: remainingNodes,
|
|
266
|
+
roots: [...new Set(roots)],
|
|
267
|
+
};
|
|
268
|
+
};
|
package/src/lib/index.js
CHANGED
|
@@ -22,7 +22,7 @@ export { default as TableSelectedAction } from "./TableSelectedAction/TableSelec
|
|
|
22
22
|
export { default as useTable } from "./useTable/useTable";
|
|
23
23
|
export { default as useChart } from "./useChart/useChart";
|
|
24
24
|
|
|
25
|
-
export { default as Flow } from "./Flow/Flow";
|
|
25
|
+
export { default as Flow } from "./Flow/core/Flow";
|
|
26
26
|
|
|
27
27
|
export {
|
|
28
28
|
HeaderCard,
|
package/src/pages/LoginPage.jsx
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import CognitoLogin from "../widgets/Login/CognitoLogin";
|
|
2
|
+
import DemoLogin from "../widgets/Login/DemoLogin";
|
|
1
3
|
import LoginForm from "../widgets/LoginForm/LoginForm";
|
|
2
4
|
import Page from "../layouts/Page";
|
|
3
5
|
import React from "react";
|
|
@@ -6,7 +8,7 @@ import { storage } from "@nucleoidjs/webstorage";
|
|
|
6
8
|
import { useEffect } from "react";
|
|
7
9
|
import { useNavigate } from "react-router-dom";
|
|
8
10
|
function LoginPage() {
|
|
9
|
-
const { name, template } = config();
|
|
11
|
+
const { name, template, credentials } = config();
|
|
10
12
|
const formColor = "#a8a9ad";
|
|
11
13
|
const navigate = useNavigate();
|
|
12
14
|
|
|
@@ -28,6 +30,18 @@ function LoginPage() {
|
|
|
28
30
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
29
31
|
}, [navigate]);
|
|
30
32
|
|
|
33
|
+
if (credentials?.provider === "COGNITO") {
|
|
34
|
+
return <CognitoLogin />;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (credentials?.provider === "DEMO") {
|
|
38
|
+
return (
|
|
39
|
+
<Page title={`Sign in to ${name}`}>
|
|
40
|
+
<DemoLogin />
|
|
41
|
+
</Page>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
31
45
|
return (
|
|
32
46
|
<Page title={`Sign in to ${name}`}>
|
|
33
47
|
<LoginForm icon={template.login.icon} name={name} formColor={formColor} />
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { loginWithCognito } from "./cognitoAuth";
|
|
2
|
+
import { storage } from "@nucleoidjs/webstorage";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
|
|
5
|
+
import { Button, Stack, TextField, Typography } from "@mui/material";
|
|
6
|
+
|
|
7
|
+
export default function CognitoLogin() {
|
|
8
|
+
const [username, setUsername] = useState("");
|
|
9
|
+
const [password, setPassword] = useState("");
|
|
10
|
+
|
|
11
|
+
const handleLogin = async () => {
|
|
12
|
+
try {
|
|
13
|
+
const tokens = await loginWithCognito(username, password);
|
|
14
|
+
console.log("Login successful, tokens:", tokens);
|
|
15
|
+
storage.set("link", "accessToken", tokens.AccessToken);
|
|
16
|
+
storage.set("link", "refreshToken", tokens.RefreshToken);
|
|
17
|
+
window.location.href = "/";
|
|
18
|
+
} catch (e) {
|
|
19
|
+
console.error("Login error:", e);
|
|
20
|
+
alert(`Login failed: ${e.message || e}`);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<>
|
|
26
|
+
<Typography variant="h4">Login</Typography>
|
|
27
|
+
<Stack spacing={2}>
|
|
28
|
+
<TextField
|
|
29
|
+
label="Username or Email"
|
|
30
|
+
value={username}
|
|
31
|
+
onChange={(e) => setUsername(e.target.value)}
|
|
32
|
+
/>
|
|
33
|
+
<TextField
|
|
34
|
+
type="password"
|
|
35
|
+
label="Password"
|
|
36
|
+
value={password}
|
|
37
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
38
|
+
/>
|
|
39
|
+
<Button variant="contained" onClick={handleLogin}>
|
|
40
|
+
Login
|
|
41
|
+
</Button>
|
|
42
|
+
</Stack>
|
|
43
|
+
</>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Box,
|
|
3
|
+
Button,
|
|
4
|
+
IconButton,
|
|
5
|
+
InputAdornment,
|
|
6
|
+
Stack,
|
|
7
|
+
TextField,
|
|
8
|
+
Typography,
|
|
9
|
+
alpha,
|
|
10
|
+
} from "@mui/material";
|
|
11
|
+
import {
|
|
12
|
+
LockOutlined,
|
|
13
|
+
PersonOutline,
|
|
14
|
+
Visibility,
|
|
15
|
+
VisibilityOff,
|
|
16
|
+
} from "@mui/icons-material";
|
|
17
|
+
import React, { useState } from "react";
|
|
18
|
+
|
|
19
|
+
import config from "../../config/config";
|
|
20
|
+
import { storage } from "@nucleoidjs/webstorage";
|
|
21
|
+
import { useNavigate } from "react-router-dom";
|
|
22
|
+
|
|
23
|
+
export default function DemoLogin() {
|
|
24
|
+
const [username, setUsername] = useState("");
|
|
25
|
+
const [password, setPassword] = useState("");
|
|
26
|
+
const [showPassword, setShowPassword] = useState(false);
|
|
27
|
+
const navigate = useNavigate();
|
|
28
|
+
|
|
29
|
+
const { appId } = config();
|
|
30
|
+
|
|
31
|
+
async function handleLogin() {
|
|
32
|
+
const res = await fetch("/api/oauth/demo", {
|
|
33
|
+
method: "POST",
|
|
34
|
+
headers: { "Content-Type": "application/json" },
|
|
35
|
+
body: JSON.stringify({
|
|
36
|
+
appId: appId,
|
|
37
|
+
projectId: "cb16e069-6214-47f1-9922-1f7fe7629525",
|
|
38
|
+
username,
|
|
39
|
+
password,
|
|
40
|
+
}),
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
if (!res.ok) throw new Error("Demo login failed");
|
|
44
|
+
|
|
45
|
+
const data = await res.json();
|
|
46
|
+
|
|
47
|
+
storage.set("link", "accessToken", data.accessToken);
|
|
48
|
+
storage.set("link", "refreshToken", data.refreshToken);
|
|
49
|
+
storage.set("link", "identityProvider", "Demo");
|
|
50
|
+
|
|
51
|
+
navigate("/");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<Stack spacing={2.5}>
|
|
56
|
+
<Stack spacing={2}>
|
|
57
|
+
<TextField
|
|
58
|
+
label="Username"
|
|
59
|
+
value={username}
|
|
60
|
+
onChange={(e) => setUsername(e.target.value)}
|
|
61
|
+
fullWidth
|
|
62
|
+
InputProps={{
|
|
63
|
+
startAdornment: (
|
|
64
|
+
<InputAdornment position="start">
|
|
65
|
+
<PersonOutline sx={{ color: "text.secondary", fontSize: 22 }} />
|
|
66
|
+
</InputAdornment>
|
|
67
|
+
),
|
|
68
|
+
}}
|
|
69
|
+
sx={{
|
|
70
|
+
"& .MuiOutlinedInput-root": {
|
|
71
|
+
fontSize: "1rem",
|
|
72
|
+
"& input": {
|
|
73
|
+
py: 1.5,
|
|
74
|
+
},
|
|
75
|
+
"&:hover fieldset": {
|
|
76
|
+
borderColor: "primary.main",
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
}}
|
|
80
|
+
/>
|
|
81
|
+
|
|
82
|
+
<TextField
|
|
83
|
+
label="Password"
|
|
84
|
+
type={showPassword ? "text" : "password"}
|
|
85
|
+
value={password}
|
|
86
|
+
onChange={(e) => setPassword(e.target.value)}
|
|
87
|
+
fullWidth
|
|
88
|
+
InputProps={{
|
|
89
|
+
startAdornment: (
|
|
90
|
+
<InputAdornment position="start">
|
|
91
|
+
<LockOutlined sx={{ color: "text.secondary", fontSize: 22 }} />
|
|
92
|
+
</InputAdornment>
|
|
93
|
+
),
|
|
94
|
+
endAdornment: (
|
|
95
|
+
<InputAdornment position="end">
|
|
96
|
+
<IconButton
|
|
97
|
+
onClick={() => setShowPassword(!showPassword)}
|
|
98
|
+
edge="end"
|
|
99
|
+
size="small"
|
|
100
|
+
tabIndex={-1}
|
|
101
|
+
>
|
|
102
|
+
{showPassword ? <VisibilityOff /> : <Visibility />}
|
|
103
|
+
</IconButton>
|
|
104
|
+
</InputAdornment>
|
|
105
|
+
),
|
|
106
|
+
}}
|
|
107
|
+
sx={{
|
|
108
|
+
"& .MuiOutlinedInput-root": {
|
|
109
|
+
fontSize: "1rem",
|
|
110
|
+
"& input": {
|
|
111
|
+
py: 1.5,
|
|
112
|
+
},
|
|
113
|
+
"&:hover fieldset": {
|
|
114
|
+
borderColor: "primary.main",
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
}}
|
|
118
|
+
onKeyPress={(e) => {
|
|
119
|
+
if (e.key === "Enter") {
|
|
120
|
+
handleLogin();
|
|
121
|
+
}
|
|
122
|
+
}}
|
|
123
|
+
/>
|
|
124
|
+
</Stack>
|
|
125
|
+
|
|
126
|
+
<Button
|
|
127
|
+
variant="contained"
|
|
128
|
+
onClick={handleLogin}
|
|
129
|
+
size="large"
|
|
130
|
+
fullWidth
|
|
131
|
+
sx={{
|
|
132
|
+
mt: 1,
|
|
133
|
+
py: 1.5,
|
|
134
|
+
fontSize: "1rem",
|
|
135
|
+
fontWeight: 600,
|
|
136
|
+
textTransform: "none",
|
|
137
|
+
borderRadius: 1.5,
|
|
138
|
+
boxShadow: (theme) =>
|
|
139
|
+
`0 8px 16px ${alpha(theme.palette.primary.main, 0.24)}`,
|
|
140
|
+
transition: "all 0.2s cubic-bezier(0.4, 0, 0.2, 1)",
|
|
141
|
+
"&:hover": {
|
|
142
|
+
transform: "translateY(-2px)",
|
|
143
|
+
boxShadow: (theme) =>
|
|
144
|
+
`0 12px 24px ${alpha(theme.palette.primary.main, 0.32)}`,
|
|
145
|
+
},
|
|
146
|
+
"&:active": {
|
|
147
|
+
transform: "translateY(0px)",
|
|
148
|
+
},
|
|
149
|
+
}}
|
|
150
|
+
>
|
|
151
|
+
Sign in
|
|
152
|
+
</Button>
|
|
153
|
+
|
|
154
|
+
<Box
|
|
155
|
+
sx={{
|
|
156
|
+
mt: 1,
|
|
157
|
+
textAlign: "center",
|
|
158
|
+
p: 2,
|
|
159
|
+
borderRadius: 1.5,
|
|
160
|
+
bgcolor: (theme) => alpha(theme.palette.info.main, 0.08),
|
|
161
|
+
border: (theme) =>
|
|
162
|
+
`1px dashed ${alpha(theme.palette.info.main, 0.24)}`,
|
|
163
|
+
}}
|
|
164
|
+
>
|
|
165
|
+
<Typography
|
|
166
|
+
variant="caption"
|
|
167
|
+
color="text.secondary"
|
|
168
|
+
sx={{ fontSize: "0.8125rem" }}
|
|
169
|
+
>
|
|
170
|
+
Demo credentials:{" "}
|
|
171
|
+
<Box component="strong" sx={{ color: "text.primary" }}>
|
|
172
|
+
admin / admin
|
|
173
|
+
</Box>
|
|
174
|
+
</Typography>
|
|
175
|
+
</Box>
|
|
176
|
+
</Stack>
|
|
177
|
+
);
|
|
178
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import config from "../../config/config";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
CognitoIdentityProviderClient,
|
|
5
|
+
InitiateAuthCommand,
|
|
6
|
+
} from "@aws-sdk/client-cognito-identity-provider";
|
|
7
|
+
|
|
8
|
+
let client = null;
|
|
9
|
+
|
|
10
|
+
function getClient() {
|
|
11
|
+
if (!client) {
|
|
12
|
+
const { credentials } = config();
|
|
13
|
+
if (!credentials) {
|
|
14
|
+
throw new Error("Cognito credentials not initialized yet");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
client = new CognitoIdentityProviderClient({
|
|
18
|
+
region: credentials.region,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
console.log("Initialized Cognito Client with region:", credentials.region);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return client;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function loginWithCognito(username, password) {
|
|
28
|
+
const { credentials } = config();
|
|
29
|
+
if (!credentials) {
|
|
30
|
+
throw new Error("CONFIG not initialized yet. Wait for CONFIG_INITIALIZED.");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const command = new InitiateAuthCommand({
|
|
34
|
+
AuthFlow: "USER_PASSWORD_AUTH",
|
|
35
|
+
ClientId: credentials.clientId,
|
|
36
|
+
AuthParameters: {
|
|
37
|
+
USERNAME: username,
|
|
38
|
+
PASSWORD: password,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const response = await getClient().send(command);
|
|
43
|
+
return response.AuthenticationResult;
|
|
44
|
+
}
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import { Box } from "@mui/material";
|
|
2
|
-
import { useSelection } from "./SelectionContext";
|
|
3
|
-
|
|
4
|
-
import React, { useEffect, useRef, useState } from "react";
|
|
5
|
-
|
|
6
|
-
const DraggableNode = ({
|
|
7
|
-
children,
|
|
8
|
-
registerRef,
|
|
9
|
-
onDrag,
|
|
10
|
-
nodeId,
|
|
11
|
-
selectionColor = "#64748b",
|
|
12
|
-
}) => {
|
|
13
|
-
const [offset, setOffset] = useState({ x: 0, y: 0 });
|
|
14
|
-
const localRef = useRef(null);
|
|
15
|
-
const lastDeltaRef = useRef({ x: 0, y: 0 });
|
|
16
|
-
|
|
17
|
-
const {
|
|
18
|
-
isSelected,
|
|
19
|
-
selectNode,
|
|
20
|
-
toggleSelection,
|
|
21
|
-
clearSelection,
|
|
22
|
-
registerNodeHandlers,
|
|
23
|
-
moveSelectedNodes,
|
|
24
|
-
selectedIds,
|
|
25
|
-
} = useSelection();
|
|
26
|
-
|
|
27
|
-
const selected = isSelected(nodeId);
|
|
28
|
-
const onDragRef = useRef(onDrag);
|
|
29
|
-
|
|
30
|
-
useEffect(() => {
|
|
31
|
-
onDragRef.current = onDrag;
|
|
32
|
-
}, [onDrag]);
|
|
33
|
-
|
|
34
|
-
useEffect(() => {
|
|
35
|
-
if (nodeId) {
|
|
36
|
-
return registerNodeHandlers(nodeId, {
|
|
37
|
-
setOffset,
|
|
38
|
-
onDrag: () => onDragRef.current?.(),
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
}, [nodeId, registerNodeHandlers]);
|
|
42
|
-
|
|
43
|
-
const setRef = (el) => {
|
|
44
|
-
localRef.current = el;
|
|
45
|
-
if (registerRef) registerRef(el);
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
const handleMouseDown = (e) => {
|
|
49
|
-
if (e.button !== 0) return;
|
|
50
|
-
e.stopPropagation();
|
|
51
|
-
|
|
52
|
-
if (e.shiftKey || e.ctrlKey || e.metaKey) {
|
|
53
|
-
toggleSelection(nodeId);
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
if (!selected) {
|
|
57
|
-
clearSelection();
|
|
58
|
-
selectNode(nodeId);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const startX = e.clientX;
|
|
62
|
-
const startY = e.clientY;
|
|
63
|
-
const startOffset = { ...offset };
|
|
64
|
-
lastDeltaRef.current = { x: 0, y: 0 };
|
|
65
|
-
|
|
66
|
-
const onMove = (ev) => {
|
|
67
|
-
const dx = ev.clientX - startX;
|
|
68
|
-
const dy = ev.clientY - startY;
|
|
69
|
-
|
|
70
|
-
const deltaDx = dx - lastDeltaRef.current.x;
|
|
71
|
-
const deltaDy = dy - lastDeltaRef.current.y;
|
|
72
|
-
lastDeltaRef.current = { x: dx, y: dy };
|
|
73
|
-
|
|
74
|
-
setOffset({
|
|
75
|
-
x: startOffset.x + dx,
|
|
76
|
-
y: startOffset.y + dy,
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
if (selectedIds.size > 1) {
|
|
80
|
-
moveSelectedNodes(deltaDx, deltaDy, nodeId);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (onDrag) onDrag();
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
const onUp = () => {
|
|
87
|
-
window.removeEventListener("mousemove", onMove);
|
|
88
|
-
window.removeEventListener("mouseup", onUp);
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
window.addEventListener("mousemove", onMove);
|
|
92
|
-
window.addEventListener("mouseup", onUp);
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
return (
|
|
96
|
-
<Box
|
|
97
|
-
ref={setRef}
|
|
98
|
-
data-node-id={nodeId}
|
|
99
|
-
onMouseDown={handleMouseDown}
|
|
100
|
-
sx={{
|
|
101
|
-
display: "inline-flex",
|
|
102
|
-
flexDirection: "column",
|
|
103
|
-
alignItems: "center",
|
|
104
|
-
position: "relative",
|
|
105
|
-
transform: `translate(${offset.x}px, ${offset.y}px)`,
|
|
106
|
-
cursor: "grab",
|
|
107
|
-
"&:active": {
|
|
108
|
-
cursor: "grabbing",
|
|
109
|
-
},
|
|
110
|
-
...(selected && {
|
|
111
|
-
"&::after": {
|
|
112
|
-
content: '""',
|
|
113
|
-
position: "absolute",
|
|
114
|
-
inset: -6,
|
|
115
|
-
border: `2px solid ${selectionColor}`,
|
|
116
|
-
borderRadius: "12px",
|
|
117
|
-
pointerEvents: "none",
|
|
118
|
-
boxShadow: `0 0 8px ${selectionColor}66`,
|
|
119
|
-
},
|
|
120
|
-
}),
|
|
121
|
-
}}
|
|
122
|
-
>
|
|
123
|
-
{children}
|
|
124
|
-
</Box>
|
|
125
|
-
);
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
export default DraggableNode;
|