@canmingir/link 1.2.44 → 1.2.49
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 +4 -1
- package/server/server.ts +131 -0
- package/src/components/logo/logo.jsx +4 -4
- package/src/config/schemas.js +7 -6
- package/src/layouts/DashboardLayout/header.jsx +12 -4
- package/src/lib/DevTool/DevTool.jsx +117 -0
- package/src/lib/DevTool/index.js +1 -0
- package/src/lib/Flow/nodes/DraggableNode.jsx +47 -12
- package/src/lib/Flow/selection/SelectionOverlay.jsx +50 -3
- package/src/lib/index.js +1 -0
- package/src/widgets/Login/CognitoLogin.jsx +123 -11
- package/src/widgets/Login/amplifyAuth.js +14 -0
- package/vite/vite.js +13 -1
package/package.json
CHANGED
package/server/server.ts
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import path from "path";
|
|
4
|
+
|
|
5
|
+
const CORS_HEADERS: Record<string, string> = {
|
|
6
|
+
"Access-Control-Allow-Origin": "*",
|
|
7
|
+
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
|
|
8
|
+
"Access-Control-Allow-Headers": "*",
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const TIMEOUT_MS = 120_000;
|
|
12
|
+
|
|
13
|
+
function parseArgs(): Record<string, string> {
|
|
14
|
+
const args = process.argv.slice(2);
|
|
15
|
+
const result: Record<string, string> = {};
|
|
16
|
+
|
|
17
|
+
for (let i = 0; i < args.length; i++) {
|
|
18
|
+
if (args[i].startsWith("--") && args[i + 1] && !args[i + 1].startsWith("--")) {
|
|
19
|
+
result[args[i].slice(2)] = args[i + 1];
|
|
20
|
+
i++;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function getDefaultDashboardDist(): Promise<string> {
|
|
28
|
+
try {
|
|
29
|
+
const configPath = path.join(process.cwd(), "config.js");
|
|
30
|
+
const { default: config } = await import(configPath);
|
|
31
|
+
const base = (config.base as string)?.replace(/^\//, "") ?? "dashboard";
|
|
32
|
+
return `./${base}/dist`;
|
|
33
|
+
} catch {
|
|
34
|
+
return "./dashboard/dist";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function proxyRequest(
|
|
39
|
+
req: Request,
|
|
40
|
+
targetUrl: string,
|
|
41
|
+
): Promise<Response> {
|
|
42
|
+
const headers = new Headers(req.headers);
|
|
43
|
+
headers.delete("host");
|
|
44
|
+
|
|
45
|
+
const hasBody = req.method !== "GET" && req.method !== "HEAD";
|
|
46
|
+
|
|
47
|
+
const response = await fetch(targetUrl, {
|
|
48
|
+
method: req.method,
|
|
49
|
+
headers,
|
|
50
|
+
body: hasBody ? req.body : undefined,
|
|
51
|
+
signal: AbortSignal.timeout(TIMEOUT_MS),
|
|
52
|
+
// @ts-ignore — Bun requires this for streaming request bodies
|
|
53
|
+
duplex: "half",
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const responseHeaders = new Headers(response.headers);
|
|
57
|
+
for (const [key, value] of Object.entries(CORS_HEADERS)) {
|
|
58
|
+
responseHeaders.set(key, value);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return new Response(response.body, {
|
|
62
|
+
status: response.status,
|
|
63
|
+
statusText: response.statusText,
|
|
64
|
+
headers: responseHeaders,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function serveStatic(
|
|
69
|
+
pathname: string,
|
|
70
|
+
dashboardDist: string,
|
|
71
|
+
): Promise<Response> {
|
|
72
|
+
const subPath = pathname.replace(/^\/dashboard\/?/, "") || "index.html";
|
|
73
|
+
const file = Bun.file(path.join(dashboardDist, subPath));
|
|
74
|
+
|
|
75
|
+
if (await file.exists()) {
|
|
76
|
+
return new Response(file);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return new Response(Bun.file(path.join(dashboardDist, "index.html")));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const parsed = parseArgs();
|
|
83
|
+
|
|
84
|
+
const PORT = Number(parsed["port"] ?? 3000);
|
|
85
|
+
const API_TARGET = parsed["api"] ?? "http://localhost:4000";
|
|
86
|
+
const DASHBOARD_DIST = path.resolve(
|
|
87
|
+
parsed["dist-location"] ?? await getDefaultDashboardDist()
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const server = Bun.serve({
|
|
91
|
+
port: PORT,
|
|
92
|
+
|
|
93
|
+
async fetch(req) {
|
|
94
|
+
const url = new URL(req.url);
|
|
95
|
+
|
|
96
|
+
if (req.method === "OPTIONS") {
|
|
97
|
+
return new Response(null, { status: 204, headers: CORS_HEADERS });
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (url.pathname === "/") {
|
|
101
|
+
return Response.redirect("/dashboard", 301);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
if (url.pathname.startsWith("/api")) {
|
|
106
|
+
const newPath = url.pathname.replace(/^\/api/, "") || "/";
|
|
107
|
+
console.info(
|
|
108
|
+
`[Proxy] ${req.method} ${url.pathname} → ${API_TARGET}${newPath}`,
|
|
109
|
+
);
|
|
110
|
+
return await proxyRequest(req, `${API_TARGET}${newPath}${url.search}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return await serveStatic(url.pathname, DASHBOARD_DIST);
|
|
114
|
+
} catch (err: unknown) {
|
|
115
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
116
|
+
console.error("[Proxy Error]", {
|
|
117
|
+
path: url.pathname,
|
|
118
|
+
method: req.method,
|
|
119
|
+
message,
|
|
120
|
+
});
|
|
121
|
+
return new Response(`Proxy Error: ${message}`, {
|
|
122
|
+
status: 500,
|
|
123
|
+
headers: CORS_HEADERS,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
console.log(
|
|
130
|
+
`Server running on port ${server.port} [static: ${DASHBOARD_DIST}]`,
|
|
131
|
+
);
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from "react";
|
|
2
|
+
|
|
1
3
|
import Box from "@mui/material/Box";
|
|
2
4
|
import Link from "@mui/material/Link";
|
|
3
5
|
import { RouterLink } from "../../routes/components";
|
|
4
6
|
import config from "../../config/config";
|
|
5
7
|
|
|
6
|
-
import React, { useEffect, useRef, useState } from "react";
|
|
7
|
-
|
|
8
8
|
const resolvedDimensions = {};
|
|
9
9
|
|
|
10
10
|
const Logo = ({ disabledLink = false, sx, maxSize = 65, isLogin = false }) => {
|
|
11
11
|
const { icon } = config().template.login;
|
|
12
|
-
const key = `${icon}`;
|
|
12
|
+
const key = `${icon}_${maxSize}_${isLogin}`;
|
|
13
13
|
|
|
14
14
|
const [dimensions, setDimensions] = useState(
|
|
15
|
-
resolvedDimensions[key] || { width: maxSize, height: maxSize }
|
|
15
|
+
resolvedDimensions[key] || { width: maxSize, height: maxSize },
|
|
16
16
|
);
|
|
17
17
|
|
|
18
18
|
useEffect(() => {
|
package/src/config/schemas.js
CHANGED
|
@@ -65,13 +65,13 @@ export const MenuConfigSchema = Joi.object({
|
|
|
65
65
|
path: Joi.string().required(),
|
|
66
66
|
icon: Joi.string().required(),
|
|
67
67
|
external: Joi.boolean().optional().default(false),
|
|
68
|
-
})
|
|
68
|
+
}),
|
|
69
69
|
)
|
|
70
70
|
.optional(),
|
|
71
|
-
})
|
|
71
|
+
}),
|
|
72
72
|
)
|
|
73
73
|
.required(),
|
|
74
|
-
})
|
|
74
|
+
}),
|
|
75
75
|
)
|
|
76
76
|
.optional()
|
|
77
77
|
.default([]),
|
|
@@ -81,7 +81,7 @@ export const MenuConfigSchema = Joi.object({
|
|
|
81
81
|
title: Joi.string().required(),
|
|
82
82
|
icon: Joi.string().required(),
|
|
83
83
|
path: Joi.string().required(),
|
|
84
|
-
})
|
|
84
|
+
}),
|
|
85
85
|
)
|
|
86
86
|
.optional()
|
|
87
87
|
.default([]),
|
|
@@ -90,11 +90,12 @@ export const MenuConfigSchema = Joi.object({
|
|
|
90
90
|
Joi.object({
|
|
91
91
|
label: Joi.string().required(),
|
|
92
92
|
linkTo: Joi.string().required(),
|
|
93
|
-
})
|
|
93
|
+
}),
|
|
94
94
|
)
|
|
95
95
|
.optional()
|
|
96
96
|
.default([]),
|
|
97
97
|
actionButtons: Joi.array().items(Joi.any()).optional().default([]),
|
|
98
|
+
topBar: Joi.any().optional(),
|
|
98
99
|
fullScreenLayout: Joi.string()
|
|
99
100
|
.valid("left", "right")
|
|
100
101
|
.optional()
|
|
@@ -139,7 +140,7 @@ export const TemplateConfigSchema = Joi.object({
|
|
|
139
140
|
Joi.object({
|
|
140
141
|
label: Joi.string().required(),
|
|
141
142
|
panel: Joi.any().required(),
|
|
142
|
-
})
|
|
143
|
+
}),
|
|
143
144
|
)
|
|
144
145
|
.optional()
|
|
145
146
|
.default([]),
|
|
@@ -2,6 +2,7 @@ import { HEADER, NAV } from "../config-layout";
|
|
|
2
2
|
|
|
3
3
|
import AccountPopover from "../common/account-popover";
|
|
4
4
|
import AppBar from "@mui/material/AppBar";
|
|
5
|
+
import Box from "@mui/material/Box";
|
|
5
6
|
import IconButton from "@mui/material/IconButton";
|
|
6
7
|
import Iconify from "../../components/Iconify";
|
|
7
8
|
import Logo from "../../components/logo";
|
|
@@ -23,6 +24,7 @@ export default function Header({ onOpenNav }) {
|
|
|
23
24
|
const settings = useSettingsContext();
|
|
24
25
|
|
|
25
26
|
const projectBar = config().template?.projectBar;
|
|
27
|
+
const TopBar = config().menu?.topBar;
|
|
26
28
|
|
|
27
29
|
const isNavHorizontal = settings.themeLayout === "horizontal";
|
|
28
30
|
|
|
@@ -44,15 +46,21 @@ export default function Header({ onOpenNav }) {
|
|
|
44
46
|
</IconButton>
|
|
45
47
|
)}
|
|
46
48
|
{projectBar && <ProjectBar />}
|
|
49
|
+
{TopBar && (
|
|
50
|
+
<Box sx={{ flexGrow: 1, mx: 2 }}>
|
|
51
|
+
<TopBar />
|
|
52
|
+
</Box>
|
|
53
|
+
)}
|
|
47
54
|
|
|
48
55
|
<Stack
|
|
49
56
|
direction="row"
|
|
50
57
|
spacing={{ xs: 0.5, sm: 1 }}
|
|
51
58
|
sx={{
|
|
52
|
-
flexGrow: 1,
|
|
59
|
+
...(!TopBar && { flexGrow: 1 }),
|
|
53
60
|
alignItems: "center",
|
|
54
|
-
justifyContent: "flex-end"
|
|
55
|
-
}}
|
|
61
|
+
justifyContent: "flex-end",
|
|
62
|
+
}}
|
|
63
|
+
>
|
|
56
64
|
<NotificationsPopover />
|
|
57
65
|
|
|
58
66
|
<SettingsButton />
|
|
@@ -67,7 +75,7 @@ export default function Header({ onOpenNav }) {
|
|
|
67
75
|
data-cy="dashboard-layout-header"
|
|
68
76
|
sx={{
|
|
69
77
|
height: HEADER.H_MOBILE,
|
|
70
|
-
zIndex:
|
|
78
|
+
zIndex: theme.zIndex.appBar,
|
|
71
79
|
...bgBlur({
|
|
72
80
|
color: theme.palette.background.default,
|
|
73
81
|
}),
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { Box, Divider } from "@mui/material";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
|
|
5
|
+
const DevTool = ({
|
|
6
|
+
width = 62,
|
|
7
|
+
height = "auto",
|
|
8
|
+
top = "50%",
|
|
9
|
+
open = true,
|
|
10
|
+
content,
|
|
11
|
+
header,
|
|
12
|
+
footer,
|
|
13
|
+
sx,
|
|
14
|
+
}) => {
|
|
15
|
+
if (!open) return null;
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<Box
|
|
19
|
+
component="aside"
|
|
20
|
+
sx={{
|
|
21
|
+
position: "fixed",
|
|
22
|
+
top,
|
|
23
|
+
right: 0,
|
|
24
|
+
transform: "translateY(-50%)",
|
|
25
|
+
width,
|
|
26
|
+
height,
|
|
27
|
+
bgcolor: (theme) =>
|
|
28
|
+
theme.palette.mode === "dark"
|
|
29
|
+
? "background.paper"
|
|
30
|
+
: "rgba(255, 255, 255, 0.85)",
|
|
31
|
+
backdropFilter: "blur(16px) saturate(180%)",
|
|
32
|
+
WebkitBackdropFilter: "blur(16px) saturate(180%)",
|
|
33
|
+
border: (theme) =>
|
|
34
|
+
`1px solid ${
|
|
35
|
+
theme.palette.mode === "dark"
|
|
36
|
+
? "rgba(255,255,255,0.07)"
|
|
37
|
+
: "rgba(0,0,0,0.07)"
|
|
38
|
+
}`,
|
|
39
|
+
borderRadius: "4px",
|
|
40
|
+
boxShadow: (theme) =>
|
|
41
|
+
theme.palette.mode === "dark"
|
|
42
|
+
? "0 8px 32px rgba(0,0,0,0.5), 0 0 0 1px rgba(255,255,255,0.04)"
|
|
43
|
+
: "0 8px 32px rgba(0,0,0,0.12), 0 0 0 1px rgba(0,0,0,0.04)",
|
|
44
|
+
zIndex: (theme) => theme.zIndex.modal + 3,
|
|
45
|
+
display: "flex",
|
|
46
|
+
flexDirection: "column",
|
|
47
|
+
alignItems: "center",
|
|
48
|
+
py: 1.5,
|
|
49
|
+
overflowY: "hidden",
|
|
50
|
+
overflowX: "hidden",
|
|
51
|
+
"&::-webkit-scrollbar": { display: "none" },
|
|
52
|
+
scrollbarWidth: "none",
|
|
53
|
+
...(sx || {}),
|
|
54
|
+
}}
|
|
55
|
+
>
|
|
56
|
+
{header && (
|
|
57
|
+
<Box
|
|
58
|
+
sx={{
|
|
59
|
+
width: "100%",
|
|
60
|
+
display: "flex",
|
|
61
|
+
flexDirection: "column",
|
|
62
|
+
alignItems: "center",
|
|
63
|
+
flexShrink: 0,
|
|
64
|
+
}}
|
|
65
|
+
>
|
|
66
|
+
{header}
|
|
67
|
+
</Box>
|
|
68
|
+
)}
|
|
69
|
+
|
|
70
|
+
<Box
|
|
71
|
+
sx={{
|
|
72
|
+
flex: 1,
|
|
73
|
+
width: "100%",
|
|
74
|
+
display: "flex",
|
|
75
|
+
flexDirection: "column",
|
|
76
|
+
alignItems: "center",
|
|
77
|
+
overflowY: "auto",
|
|
78
|
+
overflowX: "hidden",
|
|
79
|
+
"&::-webkit-scrollbar": { display: "none" },
|
|
80
|
+
scrollbarWidth: "none",
|
|
81
|
+
}}
|
|
82
|
+
>
|
|
83
|
+
{content}
|
|
84
|
+
</Box>
|
|
85
|
+
|
|
86
|
+
{footer && (
|
|
87
|
+
<>
|
|
88
|
+
<Divider
|
|
89
|
+
flexItem
|
|
90
|
+
sx={{
|
|
91
|
+
borderColor: (theme) =>
|
|
92
|
+
theme.palette.mode === "dark"
|
|
93
|
+
? "rgba(255,255,255,0.07)"
|
|
94
|
+
: "rgba(0,0,0,0.07)",
|
|
95
|
+
width: "100%",
|
|
96
|
+
flexShrink: 0,
|
|
97
|
+
}}
|
|
98
|
+
/>
|
|
99
|
+
<Box
|
|
100
|
+
sx={{
|
|
101
|
+
width: "100%",
|
|
102
|
+
display: "flex",
|
|
103
|
+
flexDirection: "column",
|
|
104
|
+
alignItems: "center",
|
|
105
|
+
flexShrink: 0,
|
|
106
|
+
pt: 0.5,
|
|
107
|
+
}}
|
|
108
|
+
>
|
|
109
|
+
{footer}
|
|
110
|
+
</Box>
|
|
111
|
+
</>
|
|
112
|
+
)}
|
|
113
|
+
</Box>
|
|
114
|
+
);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export default DevTool;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./DevTool";
|
|
@@ -8,7 +8,7 @@ const DraggableNode = ({
|
|
|
8
8
|
registerRef,
|
|
9
9
|
onDrag,
|
|
10
10
|
nodeId,
|
|
11
|
-
selectionColor = "#
|
|
11
|
+
selectionColor = "#373739",
|
|
12
12
|
initialPosition,
|
|
13
13
|
onConnect,
|
|
14
14
|
}) => {
|
|
@@ -164,20 +164,55 @@ const DraggableNode = ({
|
|
|
164
164
|
transform: `translate(${offset.x}px, ${offset.y}px)`,
|
|
165
165
|
cursor: "grab",
|
|
166
166
|
"&:active": { cursor: "grabbing" },
|
|
167
|
-
...(selected && {
|
|
168
|
-
"&::after": {
|
|
169
|
-
content: '""',
|
|
170
|
-
position: "absolute",
|
|
171
|
-
inset: -6,
|
|
172
|
-
border: `2px solid ${selectionColor}`,
|
|
173
|
-
borderRadius: "12px",
|
|
174
|
-
pointerEvents: "none",
|
|
175
|
-
boxShadow: `0 0 8px ${selectionColor}66`,
|
|
176
|
-
},
|
|
177
|
-
}),
|
|
178
167
|
}}
|
|
179
168
|
>
|
|
180
169
|
{children}
|
|
170
|
+
{selected &&
|
|
171
|
+
selectedIds.size > 1 &&
|
|
172
|
+
[
|
|
173
|
+
{
|
|
174
|
+
top: -10,
|
|
175
|
+
left: -6,
|
|
176
|
+
borderTop: 2,
|
|
177
|
+
borderLeft: 2,
|
|
178
|
+
color: selectionColor,
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
top: -8,
|
|
182
|
+
right: -12,
|
|
183
|
+
borderTop: 2,
|
|
184
|
+
borderRight: 2,
|
|
185
|
+
color: selectionColor,
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
bottom: -14,
|
|
189
|
+
left: -6,
|
|
190
|
+
borderBottom: 2,
|
|
191
|
+
borderLeft: 2,
|
|
192
|
+
color: selectionColor,
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
bottom: -16,
|
|
196
|
+
right: -12,
|
|
197
|
+
borderBottom: 2,
|
|
198
|
+
borderRight: 2,
|
|
199
|
+
color: selectionColor,
|
|
200
|
+
},
|
|
201
|
+
].map((pos, i) => (
|
|
202
|
+
<Box
|
|
203
|
+
key={i}
|
|
204
|
+
sx={{
|
|
205
|
+
position: "absolute",
|
|
206
|
+
width: 10,
|
|
207
|
+
height: 10,
|
|
208
|
+
borderStyle: "solid",
|
|
209
|
+
borderColor: selectionColor,
|
|
210
|
+
borderWidth: 0,
|
|
211
|
+
pointerEvents: "none",
|
|
212
|
+
...pos,
|
|
213
|
+
}}
|
|
214
|
+
/>
|
|
215
|
+
))}
|
|
181
216
|
</Box>
|
|
182
217
|
);
|
|
183
218
|
};
|
|
@@ -1,6 +1,49 @@
|
|
|
1
1
|
import { Box } from "@mui/material";
|
|
2
2
|
import { hexToRgba } from "../utils/flowUtils";
|
|
3
3
|
|
|
4
|
+
const HANDLE_SIZE = 10;
|
|
5
|
+
const HANDLE_THICKNESS = 2;
|
|
6
|
+
|
|
7
|
+
const cornerStyles = (placement) => {
|
|
8
|
+
const base = {
|
|
9
|
+
position: "absolute",
|
|
10
|
+
width: HANDLE_SIZE,
|
|
11
|
+
height: HANDLE_SIZE,
|
|
12
|
+
borderColor: "inherit",
|
|
13
|
+
borderStyle: "solid",
|
|
14
|
+
borderWidth: 0,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const map = {
|
|
18
|
+
topLeft: {
|
|
19
|
+
top: 0,
|
|
20
|
+
left: 0,
|
|
21
|
+
borderTopWidth: HANDLE_THICKNESS,
|
|
22
|
+
borderLeftWidth: HANDLE_THICKNESS,
|
|
23
|
+
},
|
|
24
|
+
topRight: {
|
|
25
|
+
top: 0,
|
|
26
|
+
right: 0,
|
|
27
|
+
borderTopWidth: HANDLE_THICKNESS,
|
|
28
|
+
borderRightWidth: HANDLE_THICKNESS,
|
|
29
|
+
},
|
|
30
|
+
bottomLeft: {
|
|
31
|
+
bottom: 0,
|
|
32
|
+
left: 0,
|
|
33
|
+
borderBottomWidth: HANDLE_THICKNESS,
|
|
34
|
+
borderLeftWidth: HANDLE_THICKNESS,
|
|
35
|
+
},
|
|
36
|
+
bottomRight: {
|
|
37
|
+
bottom: 0,
|
|
38
|
+
right: 0,
|
|
39
|
+
borderBottomWidth: HANDLE_THICKNESS,
|
|
40
|
+
borderRightWidth: HANDLE_THICKNESS,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
return { ...base, ...map[placement] };
|
|
45
|
+
};
|
|
46
|
+
|
|
4
47
|
const SelectionOverlay = ({ box, selectionColor = "#64748b" }) => {
|
|
5
48
|
if (!box) return null;
|
|
6
49
|
|
|
@@ -18,13 +61,17 @@ const SelectionOverlay = ({ box, selectionColor = "#64748b" }) => {
|
|
|
18
61
|
top,
|
|
19
62
|
width,
|
|
20
63
|
height,
|
|
21
|
-
|
|
22
|
-
backgroundColor: hexToRgba(selectionColor, 0.1),
|
|
64
|
+
backgroundColor: hexToRgba(selectionColor, 0.08),
|
|
23
65
|
pointerEvents: "none",
|
|
24
66
|
zIndex: 9999,
|
|
25
67
|
borderRadius: "4px",
|
|
68
|
+
color: selectionColor,
|
|
26
69
|
}}
|
|
27
|
-
|
|
70
|
+
>
|
|
71
|
+
{["topLeft", "topRight", "bottomLeft", "bottomRight"].map((placement) => (
|
|
72
|
+
<Box key={placement} sx={cornerStyles(placement)} />
|
|
73
|
+
))}
|
|
74
|
+
</Box>
|
|
28
75
|
);
|
|
29
76
|
};
|
|
30
77
|
|
package/src/lib/index.js
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import config from "../../config/config";
|
|
2
|
-
import { publish } from "@nucleoidai/react-event";
|
|
3
|
-
import { storage } from "@nucleoidjs/webstorage";
|
|
4
|
-
import { useNavigate } from "react-router-dom";
|
|
5
|
-
import { useState } from "react";
|
|
6
|
-
|
|
7
1
|
import {
|
|
8
2
|
Box,
|
|
9
3
|
Button,
|
|
@@ -20,9 +14,22 @@ import {
|
|
|
20
14
|
Visibility,
|
|
21
15
|
VisibilityOff,
|
|
22
16
|
} from "@mui/icons-material";
|
|
23
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
confirmForgotPassword,
|
|
19
|
+
confirmSignup,
|
|
20
|
+
forgotPassword,
|
|
21
|
+
getTokens,
|
|
22
|
+
login,
|
|
23
|
+
signup,
|
|
24
|
+
} from "./amplifyAuth";
|
|
24
25
|
import { inputSx, primaryButtonSx } from "./styles";
|
|
25
26
|
|
|
27
|
+
import config from "../../config/config";
|
|
28
|
+
import { publish } from "@nucleoidai/react-event";
|
|
29
|
+
import { storage } from "@nucleoidjs/webstorage";
|
|
30
|
+
import { useNavigate } from "react-router-dom";
|
|
31
|
+
import { useState } from "react";
|
|
32
|
+
|
|
26
33
|
export default function CognitoLogin() {
|
|
27
34
|
const [mode, setMode] = useState("login");
|
|
28
35
|
|
|
@@ -96,6 +103,42 @@ export default function CognitoLogin() {
|
|
|
96
103
|
}
|
|
97
104
|
};
|
|
98
105
|
|
|
106
|
+
const handleForgotPassword = async () => {
|
|
107
|
+
try {
|
|
108
|
+
await forgotPassword(email);
|
|
109
|
+
publish("GLOBAL_MESSAGE_POSTED", {
|
|
110
|
+
status: true,
|
|
111
|
+
message: "Reset code sent! Check your email.",
|
|
112
|
+
severity: "success",
|
|
113
|
+
});
|
|
114
|
+
setMode("resetPassword");
|
|
115
|
+
} catch (e) {
|
|
116
|
+
publish("GLOBAL_MESSAGE_POSTED", {
|
|
117
|
+
status: true,
|
|
118
|
+
message: e.message || "Failed to send reset code",
|
|
119
|
+
severity: "error",
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const handleResetPassword = async () => {
|
|
125
|
+
try {
|
|
126
|
+
await confirmForgotPassword(email, code, password);
|
|
127
|
+
publish("GLOBAL_MESSAGE_POSTED", {
|
|
128
|
+
status: true,
|
|
129
|
+
message: "Password reset successful! You can now sign in.",
|
|
130
|
+
severity: "success",
|
|
131
|
+
});
|
|
132
|
+
setMode("login");
|
|
133
|
+
} catch (e) {
|
|
134
|
+
publish("GLOBAL_MESSAGE_POSTED", {
|
|
135
|
+
status: true,
|
|
136
|
+
message: e.message || "Password reset failed",
|
|
137
|
+
severity: "error",
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
99
142
|
const handleConfirm = async () => {
|
|
100
143
|
try {
|
|
101
144
|
await confirmSignup(email, code);
|
|
@@ -124,6 +167,14 @@ export default function CognitoLogin() {
|
|
|
124
167
|
heading: "Verify your email",
|
|
125
168
|
sub: "Enter the confirmation code sent to your inbox.",
|
|
126
169
|
},
|
|
170
|
+
forgotPassword: {
|
|
171
|
+
heading: "Reset password",
|
|
172
|
+
sub: "Enter your email to receive a reset code.",
|
|
173
|
+
},
|
|
174
|
+
resetPassword: {
|
|
175
|
+
heading: "Set new password",
|
|
176
|
+
sub: "Enter the code from your email and your new password.",
|
|
177
|
+
},
|
|
127
178
|
};
|
|
128
179
|
|
|
129
180
|
const passwordAdornment = (
|
|
@@ -155,7 +206,7 @@ export default function CognitoLogin() {
|
|
|
155
206
|
color: "primary.main",
|
|
156
207
|
}}
|
|
157
208
|
>
|
|
158
|
-
{mode === "confirm" ? (
|
|
209
|
+
{mode === "confirm" || mode === "resetPassword" ? (
|
|
159
210
|
<MarkEmailReadOutlined sx={{ fontSize: 22 }} />
|
|
160
211
|
) : (
|
|
161
212
|
<LockOutlined sx={{ fontSize: 22 }} />
|
|
@@ -181,11 +232,11 @@ export default function CognitoLogin() {
|
|
|
181
232
|
slotProps={{ input: { disableUnderline: true } }}
|
|
182
233
|
/>
|
|
183
234
|
|
|
184
|
-
{mode !== "confirm" && (
|
|
235
|
+
{mode !== "confirm" && mode !== "forgotPassword" && (
|
|
185
236
|
<TextField
|
|
186
237
|
variant="filled"
|
|
187
238
|
type={showPassword ? "text" : "password"}
|
|
188
|
-
label="Password"
|
|
239
|
+
label={mode === "resetPassword" ? "New Password" : "Password"}
|
|
189
240
|
value={password}
|
|
190
241
|
onChange={(e) => setPassword(e.target.value)}
|
|
191
242
|
fullWidth
|
|
@@ -230,7 +281,7 @@ export default function CognitoLogin() {
|
|
|
230
281
|
/>
|
|
231
282
|
)}
|
|
232
283
|
|
|
233
|
-
{mode === "confirm" && (
|
|
284
|
+
{(mode === "confirm" || mode === "resetPassword") && (
|
|
234
285
|
<TextField
|
|
235
286
|
variant="filled"
|
|
236
287
|
label="Confirmation Code"
|
|
@@ -254,6 +305,17 @@ export default function CognitoLogin() {
|
|
|
254
305
|
>
|
|
255
306
|
Sign in →
|
|
256
307
|
</Button>
|
|
308
|
+
<Button
|
|
309
|
+
onClick={() => setMode("forgotPassword")}
|
|
310
|
+
fullWidth
|
|
311
|
+
sx={{
|
|
312
|
+
textTransform: "none",
|
|
313
|
+
fontWeight: 600,
|
|
314
|
+
color: "text.secondary",
|
|
315
|
+
}}
|
|
316
|
+
>
|
|
317
|
+
Forgot password?
|
|
318
|
+
</Button>
|
|
257
319
|
<Button
|
|
258
320
|
onClick={() => setMode("signup")}
|
|
259
321
|
fullWidth
|
|
@@ -304,6 +366,56 @@ export default function CognitoLogin() {
|
|
|
304
366
|
Verify →
|
|
305
367
|
</Button>
|
|
306
368
|
)}
|
|
369
|
+
{mode === "forgotPassword" && (
|
|
370
|
+
<Stack spacing={1.5}>
|
|
371
|
+
<Button
|
|
372
|
+
variant="contained"
|
|
373
|
+
onClick={handleForgotPassword}
|
|
374
|
+
size="large"
|
|
375
|
+
fullWidth
|
|
376
|
+
disableElevation
|
|
377
|
+
sx={primaryButtonSx}
|
|
378
|
+
>
|
|
379
|
+
Send reset code →
|
|
380
|
+
</Button>
|
|
381
|
+
<Button
|
|
382
|
+
onClick={() => setMode("login")}
|
|
383
|
+
fullWidth
|
|
384
|
+
sx={{
|
|
385
|
+
textTransform: "none",
|
|
386
|
+
fontWeight: 600,
|
|
387
|
+
color: "text.secondary",
|
|
388
|
+
}}
|
|
389
|
+
>
|
|
390
|
+
← Back to sign in
|
|
391
|
+
</Button>
|
|
392
|
+
</Stack>
|
|
393
|
+
)}
|
|
394
|
+
{mode === "resetPassword" && (
|
|
395
|
+
<Stack spacing={1.5}>
|
|
396
|
+
<Button
|
|
397
|
+
variant="contained"
|
|
398
|
+
onClick={handleResetPassword}
|
|
399
|
+
size="large"
|
|
400
|
+
fullWidth
|
|
401
|
+
disableElevation
|
|
402
|
+
sx={primaryButtonSx}
|
|
403
|
+
>
|
|
404
|
+
Reset password →
|
|
405
|
+
</Button>
|
|
406
|
+
<Button
|
|
407
|
+
onClick={() => setMode("login")}
|
|
408
|
+
fullWidth
|
|
409
|
+
sx={{
|
|
410
|
+
textTransform: "none",
|
|
411
|
+
fontWeight: 600,
|
|
412
|
+
color: "text.secondary",
|
|
413
|
+
}}
|
|
414
|
+
>
|
|
415
|
+
← Back to sign in
|
|
416
|
+
</Button>
|
|
417
|
+
</Stack>
|
|
418
|
+
)}
|
|
307
419
|
</Stack>
|
|
308
420
|
);
|
|
309
421
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
+
confirmResetPassword,
|
|
2
3
|
confirmSignUp,
|
|
3
4
|
fetchAuthSession,
|
|
5
|
+
resetPassword,
|
|
4
6
|
signIn,
|
|
5
7
|
signOut,
|
|
6
8
|
signUp,
|
|
@@ -29,6 +31,18 @@ export async function confirmSignup(email, code) {
|
|
|
29
31
|
});
|
|
30
32
|
}
|
|
31
33
|
|
|
34
|
+
export async function forgotPassword(email) {
|
|
35
|
+
return resetPassword({ username: email });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function confirmForgotPassword(email, code, newPassword) {
|
|
39
|
+
return confirmResetPassword({
|
|
40
|
+
username: email,
|
|
41
|
+
confirmationCode: code,
|
|
42
|
+
newPassword,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
32
46
|
export async function logout() {
|
|
33
47
|
await signOut();
|
|
34
48
|
}
|
package/vite/vite.js
CHANGED
|
@@ -15,6 +15,7 @@ if (error) {
|
|
|
15
15
|
|
|
16
16
|
async function vite() {
|
|
17
17
|
const base = value.base;
|
|
18
|
+
const api = value.api;
|
|
18
19
|
|
|
19
20
|
return {
|
|
20
21
|
plugins: [
|
|
@@ -31,6 +32,17 @@ async function vite() {
|
|
|
31
32
|
},
|
|
32
33
|
}),
|
|
33
34
|
],
|
|
35
|
+
server: {
|
|
36
|
+
port: 3000,
|
|
37
|
+
proxy: {
|
|
38
|
+
"/api": {
|
|
39
|
+
target: api?.split("/api")?.[0],
|
|
40
|
+
rewrite: (path) => path.replace(/^\/api/, ""),
|
|
41
|
+
changeOrigin: true,
|
|
42
|
+
timeout: 120_000,
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
34
46
|
base,
|
|
35
47
|
optimizeDeps: {
|
|
36
48
|
esbuildOptions: {
|
|
@@ -55,7 +67,7 @@ async function vite() {
|
|
|
55
67
|
find: /^src(.+)/,
|
|
56
68
|
replacement: path.join(
|
|
57
69
|
process.cwd(),
|
|
58
|
-
"/node_modules/@nucleoidai/platform/minimal/src/$1"
|
|
70
|
+
"/node_modules/@nucleoidai/platform/minimal/src/$1",
|
|
59
71
|
),
|
|
60
72
|
},
|
|
61
73
|
],
|