@canmingir/link 1.2.11 → 1.2.13
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/AddItemWizard/AddItemWizard.jsx +3 -3
- package/src/lib/CustomBreadcrumbs/CustomBreadcrumbs.jsx +1 -1
- package/src/lib/CustomPopover/usePopover.js +1 -1
- package/src/lib/FormProvider/FormProvider.jsx +1 -1
- package/src/lib/ItemSummary/ItemSummary.jsx +1 -0
- package/src/lib/ItemSummary/ItemsSummary.jsx +1 -0
- package/src/lib/RHFTextfield/RHFTextfield.jsx +3 -2
- package/src/lib/SearchNotFound/SearchNotFound.jsx +1 -1
- package/src/lib/SvgColor/SvgColor.jsx +2 -1
- package/src/lib/TableHeadCustom/TableHeadCustom.jsx +1 -1
- package/src/lib/TableSelectedAction/TableSelectedAction.jsx +1 -1
- package/src/lib/useChart/useChart.js +1 -0
- package/src/lib/useTable/useTable.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canmingir/link",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.13",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./index.js",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"cypress": "cypress open --browser chrome"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
+
"@aws-sdk/client-cognito-identity-provider": "^3.968.0",
|
|
24
25
|
"@chatscope/chat-ui-kit-react": "^1.10.1",
|
|
25
26
|
"@emoji-mart/data": "^1.1.2",
|
|
26
27
|
"@emoji-mart/react": "^1.1.1",
|
|
@@ -101,7 +102,7 @@
|
|
|
101
102
|
"playwright": "^1.55.1",
|
|
102
103
|
"prettier": "^2.8.8",
|
|
103
104
|
"storybook": "^9.1.8",
|
|
104
|
-
"vite": "^7.1
|
|
105
|
+
"vite": "^7.3.1",
|
|
105
106
|
"vite-plugin-checker": "^0.9.0",
|
|
106
107
|
"vite-plugin-svgr": "^4.2.0",
|
|
107
108
|
"vitest": "^3.2.4"
|
package/src/config/schemas.js
CHANGED
|
@@ -10,6 +10,12 @@ export const ConfigSchema = Joi.object({
|
|
|
10
10
|
host: Joi.string().uri().required(),
|
|
11
11
|
path: Joi.string().required(),
|
|
12
12
|
}).optional(),
|
|
13
|
+
credentials: Joi.object({
|
|
14
|
+
provider: Joi.string().valid("DEMO", "COGNITO").required(),
|
|
15
|
+
region: Joi.string().optional(),
|
|
16
|
+
userPoolId: Joi.string().optional(),
|
|
17
|
+
clientId: Joi.string().optional(),
|
|
18
|
+
}).optional(),
|
|
13
19
|
project: Joi.object({
|
|
14
20
|
nucleoid: Joi.object().optional(),
|
|
15
21
|
github: Joi.object({
|
package/src/http/index.js
CHANGED
|
@@ -86,27 +86,38 @@ export const fetcher = (url) => instance.get(url).then((res) => res.data);
|
|
|
86
86
|
const refreshAuthLogic = async (failedRequest) => {
|
|
87
87
|
try {
|
|
88
88
|
const { appId } = config();
|
|
89
|
-
|
|
90
89
|
const projectId = storage.get("projectId");
|
|
91
|
-
|
|
92
90
|
const identityProvider = storage.get("link", "identityProvider");
|
|
93
91
|
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
92
|
+
const isDemo = identityProvider?.toUpperCase() === "DEMO";
|
|
93
|
+
|
|
94
|
+
const { data } = isDemo
|
|
95
|
+
? await oauth.post("/oauth/demo", {
|
|
96
|
+
appId,
|
|
97
|
+
projectId,
|
|
98
|
+
username: "admin",
|
|
99
|
+
password: "admin",
|
|
100
|
+
})
|
|
101
|
+
: await oauth.post("/oauth", {
|
|
102
|
+
refreshToken: storage.get("link", "refreshToken"),
|
|
103
|
+
appId,
|
|
104
|
+
projectId,
|
|
105
|
+
identityProvider,
|
|
106
|
+
});
|
|
100
107
|
|
|
101
|
-
const accessToken = data
|
|
108
|
+
const { accessToken, refreshToken } = data;
|
|
102
109
|
|
|
103
110
|
failedRequest.response.config.headers["Authorization"] =
|
|
104
111
|
"Bearer " + accessToken;
|
|
105
112
|
|
|
106
113
|
storage.set("link", "accessToken", accessToken);
|
|
114
|
+
if (refreshToken) {
|
|
115
|
+
storage.set("link", "refreshToken", refreshToken);
|
|
116
|
+
}
|
|
117
|
+
|
|
107
118
|
return Promise.resolve();
|
|
108
119
|
} catch (error) {
|
|
109
|
-
const {
|
|
120
|
+
const { base } = config();
|
|
110
121
|
|
|
111
122
|
storage.remove("link", "accessToken");
|
|
112
123
|
storage.remove("link", "refreshToken");
|
|
@@ -4,6 +4,7 @@ import Logo from "../../components/logo";
|
|
|
4
4
|
import { Outlet } from "react-router";
|
|
5
5
|
import React from "react";
|
|
6
6
|
import Stack from "@mui/material/Stack";
|
|
7
|
+
import { alpha } from "@mui/material/styles";
|
|
7
8
|
import { useResponsive } from "../../hooks/use-responsive";
|
|
8
9
|
|
|
9
10
|
// ----------------------------------------------------------------------
|
|
@@ -16,28 +17,37 @@ export default function AuthModernLayout({ image }) {
|
|
|
16
17
|
sx={{
|
|
17
18
|
width: 1,
|
|
18
19
|
mx: "auto",
|
|
19
|
-
maxWidth:
|
|
20
|
-
px: { xs:
|
|
20
|
+
maxWidth: 600,
|
|
21
|
+
px: { xs: 3, md: 10 },
|
|
22
|
+
py: { xs: 4, md: 0 },
|
|
21
23
|
height: "100vh",
|
|
22
24
|
justifyContent: "center",
|
|
23
25
|
alignItems: "center",
|
|
24
26
|
}}
|
|
25
27
|
>
|
|
26
28
|
<Logo
|
|
27
|
-
maxSize={
|
|
29
|
+
maxSize={140}
|
|
28
30
|
sx={{
|
|
29
|
-
|
|
30
|
-
mb: { xs: 10, md: 8 },
|
|
31
|
+
mb: { xs: 1, md: 2 },
|
|
31
32
|
}}
|
|
32
33
|
/>
|
|
33
34
|
|
|
34
35
|
<Card
|
|
35
36
|
sx={{
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
width: 1,
|
|
38
|
+
py: { xs: 6, md: 8 },
|
|
39
|
+
px: { xs: 4, md: 6 },
|
|
40
|
+
boxShadow: {
|
|
41
|
+
xs: (theme) =>
|
|
42
|
+
`0 0 2px ${alpha(
|
|
43
|
+
theme.palette.grey[500],
|
|
44
|
+
0.16
|
|
45
|
+
)}, 0 12px 24px -4px ${alpha(theme.palette.grey[500], 0.12)}`,
|
|
46
|
+
md: "none",
|
|
47
|
+
},
|
|
39
48
|
overflow: { md: "unset" },
|
|
40
|
-
bgcolor: { md: "
|
|
49
|
+
bgcolor: { md: "transparent" },
|
|
50
|
+
borderRadius: 2,
|
|
41
51
|
}}
|
|
42
52
|
>
|
|
43
53
|
<Outlet />
|
|
@@ -46,7 +56,12 @@ export default function AuthModernLayout({ image }) {
|
|
|
46
56
|
);
|
|
47
57
|
|
|
48
58
|
const renderSection = (
|
|
49
|
-
<Stack
|
|
59
|
+
<Stack
|
|
60
|
+
flexGrow={1}
|
|
61
|
+
sx={{
|
|
62
|
+
position: "relative",
|
|
63
|
+
}}
|
|
64
|
+
>
|
|
50
65
|
<Box
|
|
51
66
|
component="img"
|
|
52
67
|
alt="auth"
|
|
@@ -59,6 +74,7 @@ export default function AuthModernLayout({ image }) {
|
|
|
59
74
|
position: "absolute",
|
|
60
75
|
width: "calc(60% - 32px)",
|
|
61
76
|
height: "calc(60% - 32px)",
|
|
77
|
+
borderRadius: 3,
|
|
62
78
|
}}
|
|
63
79
|
/>
|
|
64
80
|
</Stack>
|
|
@@ -94,8 +94,23 @@ function ProjectBar() {
|
|
|
94
94
|
const refreshToken = storage.get("link", "refreshToken");
|
|
95
95
|
const identityProvider = storage.get("link", "identityProvider");
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
const isDemo = identityProvider?.toUpperCase() === "DEMO";
|
|
98
|
+
|
|
99
|
+
const request = isDemo
|
|
100
|
+
? oauth.post("/oauth/demo", {
|
|
101
|
+
appId,
|
|
102
|
+
projectId,
|
|
103
|
+
username: "admin",
|
|
104
|
+
password: "admin",
|
|
105
|
+
})
|
|
106
|
+
: oauth.post("/oauth", {
|
|
107
|
+
appId,
|
|
108
|
+
refreshToken,
|
|
109
|
+
projectId,
|
|
110
|
+
identityProvider,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
request
|
|
99
114
|
.then(({ data }) => {
|
|
100
115
|
const { refreshToken, accessToken } = data;
|
|
101
116
|
storage.set("link", "accessToken", accessToken);
|
|
@@ -104,9 +119,7 @@ function ProjectBar() {
|
|
|
104
119
|
})
|
|
105
120
|
.finally(() => {
|
|
106
121
|
setSelectedProject(project);
|
|
107
|
-
|
|
108
122
|
publish("PROJECT_SELECTED", { projectId });
|
|
109
|
-
|
|
110
123
|
search.onFalse();
|
|
111
124
|
setSearchQuery("");
|
|
112
125
|
});
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { Grid, Stack, Switch, Typography } from "@mui/material";
|
|
2
|
+
import { publish, useEvent } from "@nucleoidai/react-event";
|
|
3
|
+
|
|
1
4
|
import Dialog from "@mui/material/Dialog";
|
|
2
5
|
import DialogContent from "@mui/material/DialogContent";
|
|
3
6
|
import DialogTitle from "@mui/material/DialogTitle";
|
|
@@ -7,9 +10,6 @@ import SelectAvatar from "../IconSelector/IconSelector";
|
|
|
7
10
|
import SparkleInput from "../SparkleInput/SparkleInput";
|
|
8
11
|
import StepComponent from "../StepComponent/StepComponent";
|
|
9
12
|
|
|
10
|
-
import { Grid, Stack, Switch, Typography } from "@mui/material";
|
|
11
|
-
import { publish, useEvent } from "@nucleoidai/react-event";
|
|
12
|
-
|
|
13
13
|
function AddItemWizard({ onSubmit, items, steps, stepExp }) {
|
|
14
14
|
const [activeStep, setActiveStep] = React.useState(0);
|
|
15
15
|
const [newItems, setNewItems] = React.useState(items);
|
|
@@ -3,9 +3,9 @@ import Breadcrumbs from "@mui/material/Breadcrumbs";
|
|
|
3
3
|
import Link from "@mui/material/Link";
|
|
4
4
|
import LinkItem from "./link-item";
|
|
5
5
|
import PropTypes from "prop-types";
|
|
6
|
+
import React from "react";
|
|
6
7
|
import Stack from "@mui/material/Stack";
|
|
7
8
|
import Typography from "@mui/material/Typography";
|
|
8
|
-
|
|
9
9
|
export default function CustomBreadcrumbs({
|
|
10
10
|
links,
|
|
11
11
|
action,
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { Controller, useFormContext } from "react-hook-form";
|
|
2
|
-
|
|
3
1
|
import PropTypes from "prop-types";
|
|
2
|
+
import React from "react";
|
|
4
3
|
import TextField from "@mui/material/TextField";
|
|
5
4
|
|
|
5
|
+
import { Controller, useFormContext } from "react-hook-form";
|
|
6
|
+
|
|
6
7
|
export default function RHFTextField({ name, helperText, type, ...other }) {
|
|
7
8
|
const { control } = useFormContext();
|
|
8
9
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import Box from "@mui/material/Box";
|
|
2
2
|
import Checkbox from "@mui/material/Checkbox";
|
|
3
3
|
import PropTypes from "prop-types";
|
|
4
|
+
import React from "react";
|
|
4
5
|
import TableCell from "@mui/material/TableCell";
|
|
5
6
|
import TableHead from "@mui/material/TableHead";
|
|
6
7
|
import TableRow from "@mui/material/TableRow";
|
|
7
8
|
import TableSortLabel from "@mui/material/TableSortLabel";
|
|
8
|
-
|
|
9
9
|
const visuallyHidden = {
|
|
10
10
|
border: 0,
|
|
11
11
|
margin: -1,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import Checkbox from "@mui/material/Checkbox";
|
|
2
2
|
import PropTypes from "prop-types";
|
|
3
|
+
import React from "react";
|
|
3
4
|
import Stack from "@mui/material/Stack";
|
|
4
5
|
import Typography from "@mui/material/Typography";
|
|
5
|
-
|
|
6
6
|
export default function TableSelectedAction({
|
|
7
7
|
dense,
|
|
8
8
|
action,
|
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
|
+
}
|