Skip to content

Commit 63d1fb2

Browse files
authored
Added UI for Domains edit (#1897)
Signed-off-by: Benjamin Perez <[email protected]>
1 parent b3afa34 commit 63d1fb2

File tree

4 files changed

+406
-52
lines changed

4 files changed

+406
-52
lines changed

portal-ui/src/icons/PasswordKeyIcon.tsx

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,8 @@ const PasswordKeyIcon = (props: SVGProps<SVGSVGElement>) => {
2626
{...props}
2727
>
2828
<path
29-
id="Trazado_7179"
30-
data-name="Trazado 7179"
3129
d="M141.421,148.182a4.5,4.5,0,0,0-4.3,5.805l-5.188,5.195v3h3l5.187-5.2a4.5,4.5,0,0,0,5.8-3.936,4.39,4.39,0,0,0-.823-3A4.492,4.492,0,0,0,141.421,148.182Zm.5,5a1,1,0,1,1,1-1A1,1,0,0,1,141.92,153.182Z"
3230
transform="translate(-131.934 -148.182)"
33-
//fill="#5e5e5e"
34-
/>
35-
<rect
36-
id="Rectángulo_1090"
37-
data-name="Rectángulo 1090"
38-
width="13.764"
39-
height="13.764"
40-
transform="translate(0.118 0.118)"
41-
fill="none"
4231
/>
4332
</svg>
4433
);

portal-ui/src/screens/Console/Common/UsageBar/UsageBar.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,11 @@ const UsageBar = ({
4545
overflow: "hidden",
4646
}}
4747
>
48-
{sizeItems.map((sizeElement) => {
48+
{sizeItems.map((sizeElement, index) => {
4949
const itemPercentage = (sizeElement.value * 100) / totalValue;
5050
return (
5151
<div
52+
key={`itemSize-${index.toString()}`}
5253
style={{
5354
width: `${itemPercentage}%`,
5455
height: "100%",
Lines changed: 322 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,322 @@
1+
// This file is part of MinIO Console Server
2+
// Copyright (c) 2022 MinIO, Inc.
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Affero General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Affero General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Affero General Public License
15+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
17+
import React, { useState, useEffect } from "react";
18+
import { connect } from "react-redux";
19+
import { Theme } from "@mui/material/styles";
20+
import { Button, Grid, IconButton } from "@mui/material";
21+
import createStyles from "@mui/styles/createStyles";
22+
import withStyles from "@mui/styles/withStyles";
23+
import AddIcon from "@mui/icons-material/Add";
24+
import {
25+
formFieldStyles,
26+
modalStyleUtils,
27+
} from "../../Common/FormComponents/common/styleLibrary";
28+
import { setModalErrorSnackMessage } from "../../../../actions";
29+
import {
30+
ErrorResponseHandler,
31+
IDomainsRequest,
32+
} from "../../../../common/types";
33+
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
34+
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
35+
import api from "../../../../common/api";
36+
import RemoveIcon from "../../../../icons/RemoveIcon";
37+
38+
interface IEditDomains {
39+
open: boolean;
40+
closeModalAndRefresh: (update: boolean) => any;
41+
namespace: string;
42+
idTenant: string;
43+
domains: IDomainsRequest | null;
44+
setModalErrorSnackMessage: typeof setModalErrorSnackMessage;
45+
classes: any;
46+
}
47+
48+
const styles = (theme: Theme) =>
49+
createStyles({
50+
buttonContainer: {
51+
textAlign: "right",
52+
},
53+
infoText: {
54+
fontSize: 14,
55+
},
56+
domainInline: {
57+
display: "flex",
58+
marginBottom: 15,
59+
},
60+
overlayAction: {
61+
marginLeft: 10,
62+
display: "flex",
63+
alignItems: "center",
64+
"& svg": {
65+
width: 15,
66+
height: 15,
67+
},
68+
"& button": {
69+
background: "#EAEAEA",
70+
},
71+
},
72+
...formFieldStyles,
73+
...modalStyleUtils,
74+
});
75+
76+
const EditDomains = ({
77+
open,
78+
closeModalAndRefresh,
79+
namespace,
80+
idTenant,
81+
domains,
82+
setModalErrorSnackMessage,
83+
classes,
84+
}: IEditDomains) => {
85+
const [isSending, setIsSending] = useState<boolean>(false);
86+
const [consoleDomain, setConsoleDomain] = useState<string>("");
87+
const [minioDomains, setMinioDomains] = useState<string[]>([""]);
88+
const [consoleDomainValid, setConsoleDomainValid] = useState<boolean>(true);
89+
const [minioDomainValid, setMinioDomainValid] = useState<boolean[]>([true]);
90+
91+
useEffect(() => {
92+
if (domains) {
93+
const consoleDomainSet = domains.console || "";
94+
setConsoleDomain(consoleDomainSet);
95+
96+
if (consoleDomainSet !== "") {
97+
// We Validate console domain
98+
const consoleRegExp = new RegExp(
99+
/((http|https):\/\/)+[a-zA-Z0-9\-.]{3,}\.[a-zA-Z]{2,}(\.[a-zA-Z]{2,})?(:[1-9]{1}([0-9]{1,4})?)?(\/[a-zA-Z0-9]{1,})*?$/
100+
);
101+
102+
setConsoleDomainValid(consoleRegExp.test(consoleDomainSet));
103+
} else {
104+
setConsoleDomainValid(true);
105+
}
106+
107+
if (domains.minio && domains.minio.length > 0) {
108+
setMinioDomains(domains.minio);
109+
110+
const minioRegExp = new RegExp(
111+
/((http|https):\/\/)+[a-zA-Z0-9\-.]{3,}\.[a-zA-Z]{2,}(\.[a-zA-Z]{2,})?$/
112+
);
113+
114+
const initialValidations = domains.minio.map((domain) => {
115+
if (domain.trim() !== "") {
116+
return minioRegExp.test(domain);
117+
} else {
118+
return true;
119+
}
120+
});
121+
122+
setMinioDomainValid(initialValidations);
123+
}
124+
}
125+
}, [domains]);
126+
127+
const closeAction = () => {
128+
closeModalAndRefresh(false);
129+
};
130+
131+
const resetForm = () => {
132+
setConsoleDomain("");
133+
setConsoleDomainValid(true);
134+
setMinioDomains([""]);
135+
setMinioDomainValid([true]);
136+
};
137+
138+
const updateDomainsList = () => {
139+
setIsSending(true);
140+
141+
let payload = {
142+
domains: {
143+
console: consoleDomain,
144+
minio: minioDomains.filter((minioDomain) => minioDomain.trim() !== ""),
145+
},
146+
};
147+
api
148+
.invoke(
149+
"PUT",
150+
`/api/v1/namespaces/${namespace}/tenants/${idTenant}/domains`,
151+
payload
152+
)
153+
.then(() => {
154+
setIsSending(false);
155+
closeModalAndRefresh(true);
156+
})
157+
.catch((error: ErrorResponseHandler) => {
158+
setModalErrorSnackMessage(error);
159+
setIsSending(false);
160+
});
161+
};
162+
163+
const updateMinIODomain = (value: string, index: number) => {
164+
const cloneDomains = [...minioDomains];
165+
cloneDomains[index] = value;
166+
167+
setMinioDomains(cloneDomains);
168+
};
169+
170+
const addNewMinIODomain = () => {
171+
const cloneDomains = [...minioDomains];
172+
const cloneValidations = [...minioDomainValid];
173+
174+
cloneDomains.push("");
175+
cloneValidations.push(true);
176+
177+
setMinioDomains(cloneDomains);
178+
setMinioDomainValid(cloneValidations);
179+
};
180+
181+
const removeMinIODomain = (removeIndex: number) => {
182+
const filteredDomains = minioDomains.filter(
183+
(_, index) => index !== removeIndex
184+
);
185+
186+
const filterValidations = minioDomainValid.filter(
187+
(_, index) => index !== removeIndex
188+
);
189+
190+
setMinioDomains(filteredDomains);
191+
setMinioDomainValid(filterValidations);
192+
};
193+
194+
const setMinioDomainValidation = (domainValid: boolean, index: number) => {
195+
const cloneValidation = [...minioDomainValid];
196+
cloneValidation[index] = domainValid;
197+
198+
setMinioDomainValid(cloneValidation);
199+
};
200+
201+
return (
202+
<ModalWrapper
203+
title={`Edit Tenant Domains - ${idTenant}`}
204+
modalOpen={open}
205+
onClose={closeAction}
206+
>
207+
<Grid container>
208+
<Grid item xs={12} className={classes.modalFormScrollable}>
209+
<Grid item xs={12} className={`${classes.configSectionItem}`}>
210+
<div className={classes.containerItem}>
211+
<InputBoxWrapper
212+
id="console_domain"
213+
name="console_domain"
214+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
215+
setConsoleDomain(e.target.value);
216+
217+
setConsoleDomainValid(e.target.validity.valid);
218+
}}
219+
label="Console Domain"
220+
value={consoleDomain}
221+
placeholder={
222+
"Eg. http://subdomain.domain:port/subpath1/subpath2"
223+
}
224+
pattern={
225+
"((http|https):\\/\\/)+[a-zA-Z0-9\\-.]{3,}\\.[a-zA-Z]{2,}(\\.[a-zA-Z]{2,})?(:[1-9]{1}([0-9]{1,4})?)?(\\/[a-zA-Z0-9]{1,})*?$"
226+
}
227+
error={
228+
!consoleDomainValid
229+
? "Domain format is incorrect (http|https://subdomain.domain:port/subpath1/subpath2)"
230+
: ""
231+
}
232+
/>
233+
</div>
234+
<div>
235+
<h4>MinIO Domains</h4>
236+
<div>
237+
{minioDomains.map((domain, index) => {
238+
return (
239+
<div
240+
className={`${classes.domainInline}`}
241+
key={`minio-domain-key-${index.toString()}`}
242+
>
243+
<InputBoxWrapper
244+
id={`minio-domain-${index.toString()}`}
245+
name={`minio-domain-${index.toString()}`}
246+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
247+
updateMinIODomain(e.target.value, index);
248+
setMinioDomainValidation(
249+
e.target.validity.valid,
250+
index
251+
);
252+
}}
253+
label={`MinIO Domain ${index + 1}`}
254+
value={domain}
255+
placeholder={"Eg. http://subdomain.domain"}
256+
pattern={
257+
"((http|https):\\/\\/)+[a-zA-Z0-9\\-.]{3,}\\.[a-zA-Z]{2,}(\\.[a-zA-Z]{2,})?$"
258+
}
259+
error={
260+
!minioDomainValid[index]
261+
? "MinIO domain format is incorrect (http|https://subdomain.domain)"
262+
: ""
263+
}
264+
/>
265+
<div className={classes.overlayAction}>
266+
<IconButton
267+
size={"small"}
268+
onClick={addNewMinIODomain}
269+
disabled={index !== minioDomains.length - 1}
270+
>
271+
<AddIcon />
272+
</IconButton>
273+
</div>
274+
275+
<div className={classes.overlayAction}>
276+
<IconButton
277+
size={"small"}
278+
onClick={() => removeMinIODomain(index)}
279+
disabled={minioDomains.length <= 1}
280+
>
281+
<RemoveIcon />
282+
</IconButton>
283+
</div>
284+
</div>
285+
);
286+
})}
287+
</div>
288+
</div>
289+
</Grid>
290+
<Grid item xs={12} className={classes.modalButtonBar}>
291+
<Button
292+
type="button"
293+
color="primary"
294+
variant="outlined"
295+
onClick={resetForm}
296+
>
297+
Clear
298+
</Button>
299+
<Button
300+
type="submit"
301+
variant="contained"
302+
color="primary"
303+
disabled={
304+
isSending ||
305+
!consoleDomainValid ||
306+
minioDomainValid.filter((domain) => !domain).length > 0
307+
}
308+
onClick={updateDomainsList}
309+
>
310+
Save
311+
</Button>
312+
</Grid>
313+
</Grid>
314+
</Grid>
315+
</ModalWrapper>
316+
);
317+
};
318+
const connector = connect(null, {
319+
setModalErrorSnackMessage,
320+
});
321+
322+
export default withStyles(styles)(connector(EditDomains));

0 commit comments

Comments
 (0)