Skip to content

Commit 6d05153

Browse files
committed
Ambari-Web React Implementation: Authentication Components
1 parent 982c545 commit 6d05153

File tree

17 files changed

+4004
-171
lines changed

17 files changed

+4004
-171
lines changed

ambari-admin/src/main/resources/ui/ambari-admin/package-lock.json

Lines changed: 2706 additions & 163 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ambari-admin/src/main/resources/ui/ambari-admin/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"@fortawesome/react-fontawesome": "^0.2.2",
1717
"@tanstack/react-table": "^8.20.5",
1818
"@types/lodash": "^4.17.12",
19+
"@types/recharts": "^1.8.29",
1920
"axios": "^1.7.7",
2021
"bootstrap": "^5.3.3",
2122
"history": "^5.3.0",
@@ -27,6 +28,7 @@
2728
"react-hot-toast": "^2.4.1",
2829
"react-router-dom": "^5.3.4",
2930
"react-select": "^5.8.3",
31+
"recharts": "^2.15.1",
3032
"sass": "^1.77.6"
3133
},
3234
"devDependencies": {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
.loading-spinner-container {
20+
display: flex;
21+
justify-content: center;
22+
align-items: center;
23+
height: 100vh;
24+
width: 100%;
25+
26+
.loading-spinner-content {
27+
text-align: center;
28+
29+
.spinner-border {
30+
width: 3rem;
31+
height: 3rem;
32+
}
33+
34+
p {
35+
font-size: 1.2rem;
36+
color: #666;
37+
}
38+
}
39+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
import React from 'react';
20+
import { Spinner, Container } from 'react-bootstrap';
21+
import './LoadingSpinner.scss';
22+
23+
interface LoadingSpinnerProps {
24+
message?: string;
25+
}
26+
27+
const LoadingSpinner: React.FC<LoadingSpinnerProps> = ({ message = 'Loading...' }) => {
28+
return (
29+
<Container className="loading-spinner-container">
30+
<div className="loading-spinner-content">
31+
<Spinner animation="border" role="status" variant="primary" />
32+
<p className="mt-3">{message}</p>
33+
</div>
34+
</Container>
35+
);
36+
};
37+
38+
export default LoadingSpinner;
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
.login-form {
20+
width: 100%;
21+
22+
h2 {
23+
margin-bottom: 20px;
24+
font-size: 1.5rem;
25+
font-weight: normal;
26+
}
27+
28+
.form-label {
29+
font-weight: normal;
30+
margin-bottom: 5px;
31+
}
32+
33+
.form-control {
34+
margin-bottom: 5px;
35+
border-radius: 0;
36+
37+
&:focus {
38+
box-shadow: none;
39+
border-color: #ccc;
40+
}
41+
42+
&.is-invalid {
43+
border-color: #dc3545;
44+
background-image: none;
45+
padding-right: 0.75rem;
46+
}
47+
}
48+
49+
.invalid-feedback {
50+
margin-bottom: 10px;
51+
font-size: 0.8rem;
52+
}
53+
54+
.sign-in-button {
55+
background-color: #5cb85c;
56+
border-color: #5cb85c;
57+
border-radius: 0;
58+
width: 100px;
59+
text-transform: uppercase;
60+
font-size: 0.8rem;
61+
font-weight: bold;
62+
margin-top: 10px;
63+
64+
&:hover, &:focus {
65+
background-color: #4cae4c;
66+
border-color: #4cae4c;
67+
}
68+
69+
&:disabled {
70+
background-color: #8bc58b;
71+
border-color: #8bc58b;
72+
}
73+
}
74+
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
import React, { useState, useRef, useEffect } from 'react';
20+
import { Form, Button, Alert } from 'react-bootstrap';
21+
import './LoginForm.scss';
22+
23+
interface LoginFormProps {
24+
onSubmit: (username: string, password: string) => Promise<void>;
25+
isSubmitting: boolean;
26+
errorMessage?: string;
27+
}
28+
29+
interface ValidationErrors {
30+
username?: string;
31+
password?: string;
32+
}
33+
34+
const LoginForm: React.FC<LoginFormProps> = ({
35+
onSubmit,
36+
isSubmitting,
37+
errorMessage
38+
}) => {
39+
const [username, setUsername] = useState('');
40+
const [password, setPassword] = useState('');
41+
const [validationErrors, setValidationErrors] = useState<ValidationErrors>({});
42+
const [touched, setTouched] = useState({ username: false, password: false });
43+
const usernameInputRef = useRef<HTMLInputElement>(null);
44+
45+
useEffect(() => {
46+
// Focus the username input when the component mounts
47+
if (usernameInputRef.current) {
48+
usernameInputRef.current.focus();
49+
}
50+
}, []);
51+
52+
const validateForm = (): boolean => {
53+
const errors: ValidationErrors = {};
54+
55+
if (!username.trim()) {
56+
errors.username = 'Username is required';
57+
}
58+
59+
if (!password) {
60+
errors.password = 'Password is required';
61+
} else if (password.length < 4) {
62+
errors.password = 'Password must be at least 4 characters';
63+
}
64+
65+
setValidationErrors(errors);
66+
return Object.keys(errors).length === 0;
67+
};
68+
69+
const handleSubmit = async (e: React.FormEvent) => {
70+
e.preventDefault();
71+
72+
// Mark all fields as touched to show validation errors
73+
setTouched({ username: true, password: true });
74+
75+
if (validateForm()) {
76+
await onSubmit(username, password);
77+
}
78+
};
79+
80+
const handleBlur = (field: 'username' | 'password') => {
81+
setTouched({ ...touched, [field]: true });
82+
validateForm();
83+
};
84+
85+
return (
86+
<div className="login-form">
87+
<h2>Sign in</h2>
88+
89+
{errorMessage && (
90+
<Alert variant="danger" data-qa="login-error">
91+
{errorMessage}
92+
</Alert>
93+
)}
94+
95+
<Form onSubmit={handleSubmit} noValidate>
96+
<Form.Group className="mb-3">
97+
<Form.Label data-qa="username-label">Username</Form.Label>
98+
<Form.Control
99+
type="text"
100+
value={username}
101+
onChange={(e) => setUsername(e.target.value)}
102+
onBlur={() => handleBlur('username')}
103+
isInvalid={touched.username && !!validationErrors.username}
104+
disabled={isSubmitting}
105+
ref={usernameInputRef}
106+
data-qa="username-input"
107+
className="login-username"
108+
/>
109+
<Form.Control.Feedback type="invalid">
110+
{validationErrors.username}
111+
</Form.Control.Feedback>
112+
</Form.Group>
113+
114+
<Form.Group className="mb-3">
115+
<Form.Label data-qa="password-label">Password</Form.Label>
116+
<Form.Control
117+
type="password"
118+
value={password}
119+
onChange={(e) => setPassword(e.target.value)}
120+
onBlur={() => handleBlur('password')}
121+
isInvalid={touched.password && !!validationErrors.password}
122+
disabled={isSubmitting}
123+
data-qa="password-input"
124+
className="login-password"
125+
autoComplete="new-password"
126+
onKeyDown={(e) => {
127+
if (e.key === 'Enter') {
128+
handleSubmit(e);
129+
}
130+
}}
131+
/>
132+
<Form.Control.Feedback type="invalid">
133+
{validationErrors.password}
134+
</Form.Control.Feedback>
135+
</Form.Group>
136+
137+
<Button
138+
variant="success"
139+
type="submit"
140+
disabled={isSubmitting}
141+
data-qa="login-button"
142+
className="sign-in-button"
143+
>
144+
{isSubmitting ? 'SIGNING IN...' : 'SIGN IN'}
145+
</Button>
146+
</Form>
147+
</div>
148+
);
149+
};
150+
151+
export default LoginForm;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
.permission-alert {
20+
margin: 1rem;
21+
border-radius: 0;
22+
font-size: 0.9rem;
23+
24+
&.alert-warning {
25+
background-color: #fff3cd;
26+
border-color: #ffecb5;
27+
color: #856404;
28+
}
29+
}

0 commit comments

Comments
 (0)