Small Tweaks (#186)
* Support for MinDNS * mindns option * Added minDNS to summary table * Validations of configure page * Added create label & removed console logs * Adding login workaround * Added min limits to inputs * Fixed issue with sizes * Removed empty values from review page * Added zone names * Added validation to zones selector * Fixed issue with back button in zones page * Changed validation for zones filter & simplified clean zones * Changed CredentialsPrompt to be a global component. * Added assets * Added hover to table & removed view button * Added view links & actions to tables * Added links for cloud & console in table * Fixed position of progress bar * Added advanced mode to wizard * Added "zebra-style" tables * Added servers field to simple form * Fixes for demo * Tweaks * updated assets * remove hardcoded bypass * Address Comments Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
FROM golang:1.13
|
||||
|
||||
RUN apt-get update -y && apt-get install -y ca-certificates
|
||||
|
||||
ADD go.mod /go/src/github.com/minio/mcs/go.mod
|
||||
ADD go.sum /go/src/github.com/minio/mcs/go.sum
|
||||
WORKDIR /go/src/github.com/minio/mcs/
|
||||
@@ -12,7 +14,6 @@ WORKDIR /go/src/github.com/minio/mcs/
|
||||
|
||||
ENV CGO_ENABLED=0
|
||||
|
||||
RUN apt-get update -y && apt-get install -y ca-certificates
|
||||
RUN go build -ldflags "-w -s" -a -o mcs ./cmd/mcs
|
||||
|
||||
FROM scratch
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -33,34 +33,35 @@ const GlobalCss = withStyles({
|
||||
fontSize: "14px",
|
||||
textTransform: "capitalize",
|
||||
padding: "16px 25px 16px 25px",
|
||||
borderRadius: "3px"
|
||||
borderRadius: 3,
|
||||
},
|
||||
".MuiButton-sizeSmall": {
|
||||
padding: "4px 10px",
|
||||
fontSize: "0.8125rem"
|
||||
fontSize: "0.8125rem",
|
||||
},
|
||||
".MuiTableCell-head": {
|
||||
borderRadius: "3px 3px 0px 0px",
|
||||
fontSize: "13px"
|
||||
fontSize: 13,
|
||||
},
|
||||
".MuiPaper-root": {
|
||||
borderRadius: "3px"
|
||||
borderRadius: 3,
|
||||
},
|
||||
".MuiDrawer-paperAnchorDockedLeft": {
|
||||
borderRight: "0px"
|
||||
borderRight: 0,
|
||||
},
|
||||
".MuiDrawer-root": {
|
||||
"& .MuiPaper-root": {
|
||||
borderRadius: "0px"
|
||||
}
|
||||
}
|
||||
}
|
||||
borderRadius: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
})(() => null);
|
||||
|
||||
ReactDOM.render(
|
||||
<Provider store={configureStore()}>
|
||||
<GlobalCss />
|
||||
<ThemeProvider theme={theme}>
|
||||
{/*<ThemeProvider theme={newTheme}>*/}
|
||||
<Routes />
|
||||
</ThemeProvider>
|
||||
</Provider>,
|
||||
|
||||
@@ -19,7 +19,7 @@ import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { NewServiceAccount } from "./types";
|
||||
import { Button } from "@material-ui/core";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
|
||||
import ModalWrapper from "../ModalWrapper/ModalWrapper";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
@@ -36,6 +36,7 @@ interface ICredentialsPromptProps {
|
||||
classes: any;
|
||||
newServiceAccount: NewServiceAccount | null;
|
||||
open: boolean;
|
||||
entity: string;
|
||||
closeModal: () => void;
|
||||
}
|
||||
|
||||
@@ -60,6 +61,7 @@ const CredentialsPrompt = ({
|
||||
newServiceAccount,
|
||||
open,
|
||||
closeModal,
|
||||
entity,
|
||||
}: ICredentialsPromptProps) => {
|
||||
if (!newServiceAccount) {
|
||||
return null;
|
||||
@@ -71,12 +73,12 @@ const CredentialsPrompt = ({
|
||||
onClose={() => {
|
||||
closeModal();
|
||||
}}
|
||||
title="New Service Account Created"
|
||||
title={`New ${entity} Created`}
|
||||
>
|
||||
<React.Fragment>
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.formScrollable}>
|
||||
A new service account has been created with the following details:
|
||||
A new {entity} has been created with the following details:
|
||||
<ul>
|
||||
<li>
|
||||
<b>Access Key:</b> {newServiceAccount.accessKey}
|
||||
@@ -0,0 +1,20 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export interface NewServiceAccount {
|
||||
accessKey: string;
|
||||
secretKey: string;
|
||||
}
|
||||
@@ -47,6 +47,8 @@ interface InputBoxProps {
|
||||
error?: string;
|
||||
required?: boolean;
|
||||
placeholder?: string;
|
||||
min?: string;
|
||||
max?: string;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
@@ -110,8 +112,20 @@ const InputBoxWrapper = ({
|
||||
error = "",
|
||||
required = false,
|
||||
placeholder = "",
|
||||
min,
|
||||
max,
|
||||
classes,
|
||||
}: InputBoxProps) => {
|
||||
let inputProps: any = { "data-index": index };
|
||||
|
||||
if (type === "number" && min) {
|
||||
inputProps["min"] = min;
|
||||
}
|
||||
|
||||
if (type === "number" && max) {
|
||||
inputProps["max"] = max;
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Grid
|
||||
@@ -154,7 +168,7 @@ const InputBoxWrapper = ({
|
||||
type={type}
|
||||
multiline={multiline}
|
||||
autoComplete={autoComplete}
|
||||
inputProps={{ "data-index": index }}
|
||||
inputProps={inputProps}
|
||||
error={error !== ""}
|
||||
helperText={error}
|
||||
placeholder={placeholder}
|
||||
|
||||
@@ -20,7 +20,7 @@ export const fieldBasic = {
|
||||
inputLabel: {
|
||||
fontWeight: 500,
|
||||
marginRight: 10,
|
||||
width: 100,
|
||||
width: 160,
|
||||
fontSize: 14,
|
||||
color: "#393939",
|
||||
textAlign: "right" as const,
|
||||
|
||||
@@ -27,6 +27,7 @@ const styles = (theme: Theme) =>
|
||||
flexGrow: 1,
|
||||
},
|
||||
wizardSteps: {
|
||||
minWidth: 180,
|
||||
marginRight: 10,
|
||||
"& ul": {
|
||||
padding: 15,
|
||||
|
||||
@@ -59,8 +59,6 @@ const WizardPage = ({ classes, page, pageChange }: IWizardPage) => {
|
||||
}
|
||||
};
|
||||
|
||||
console.log("buttons", page);
|
||||
|
||||
return (
|
||||
<div className={classes.wizardStepContainer}>
|
||||
<div className={classes.wizardComponent}>{page.componentRender}</div>
|
||||
|
||||
@@ -26,6 +26,7 @@ export interface IWizardElement {
|
||||
label: string;
|
||||
componentRender: any;
|
||||
buttons: IWizardButton[];
|
||||
advancedOnly?: boolean;
|
||||
}
|
||||
|
||||
export interface IWizardMain {
|
||||
|
||||
@@ -20,6 +20,8 @@ import ViewIcon from "./TableActionIcons/ViewIcon";
|
||||
import PencilIcon from "./TableActionIcons/PencilIcon";
|
||||
import DeleteIcon from "./TableActionIcons/DeleteIcon";
|
||||
import DescriptionIcon from "./TableActionIcons/DescriptionIcon";
|
||||
import CloudIcon from "./TableActionIcons/CloudIcon";
|
||||
import ConsoleIcon from "./TableActionIcons/ConsoleIcon";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
interface IActionButton {
|
||||
@@ -42,6 +44,10 @@ const defineIcon = (type: string, selected: boolean) => {
|
||||
return <DeleteIcon active={selected} />;
|
||||
case "description":
|
||||
return <DescriptionIcon active={selected} />;
|
||||
case "cloud":
|
||||
return <CloudIcon active={selected} />;
|
||||
case "console":
|
||||
return <ConsoleIcon active={selected} />;
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -64,7 +70,8 @@ const TableActionButton = ({
|
||||
size={"small"}
|
||||
onClick={
|
||||
onClick
|
||||
? () => {
|
||||
? (e) => {
|
||||
e.stopPropagation();
|
||||
onClick(valueClick);
|
||||
}
|
||||
: () => null
|
||||
@@ -79,7 +86,16 @@ const TableActionButton = ({
|
||||
}
|
||||
|
||||
if (isString(to)) {
|
||||
return <Link to={`${to}/${valueClick}`}>{buttonElement}</Link>;
|
||||
return (
|
||||
<Link
|
||||
to={`${to}/${valueClick}`}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{buttonElement}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import React from "react";
|
||||
import { IIcon, selected, unSelected } from "./common";
|
||||
|
||||
const CloudIcon = ({ active = false }: IIcon) => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill={active ? selected : unSelected}
|
||||
d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default CloudIcon;
|
||||
@@ -0,0 +1,20 @@
|
||||
import React from "react";
|
||||
import { IIcon, selected, unSelected } from "./common";
|
||||
|
||||
const ConsoleIcon = ({ active = false }: IIcon) => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill={active ? selected : unSelected}
|
||||
d="M21 3H3c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H3v-3h18v3z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConsoleIcon;
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { TablePaginationActionsProps } from "@material-ui/core/TablePagination/TablePaginationActions";
|
||||
import TableActionButton from "./TableActionButton";
|
||||
import history from "../../../../history";
|
||||
import { checkboxIcons } from "../FormComponents/common/styleLibrary";
|
||||
|
||||
//Interfaces for table Items
|
||||
@@ -143,6 +144,16 @@ const styles = (theme: Theme) =>
|
||||
paddingTop: "100px",
|
||||
paddingBottom: "100px",
|
||||
},
|
||||
rowElement: {
|
||||
userSelect: "none",
|
||||
|
||||
"&:hover": {
|
||||
backgroundColor: "#ececec",
|
||||
},
|
||||
},
|
||||
rowClickable: {
|
||||
cursor: "pointer",
|
||||
},
|
||||
...checkboxIcons,
|
||||
});
|
||||
|
||||
@@ -190,6 +201,10 @@ const elementActions = (
|
||||
idField: string
|
||||
) => {
|
||||
return actions.map((action: ItemActions, index: number) => {
|
||||
if (action.type === "view") {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<TableActionButton
|
||||
type={action.type}
|
||||
@@ -219,6 +234,24 @@ const TableWrapper = ({
|
||||
stickyHeader = false,
|
||||
paginatorConfig,
|
||||
}: TableWrapperProps) => {
|
||||
const findView = itemActions
|
||||
? itemActions.find((el) => el.type === "view")
|
||||
: null;
|
||||
|
||||
const clickAction = (rowItem: any) => {
|
||||
if (findView) {
|
||||
const valueClick = findView.sendOnlyId ? rowItem[idField] : rowItem;
|
||||
if (findView.to) {
|
||||
history.push(`${findView.to}/${valueClick}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (findView.onClick) {
|
||||
findView.onClick(valueClick);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Grid item xs={12}>
|
||||
<Paper className={classes.paper}>
|
||||
@@ -265,7 +298,15 @@ const TableWrapper = ({
|
||||
: false;
|
||||
|
||||
return (
|
||||
<TableRow key={`tb-${entityName}-${index.toString()}`}>
|
||||
<TableRow
|
||||
key={`tb-${entityName}-${index.toString()}`}
|
||||
className={`${findView ? classes.rowClickable : ""} ${
|
||||
classes.rowElement
|
||||
}`}
|
||||
onClick={() => {
|
||||
clickAction(record);
|
||||
}}
|
||||
>
|
||||
{onSelect && selectedItems && (
|
||||
<TableCell
|
||||
padding="checkbox"
|
||||
@@ -278,6 +319,10 @@ const TableWrapper = ({
|
||||
inputProps={{ "aria-label": "secondary checkbox" }}
|
||||
checked={isSelected}
|
||||
onChange={onSelect}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}}
|
||||
checkedIcon={<span className={classes.checkedIcon} />}
|
||||
icon={<span className={classes.unCheckedIcon} />}
|
||||
/>
|
||||
|
||||
@@ -378,10 +378,6 @@ const Console = ({
|
||||
) : null}
|
||||
</Switch>
|
||||
</Router>
|
||||
|
||||
<Box pt={4}>
|
||||
<Copyright />
|
||||
</Box>
|
||||
</Container>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@@ -25,7 +25,7 @@ import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
|
||||
import api from "../../../common/api";
|
||||
import "codemirror/lib/codemirror.css";
|
||||
import "codemirror/theme/material.css";
|
||||
import { NewServiceAccount } from "./types";
|
||||
import { NewServiceAccount } from "../Common/CredentialsPrompt/types";
|
||||
import HelpIcon from "@material-ui/icons/Help";
|
||||
|
||||
require("codemirror/mode/javascript/javascript");
|
||||
|
||||
@@ -20,11 +20,11 @@ import Grid from "@material-ui/core/Grid";
|
||||
import api from "../../../common/api";
|
||||
import { Button } from "@material-ui/core";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import { NewServiceAccount } from "./types";
|
||||
import { NewServiceAccount } from "../Common/CredentialsPrompt/types";
|
||||
import { MinTablePaginationActions } from "../../../common/MinTablePaginationActions";
|
||||
import AddServiceAccount from "./AddServiceAccount";
|
||||
import DeleteServiceAccount from "./DeleteServiceAccount";
|
||||
import CredentialsPrompt from "./CredentialsPrompt";
|
||||
import CredentialsPrompt from "../Common/CredentialsPrompt/CredentialsPrompt";
|
||||
import { CreateIcon } from "../../../icons";
|
||||
import TextField from "@material-ui/core/TextField";
|
||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||
@@ -222,6 +222,7 @@ const ServiceAccounts = ({ classes }: IServiceAccountsProps) => {
|
||||
closeModal={() => {
|
||||
closeCredentialsModal();
|
||||
}}
|
||||
entity="Service Account"
|
||||
/>
|
||||
)}
|
||||
<Grid container>
|
||||
|
||||
@@ -18,8 +18,3 @@ export interface ServiceAccountsList {
|
||||
service_accounts: string[];
|
||||
total: number;
|
||||
}
|
||||
|
||||
export interface NewServiceAccount {
|
||||
accessKey: string;
|
||||
secretKey: string;
|
||||
}
|
||||
|
||||
@@ -38,10 +38,14 @@ import {
|
||||
} from "../../../../utils/validationFunctions";
|
||||
import GenericWizard from "../../Common/GenericWizard/GenericWizard";
|
||||
import { IWizardElement } from "../../Common/GenericWizard/types";
|
||||
import { NewServiceAccount } from "../../Common/CredentialsPrompt/types";
|
||||
|
||||
interface IAddTenantProps {
|
||||
open: boolean;
|
||||
closeModalAndRefresh: (reloadData: boolean) => any;
|
||||
closeModalAndRefresh: (
|
||||
reloadData: boolean,
|
||||
res: NewServiceAccount | null
|
||||
) => any;
|
||||
classes: any;
|
||||
}
|
||||
|
||||
@@ -68,11 +72,17 @@ const styles = (theme: Theme) =>
|
||||
paddingTop: 5,
|
||||
marginBottom: 10,
|
||||
backgroundColor: "#fff",
|
||||
zIndex: 500,
|
||||
},
|
||||
tableTitle: {
|
||||
fontWeight: 700,
|
||||
width: "30%",
|
||||
},
|
||||
zoneError: {
|
||||
color: "#dc1f2e",
|
||||
fontSize: "0.75rem",
|
||||
paddingLeft: 120,
|
||||
},
|
||||
...modalBasic,
|
||||
});
|
||||
|
||||
@@ -86,6 +96,7 @@ const AddTenant = ({
|
||||
closeModalAndRefresh,
|
||||
classes,
|
||||
}: IAddTenantProps) => {
|
||||
// Fields
|
||||
const [addSending, setAddSending] = useState<boolean>(false);
|
||||
const [addError, setAddError] = useState<string>("");
|
||||
const [tenantName, setTenantName] = useState<string>("");
|
||||
@@ -95,7 +106,7 @@ const AddTenant = ({
|
||||
const [volumesPerServer, setVolumesPerServer] = useState<number>(0);
|
||||
const [volumeConfiguration, setVolumeConfiguration] = useState<
|
||||
IVolumeConfiguration
|
||||
>({ size: "", storage_class: "" });
|
||||
>({ size: 0, storage_class: "" });
|
||||
const [mountPath, setMountPath] = useState<string>("");
|
||||
const [accessKey, setAccessKey] = useState<string>("");
|
||||
const [secretKey, setSecretKey] = useState<string>("");
|
||||
@@ -105,13 +116,23 @@ const AddTenant = ({
|
||||
const [storageClasses, setStorageClassesList] = useState<Opts[]>([]);
|
||||
const [validationErrors, setValidationErrors] = useState<any>({});
|
||||
const [namespace, setNamespace] = useState<string>("");
|
||||
const [advancedMode, setAdvancedMode] = useState<boolean>(false);
|
||||
|
||||
// Forms Validation
|
||||
const [nameTenantValid, setNameTenantValid] = useState<boolean>(false);
|
||||
const [configValid, setConfigValid] = useState<boolean>(false);
|
||||
const [configureValid, setConfigureValid] = useState<boolean>(false);
|
||||
const [zonesValid, setZonesValid] = useState<boolean>(false);
|
||||
|
||||
// Custom Elements
|
||||
const [customACCK, setCustomACCK] = useState<boolean>(false);
|
||||
const [customDockerhub, setCustomDockerhub] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
fetchStorageClassList();
|
||||
}, []);
|
||||
|
||||
/* Validations of pages */
|
||||
useEffect(() => {
|
||||
const commonValidation = commonFormValidation([validationElements[0]]);
|
||||
|
||||
@@ -121,17 +142,84 @@ const AddTenant = ({
|
||||
}, [tenantName]);
|
||||
|
||||
useEffect(() => {
|
||||
const commonValidation = commonFormValidation(
|
||||
validationElements.slice(1, 3)
|
||||
);
|
||||
let subValidation = validationElements.slice(1, 3);
|
||||
|
||||
if (!advancedMode) {
|
||||
subValidation.push({
|
||||
fieldKey: "servers",
|
||||
required: true,
|
||||
pattern: /\d+/,
|
||||
customPatternMessage: "Field must be numeric",
|
||||
value: zones.length > 0 ? zones[0].servers.toString(10) : "0",
|
||||
});
|
||||
}
|
||||
|
||||
const commonValidation = commonFormValidation(subValidation);
|
||||
|
||||
setConfigValid(
|
||||
!("volumes_per_server" in commonValidation) &&
|
||||
!("volume_size" in commonValidation)
|
||||
!("volume_size" in commonValidation) &&
|
||||
!("servers" in commonValidation)
|
||||
);
|
||||
|
||||
setValidationErrors(commonValidation);
|
||||
}, [volumesPerServer, volumeConfiguration]);
|
||||
}, [volumesPerServer, volumeConfiguration, zones]);
|
||||
|
||||
useEffect(() => {
|
||||
let customAccountValidation: IValidation[] = [];
|
||||
if (customACCK) {
|
||||
customAccountValidation = [
|
||||
...customAccountValidation,
|
||||
{
|
||||
fieldKey: "access_key",
|
||||
required: true,
|
||||
value: accessKey,
|
||||
},
|
||||
{
|
||||
fieldKey: "secret_key",
|
||||
required: true,
|
||||
value: secretKey,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
if (customDockerhub) {
|
||||
customAccountValidation = [
|
||||
...customAccountValidation,
|
||||
{
|
||||
fieldKey: "image",
|
||||
required: true,
|
||||
value: imageName,
|
||||
pattern: /^((.*?)\/(.*?):(.+))$/,
|
||||
customPatternMessage: "Format must be of form: 'minio/minio:VERSION'",
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const commonVal = commonFormValidation(customAccountValidation);
|
||||
|
||||
setConfigureValid(Object.keys(commonVal).length === 0);
|
||||
|
||||
setValidationErrors(commonVal);
|
||||
}, [customACCK, customDockerhub, accessKey, secretKey, imageName]);
|
||||
|
||||
useEffect(() => {
|
||||
const filteredZones = zones.filter(
|
||||
(zone) => zone.name !== "" && zone.servers !== 0 && !isNaN(zone.servers)
|
||||
);
|
||||
|
||||
if (filteredZones.length > 0) {
|
||||
setZonesValid(true);
|
||||
setValidationErrors({});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setZonesValid(false);
|
||||
setValidationErrors({ zones_selector: "Please add a valid zone" });
|
||||
}, [zones]);
|
||||
|
||||
/* End Validation of pages */
|
||||
|
||||
const validationElements: IValidation[] = [
|
||||
{
|
||||
@@ -145,34 +233,23 @@ const AddTenant = ({
|
||||
{
|
||||
fieldKey: "volumes_per_server",
|
||||
required: true,
|
||||
pattern: /\d+/,
|
||||
customPatternMessage: "Field must be numeric",
|
||||
value: volumesPerServer.toString(10),
|
||||
},
|
||||
{
|
||||
fieldKey: "volume_size",
|
||||
required: true,
|
||||
value: volumeConfiguration.size,
|
||||
},
|
||||
{
|
||||
fieldKey: "image",
|
||||
required: false,
|
||||
value: imageName,
|
||||
pattern: /\d+/,
|
||||
customPatternMessage: "Field must be numeric",
|
||||
value: volumeConfiguration.size.toString(10),
|
||||
},
|
||||
|
||||
{
|
||||
fieldKey: "service_name",
|
||||
required: false,
|
||||
value: serviceName,
|
||||
},
|
||||
|
||||
{
|
||||
fieldKey: "access_key",
|
||||
required: false,
|
||||
value: accessKey,
|
||||
},
|
||||
{
|
||||
fieldKey: "secret_key",
|
||||
required: false,
|
||||
value: secretKey,
|
||||
},
|
||||
];
|
||||
|
||||
const clearValidationError = (fieldKey: string) => {
|
||||
@@ -184,38 +261,42 @@ const AddTenant = ({
|
||||
|
||||
useEffect(() => {
|
||||
if (addSending) {
|
||||
let cleanZones: IZone[] = [];
|
||||
for (let zone of zones) {
|
||||
if (zone.name !== "") {
|
||||
cleanZones.push(zone);
|
||||
}
|
||||
}
|
||||
let cleanZones = zones.filter(
|
||||
(zone) => zone.name !== "" && zone.servers > 0 && !isNaN(zone.servers)
|
||||
);
|
||||
|
||||
const commonValidation = commonFormValidation(validationElements);
|
||||
|
||||
setValidationErrors(commonValidation);
|
||||
|
||||
if (Object.keys(commonValidation).length === 0) {
|
||||
const data: { [key: string]: any } = {
|
||||
name: tenantName,
|
||||
service_name: tenantName,
|
||||
image: imageName,
|
||||
enable_ssl: enableSSL,
|
||||
enable_mcs: enableMCS,
|
||||
access_key: accessKey,
|
||||
secret_key: secretKey,
|
||||
volumes_per_server: volumesPerServer,
|
||||
volume_configuration: {
|
||||
size: `${volumeConfiguration.size}${sizeFactor}`,
|
||||
storage_class: volumeConfiguration.storage_class,
|
||||
},
|
||||
zones: cleanZones,
|
||||
};
|
||||
|
||||
api
|
||||
.invoke("POST", `/api/v1/mkube/tenants`, {
|
||||
name: tenantName,
|
||||
service_name: tenantName,
|
||||
image: imageName,
|
||||
enable_ssl: enableSSL,
|
||||
enable_mcs: enableMCS,
|
||||
access_key: accessKey,
|
||||
secret_key: secretKey,
|
||||
volumes_per_server: volumesPerServer,
|
||||
volume_configuration: {
|
||||
size: `${volumeConfiguration.size}${sizeFactor}`,
|
||||
storage_class: volumeConfiguration.storage_class,
|
||||
},
|
||||
zones: cleanZones,
|
||||
})
|
||||
.then(() => {
|
||||
.invoke("POST", `/api/v1/mkube/tenants`, data)
|
||||
.then((res) => {
|
||||
const newSrvAcc: NewServiceAccount = {
|
||||
accessKey: res.access_key,
|
||||
secretKey: res.secret_key,
|
||||
};
|
||||
|
||||
setAddSending(false);
|
||||
setAddError("");
|
||||
closeModalAndRefresh(true);
|
||||
closeModalAndRefresh(true, newSrvAcc);
|
||||
})
|
||||
.catch((err) => {
|
||||
setAddSending(false);
|
||||
@@ -228,9 +309,17 @@ const AddTenant = ({
|
||||
}
|
||||
}, [addSending]);
|
||||
|
||||
useEffect(() => {
|
||||
if (advancedMode) {
|
||||
setZones([{ name: "zone-1", servers: 0, capacity: "0", volumes: 0 }]);
|
||||
} else {
|
||||
setZones([{ name: "zone-1", servers: 1, capacity: "0", volumes: 0 }]);
|
||||
}
|
||||
}, [advancedMode]);
|
||||
|
||||
const setVolumeConfig = (item: string, value: string) => {
|
||||
const volumeCopy: IVolumeConfiguration = {
|
||||
size: item !== "size" ? volumeConfiguration.size : value,
|
||||
size: item !== "size" ? volumeConfiguration.size : parseInt(value),
|
||||
storage_class:
|
||||
item !== "storage_class" ? volumeConfiguration.storage_class : value,
|
||||
};
|
||||
@@ -238,6 +327,14 @@ const AddTenant = ({
|
||||
setVolumeConfiguration(volumeCopy);
|
||||
};
|
||||
|
||||
const setServersSimple = (value: string) => {
|
||||
const copyZone = [...zones];
|
||||
|
||||
copyZone[0].servers = parseInt(value, 10);
|
||||
|
||||
setZones(copyZone);
|
||||
};
|
||||
|
||||
const fetchStorageClassList = () => {
|
||||
api
|
||||
.invoke("GET", `/api/v1/mkube/storage-classes`)
|
||||
@@ -268,7 +365,7 @@ const AddTenant = ({
|
||||
type: "other",
|
||||
enabled: true,
|
||||
action: () => {
|
||||
closeModalAndRefresh(false);
|
||||
closeModalAndRefresh(false, null);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -276,24 +373,47 @@ const AddTenant = ({
|
||||
{
|
||||
label: "Name Tenant",
|
||||
componentRender: (
|
||||
<Grid item xs={12}>
|
||||
<React.Fragment>
|
||||
<div className={classes.headerElement}>
|
||||
<h3>Name Tenant</h3>
|
||||
<span>How would you like to name this new tenant?</span>
|
||||
</div>
|
||||
<InputBoxWrapper
|
||||
id="tenant-name"
|
||||
name="tenant-name"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setTenantName(e.target.value);
|
||||
clearValidationError("tenant-name");
|
||||
}}
|
||||
label="Tenant Name"
|
||||
value={tenantName}
|
||||
required
|
||||
error={validationErrors["tenant-name"] || ""}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="tenant-name"
|
||||
name="tenant-name"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setTenantName(e.target.value);
|
||||
clearValidationError("tenant-name");
|
||||
}}
|
||||
label="Tenant Name"
|
||||
value={tenantName}
|
||||
required
|
||||
error={validationErrors["tenant-name"] || ""}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
<span>
|
||||
Use Advanced mode to configure additional options in the tenant
|
||||
</span>
|
||||
<br />
|
||||
<br />
|
||||
<CheckboxWrapper
|
||||
value="adv_mode"
|
||||
id="adv_mode"
|
||||
name="adv_mode"
|
||||
checked={advancedMode}
|
||||
onChange={(e) => {
|
||||
const targetD = e.target;
|
||||
const checked = targetD.checked;
|
||||
|
||||
setAdvancedMode(checked);
|
||||
}}
|
||||
label={"Advanced Mode"}
|
||||
/>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
),
|
||||
buttons: [
|
||||
cancelButton,
|
||||
@@ -308,48 +428,109 @@ const AddTenant = ({
|
||||
<h3>Configure</h3>
|
||||
<span>Basic configurations for tenant management</span>
|
||||
</div>
|
||||
Please enter your access & secret keys
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="access_key"
|
||||
name="access_key"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setAccessKey(e.target.value);
|
||||
clearValidationError("access_key");
|
||||
<CheckboxWrapper
|
||||
value="custom_acck"
|
||||
id="custom_acck"
|
||||
name="custom_acck"
|
||||
checked={customACCK}
|
||||
onChange={(e) => {
|
||||
const targetD = e.target;
|
||||
const checked = targetD.checked;
|
||||
|
||||
setCustomACCK(checked);
|
||||
}}
|
||||
label="Access Key"
|
||||
value={accessKey}
|
||||
error={validationErrors["access_key"] || ""}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="secret_key"
|
||||
name="secret_key"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSecretKey(e.target.value);
|
||||
clearValidationError("secret_key");
|
||||
}}
|
||||
label="Secret Key"
|
||||
value={secretKey}
|
||||
error={validationErrors["secret_key"] || ""}
|
||||
/>
|
||||
</Grid>
|
||||
Please enter the MinIO image from dockerhub
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="image"
|
||||
name="image"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setImageName(e.target.value);
|
||||
clearValidationError("image");
|
||||
}}
|
||||
label="MinIO Image"
|
||||
value={imageName}
|
||||
error={validationErrors["image"] || ""}
|
||||
placeholder="Eg. minio/minio:RELEASE.2020-05-08T02-40-49Z"
|
||||
label={"Use Custom Access Keys"}
|
||||
/>
|
||||
</Grid>
|
||||
{customACCK && (
|
||||
<React.Fragment>
|
||||
Please enter your access & secret keys
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="access_key"
|
||||
name="access_key"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setAccessKey(e.target.value);
|
||||
clearValidationError("access_key");
|
||||
}}
|
||||
label="Access Key"
|
||||
value={accessKey}
|
||||
error={validationErrors["access_key"] || ""}
|
||||
required
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="secret_key"
|
||||
name="secret_key"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSecretKey(e.target.value);
|
||||
clearValidationError("secret_key");
|
||||
}}
|
||||
label="Secret Key"
|
||||
value={secretKey}
|
||||
error={validationErrors["secret_key"] || ""}
|
||||
required
|
||||
/>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
)}
|
||||
|
||||
{advancedMode && (
|
||||
<Grid item xs={12}>
|
||||
<CheckboxWrapper
|
||||
value="custom_dockerhub"
|
||||
id="custom_dockerhub"
|
||||
name="custom_dockerhub"
|
||||
checked={customDockerhub}
|
||||
onChange={(e) => {
|
||||
const targetD = e.target;
|
||||
const checked = targetD.checked;
|
||||
|
||||
setCustomDockerhub(checked);
|
||||
}}
|
||||
label={"Use custom image"}
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
{customDockerhub && (
|
||||
<React.Fragment>
|
||||
Please enter the MinIO image from dockerhub
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="image"
|
||||
name="image"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setImageName(e.target.value);
|
||||
clearValidationError("image");
|
||||
}}
|
||||
label="MinIO Image"
|
||||
value={imageName}
|
||||
error={validationErrors["image"] || ""}
|
||||
placeholder="Eg. minio/minio:RELEASE.2020-05-08T02-40-49Z"
|
||||
required
|
||||
/>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</React.Fragment>
|
||||
),
|
||||
buttons: [
|
||||
cancelButton,
|
||||
{ label: "Back", type: "back", enabled: true },
|
||||
{ label: "Next", type: "next", enabled: configureValid },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Service Configuration",
|
||||
advancedOnly: true,
|
||||
componentRender: (
|
||||
<React.Fragment>
|
||||
<div className={classes.headerElement}>
|
||||
<h3>Service Configuration</h3>
|
||||
</div>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="service_name"
|
||||
@@ -386,6 +567,7 @@ const AddTenant = ({
|
||||
},
|
||||
{
|
||||
label: "Storage Class",
|
||||
advancedOnly: true,
|
||||
componentRender: (
|
||||
<React.Fragment>
|
||||
<div className={classes.headerElement}>
|
||||
@@ -423,17 +605,38 @@ const AddTenant = ({
|
||||
<h3>Server Configuration</h3>
|
||||
<span>Define the server configuration</span>
|
||||
</div>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="mount_path"
|
||||
name="mount_path"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setMountPath(e.target.value);
|
||||
}}
|
||||
label="Mount Path"
|
||||
value={mountPath}
|
||||
/>
|
||||
</Grid>
|
||||
{advancedMode && (
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="mount_path"
|
||||
name="mount_path"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setMountPath(e.target.value);
|
||||
}}
|
||||
label="Mount Path"
|
||||
value={mountPath}
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
{!advancedMode && (
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="servers"
|
||||
name="servers"
|
||||
type="number"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setServersSimple(e.target.value);
|
||||
clearValidationError("servers");
|
||||
}}
|
||||
label="Number of Servers"
|
||||
value={zones.length > 0 ? zones[0].servers.toString(10) : "0"}
|
||||
min="0"
|
||||
required
|
||||
error={validationErrors["servers"] || ""}
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="volumes_per_server"
|
||||
@@ -445,6 +648,7 @@ const AddTenant = ({
|
||||
}}
|
||||
label="Volumes per Server"
|
||||
value={volumesPerServer.toString(10)}
|
||||
min="0"
|
||||
required
|
||||
error={validationErrors["volumes_per_server"] || ""}
|
||||
/>
|
||||
@@ -453,6 +657,7 @@ const AddTenant = ({
|
||||
<div className={classes.multiContainer}>
|
||||
<div>
|
||||
<InputBoxWrapper
|
||||
type="number"
|
||||
id="volume_size"
|
||||
name="volume_size"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
@@ -460,9 +665,10 @@ const AddTenant = ({
|
||||
clearValidationError("volume_size");
|
||||
}}
|
||||
label="Size"
|
||||
value={volumeConfiguration.size}
|
||||
value={volumeConfiguration.size.toString(10)}
|
||||
required
|
||||
error={validationErrors["volume_size"] || ""}
|
||||
min="0"
|
||||
/>
|
||||
</div>
|
||||
<div className={classes.sizeFactorContainer}>
|
||||
@@ -489,6 +695,7 @@ const AddTenant = ({
|
||||
},
|
||||
{
|
||||
label: "Zones Definition",
|
||||
advancedOnly: true,
|
||||
componentRender: (
|
||||
<React.Fragment>
|
||||
<div className={classes.headerElement}>
|
||||
@@ -506,17 +713,21 @@ const AddTenant = ({
|
||||
elements={zones}
|
||||
/>
|
||||
</div>
|
||||
<div className={classes.zoneError}>
|
||||
{validationErrors["zones_selector"] || ""}
|
||||
</div>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
),
|
||||
buttons: [
|
||||
cancelButton,
|
||||
{ label: "Back", type: "back", enabled: true },
|
||||
{ label: "Next", type: "next", enabled: true },
|
||||
{ label: "Next", type: "next", enabled: zonesValid },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Extra Configurations",
|
||||
advancedOnly: true,
|
||||
componentRender: (
|
||||
<React.Fragment>
|
||||
<div className={classes.headerElement}>
|
||||
@@ -534,7 +745,7 @@ const AddTenant = ({
|
||||
|
||||
setEnableMCS(checked);
|
||||
}}
|
||||
label={"Enable mcs"}
|
||||
label={"Enable Console"}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
@@ -588,48 +799,65 @@ const AddTenant = ({
|
||||
</TableCell>
|
||||
<TableCell>{tenantName}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell align="right" className={classes.tableTitle}>
|
||||
Access Key
|
||||
</TableCell>
|
||||
<TableCell>{accessKey}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell align="right" className={classes.tableTitle}>
|
||||
Secret Key
|
||||
</TableCell>
|
||||
<TableCell>{secretKey}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell align="right" className={classes.tableTitle}>
|
||||
MinIO Image
|
||||
</TableCell>
|
||||
<TableCell>{imageName}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell align="right" className={classes.tableTitle}>
|
||||
Service Name
|
||||
</TableCell>
|
||||
<TableCell>{serviceName}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell align="right" className={classes.tableTitle}>
|
||||
Namespace
|
||||
</TableCell>
|
||||
<TableCell>{namespace}</TableCell>
|
||||
</TableRow>
|
||||
{customACCK && (
|
||||
<React.Fragment>
|
||||
<TableRow>
|
||||
<TableCell align="right" className={classes.tableTitle}>
|
||||
Access Key
|
||||
</TableCell>
|
||||
<TableCell>{accessKey}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell align="right" className={classes.tableTitle}>
|
||||
Secret Key
|
||||
</TableCell>
|
||||
<TableCell>{secretKey}</TableCell>
|
||||
</TableRow>
|
||||
</React.Fragment>
|
||||
)}
|
||||
|
||||
{customDockerhub && (
|
||||
<TableRow>
|
||||
<TableCell align="right" className={classes.tableTitle}>
|
||||
MinIO Image
|
||||
</TableCell>
|
||||
<TableCell>{imageName}</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
|
||||
{serviceName !== "" && (
|
||||
<TableRow>
|
||||
<TableCell align="right" className={classes.tableTitle}>
|
||||
Service Name
|
||||
</TableCell>
|
||||
<TableCell>{serviceName}</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
|
||||
{namespace !== "" && (
|
||||
<TableRow>
|
||||
<TableCell align="right" className={classes.tableTitle}>
|
||||
Namespace
|
||||
</TableCell>
|
||||
<TableCell>{namespace}</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
|
||||
<TableRow>
|
||||
<TableCell align="right" className={classes.tableTitle}>
|
||||
Storage Class
|
||||
</TableCell>
|
||||
<TableCell>{volumeConfiguration.storage_class}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell align="right" className={classes.tableTitle}>
|
||||
Mount Path
|
||||
</TableCell>
|
||||
<TableCell>{mountPath}</TableCell>
|
||||
</TableRow>
|
||||
{mountPath !== "" && (
|
||||
<TableRow>
|
||||
<TableCell align="right" className={classes.tableTitle}>
|
||||
Mount Path
|
||||
</TableCell>
|
||||
<TableCell>{mountPath}</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
|
||||
<TableRow>
|
||||
<TableCell align="right" className={classes.tableTitle}>
|
||||
Volumes per Server
|
||||
@@ -650,37 +878,34 @@ const AddTenant = ({
|
||||
</TableCell>
|
||||
<TableCell>{zones.length}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell align="right" className={classes.tableTitle}>
|
||||
Enable SSL
|
||||
</TableCell>
|
||||
<TableCell>{enableSSL ? "Enabled" : "Disabled"}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell align="right" className={classes.tableTitle}>
|
||||
Enable MCS
|
||||
</TableCell>
|
||||
<TableCell>{enableMCS ? "Enabled" : "Disabled"}</TableCell>
|
||||
</TableRow>
|
||||
{advancedMode && (
|
||||
<React.Fragment>
|
||||
<TableRow>
|
||||
<TableCell align="right" className={classes.tableTitle}>
|
||||
Enable SSL
|
||||
</TableCell>
|
||||
<TableCell>{enableSSL ? "Enabled" : "Disabled"}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell align="right" className={classes.tableTitle}>
|
||||
Enable MCS
|
||||
</TableCell>
|
||||
<TableCell>{enableMCS ? "Enabled" : "Disabled"}</TableCell>
|
||||
</TableRow>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
{addSending && (
|
||||
<Grid item xs={12}>
|
||||
<LinearProgress />
|
||||
</Grid>
|
||||
)}
|
||||
</React.Fragment>
|
||||
),
|
||||
buttons: [
|
||||
cancelButton,
|
||||
{ label: "Back", type: "back", enabled: true },
|
||||
{
|
||||
label: "Save",
|
||||
label: "Create",
|
||||
type: "submit",
|
||||
enabled: !addSending,
|
||||
action: () => {
|
||||
console.log("Save");
|
||||
setAddSending(true);
|
||||
},
|
||||
},
|
||||
@@ -688,18 +913,29 @@ const AddTenant = ({
|
||||
},
|
||||
];
|
||||
|
||||
let filteredWizardSteps = wizardSteps;
|
||||
|
||||
if (!advancedMode) {
|
||||
filteredWizardSteps = wizardSteps.filter((step) => !step.advancedOnly);
|
||||
}
|
||||
|
||||
return (
|
||||
<ModalWrapper
|
||||
title="Create Tenant"
|
||||
modalOpen={open}
|
||||
onClose={() => {
|
||||
setAddError("");
|
||||
closeModalAndRefresh(false);
|
||||
closeModalAndRefresh(false, null);
|
||||
}}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<GenericWizard wizardSteps={wizardSteps} />
|
||||
{addSending && (
|
||||
<Grid item xs={12}>
|
||||
<LinearProgress />
|
||||
</Grid>
|
||||
)}
|
||||
<GenericWizard wizardSteps={filteredWizardSteps} />
|
||||
</ModalWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -30,6 +30,8 @@ import { ITenant, ITenantsResponse } from "./types";
|
||||
import { niceBytes } from "../../../../common/utils";
|
||||
import DeleteTenant from "./DeleteTenant";
|
||||
import AddTenant from "./AddTenant";
|
||||
import { NewServiceAccount } from "../../Common/CredentialsPrompt/types";
|
||||
import CredentialsPrompt from "../../Common/CredentialsPrompt/CredentialsPrompt";
|
||||
|
||||
interface ITenantsList {
|
||||
classes: any;
|
||||
@@ -90,10 +92,23 @@ const ListTenants = ({ classes }: ITenantsList) => {
|
||||
const [rowsPerPage, setRowsPerPage] = useState<number>(10);
|
||||
const [page, setPage] = useState<number>(0);
|
||||
const [error, setError] = useState<string>("");
|
||||
const [showNewCredentials, setShowNewCredentials] = useState<boolean>(false);
|
||||
const [
|
||||
createdAccount,
|
||||
setCreatedAccount,
|
||||
] = useState<NewServiceAccount | null>(null);
|
||||
|
||||
const closeAddModalAndRefresh = (reloadData: boolean) => {
|
||||
const closeAddModalAndRefresh = (
|
||||
reloadData: boolean,
|
||||
res: NewServiceAccount | null
|
||||
) => {
|
||||
setCreateTenantOpen(false);
|
||||
|
||||
if (res !== null) {
|
||||
setShowNewCredentials(true);
|
||||
setCreatedAccount(res);
|
||||
}
|
||||
|
||||
if (reloadData) {
|
||||
setIsLoading(true);
|
||||
}
|
||||
@@ -112,6 +127,11 @@ const ListTenants = ({ classes }: ITenantsList) => {
|
||||
setDeleteOpen(true);
|
||||
};
|
||||
|
||||
const closeCredentialsModal = () => {
|
||||
setShowNewCredentials(false);
|
||||
setCreatedAccount(null);
|
||||
};
|
||||
|
||||
const handleChangePage = (event: unknown, newPage: number) => {
|
||||
setPage(newPage);
|
||||
};
|
||||
@@ -124,6 +144,10 @@ const ListTenants = ({ classes }: ITenantsList) => {
|
||||
setRowsPerPage(rPP);
|
||||
};
|
||||
|
||||
const openLink = (link: string) => {
|
||||
window.open(link, "_blank");
|
||||
};
|
||||
|
||||
const tableActions = [
|
||||
{ type: "view", to: `/tenants`, sendOnlyId: true },
|
||||
{ type: "delete", onClick: confirmDeleteTenant, sendOnlyId: true },
|
||||
@@ -206,6 +230,16 @@ const ListTenants = ({ classes }: ITenantsList) => {
|
||||
closeDeleteModalAndRefresh={closeDeleteModalAndRefresh}
|
||||
/>
|
||||
)}
|
||||
{showNewCredentials && (
|
||||
<CredentialsPrompt
|
||||
newServiceAccount={createdAccount}
|
||||
open={showNewCredentials}
|
||||
closeModal={() => {
|
||||
closeCredentialsModal();
|
||||
}}
|
||||
entity="Tenant"
|
||||
/>
|
||||
)}
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h6">Tenants</Typography>
|
||||
|
||||
@@ -34,6 +34,7 @@ interface IZonesMultiSelector {
|
||||
label: string;
|
||||
tooltip?: string;
|
||||
classes: any;
|
||||
|
||||
onChange: (elements: IZone[]) => void;
|
||||
}
|
||||
|
||||
@@ -87,9 +88,8 @@ const ZonesMultiSelector = ({
|
||||
}: IZonesMultiSelector) => {
|
||||
const defaultZone: IZone = { name: "", servers: 0, capacity: "", volumes: 0 };
|
||||
|
||||
const [currentElements, setCurrentElements] = useState<IZone[]>([
|
||||
{ ...defaultZone },
|
||||
]);
|
||||
const [currentElements, setCurrentElements] = useState<IZone[]>([]);
|
||||
const [internalCounter, setInternalCounter] = useState<number>(1);
|
||||
const bottomList = createRef<HTMLDivElement>();
|
||||
|
||||
// Use effect to send new values to onChange
|
||||
@@ -97,13 +97,34 @@ const ZonesMultiSelector = ({
|
||||
onChange(currentElements);
|
||||
}, [currentElements]);
|
||||
|
||||
// Use effect to set initial values
|
||||
useEffect(() => {
|
||||
if (currentElements.length === 0 && elements.length === 0) {
|
||||
// Initial Value
|
||||
setCurrentElements([{ ...defaultZone, name: "zone-1" }]);
|
||||
} else if (currentElements.length === 0 && elements.length > 0) {
|
||||
setCurrentElements(elements);
|
||||
setInternalCounter(elements.length);
|
||||
}
|
||||
}, [currentElements, elements]);
|
||||
|
||||
// If the last input is not empty, we add a new one
|
||||
const addEmptyRow = (elementsUp: IZone[]) => {
|
||||
const lastElement = elementsUp[elementsUp.length - 1];
|
||||
if (lastElement.servers !== 0 && lastElement.name !== "") {
|
||||
elementsUp.push({ ...defaultZone });
|
||||
const internalElement = internalCounter + 1;
|
||||
if (
|
||||
lastElement.servers !== 0 &&
|
||||
lastElement.name !== "" &&
|
||||
!isNaN(lastElement.servers)
|
||||
) {
|
||||
elementsUp.push({
|
||||
...defaultZone,
|
||||
name: `zone-${internalElement}`,
|
||||
});
|
||||
const refScroll = bottomList.current;
|
||||
|
||||
setInternalCounter(internalElement);
|
||||
|
||||
if (refScroll) {
|
||||
refScroll.scrollIntoView(false);
|
||||
}
|
||||
@@ -158,6 +179,7 @@ const ZonesMultiSelector = ({
|
||||
<div>
|
||||
<InputBoxWrapper
|
||||
type="number"
|
||||
min="0"
|
||||
id={`${name}-${index.toString()}-servers`}
|
||||
label={""}
|
||||
name={`${name}-${index.toString()}-servers`}
|
||||
|
||||
@@ -23,7 +23,7 @@ export interface IZone {
|
||||
}
|
||||
|
||||
export interface IVolumeConfiguration {
|
||||
size: string;
|
||||
size: number;
|
||||
storage_class: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ const AddZoneModal = ({
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setNumberOfInstances(parseInt(e.target.value));
|
||||
}}
|
||||
label="Volumes per Server"
|
||||
label="Drives per Server"
|
||||
value={numberOfInstances.toString(10)}
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
@@ -6,18 +6,18 @@ const theme = createMuiTheme({
|
||||
light: "#757ce8",
|
||||
main: "#201763",
|
||||
dark: "#362585",
|
||||
contrastText: "#fff"
|
||||
contrastText: "#fff",
|
||||
},
|
||||
secondary: {
|
||||
light: "#ff7961",
|
||||
main: "#f44336",
|
||||
dark: "#ba000d",
|
||||
contrastText: "#000"
|
||||
contrastText: "#000",
|
||||
},
|
||||
error: {
|
||||
light: "#e03a48",
|
||||
main: "#dc1f2e",
|
||||
contrastText: "#ffffff"
|
||||
contrastText: "#ffffff",
|
||||
},
|
||||
grey: {
|
||||
100: "#f0f0f0",
|
||||
@@ -28,39 +28,39 @@ const theme = createMuiTheme({
|
||||
600: "#737373",
|
||||
700: "#666666",
|
||||
800: "#4d4d4d",
|
||||
900: "#333333"
|
||||
900: "#333333",
|
||||
},
|
||||
background: {
|
||||
default: "#F4F4F4"
|
||||
}
|
||||
default: "#F4F4F4",
|
||||
},
|
||||
},
|
||||
typography: {
|
||||
fontFamily: ["Lato", "sans-serif"].join(","),
|
||||
h1: {
|
||||
fontWeight: "bold",
|
||||
color: "#201763"
|
||||
color: "#201763",
|
||||
},
|
||||
h2: {
|
||||
fontWeight: "bold",
|
||||
color: "#201763"
|
||||
color: "#201763",
|
||||
},
|
||||
h3: {
|
||||
fontWeight: "bold",
|
||||
color: "#201763"
|
||||
color: "#201763",
|
||||
},
|
||||
h4: {
|
||||
fontWeight: "bold",
|
||||
color: "#201763"
|
||||
color: "#201763",
|
||||
},
|
||||
h5: {
|
||||
fontWeight: "bold",
|
||||
color: "#201763"
|
||||
color: "#201763",
|
||||
},
|
||||
h6: {
|
||||
fontWeight: "bold",
|
||||
color: "#000000"
|
||||
}
|
||||
}
|
||||
color: "#000000",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default theme;
|
||||
|
||||
66
portal-ui/src/theme/newtheme.ts
Normal file
66
portal-ui/src/theme/newtheme.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { createMuiTheme } from "@material-ui/core";
|
||||
|
||||
const newTheme = createMuiTheme({
|
||||
palette: {
|
||||
primary: {
|
||||
light: "#0c4453",
|
||||
main: "#01262e",
|
||||
dark: "#001115",
|
||||
contrastText: "#fff",
|
||||
},
|
||||
secondary: {
|
||||
light: "#ff7961",
|
||||
main: "#f44336",
|
||||
dark: "#01262E",
|
||||
contrastText: "#000",
|
||||
},
|
||||
error: {
|
||||
light: "#e03a48",
|
||||
main: "#dc1f2e",
|
||||
contrastText: "#ffffff",
|
||||
},
|
||||
grey: {
|
||||
100: "#F7F7F7",
|
||||
200: "#D8DDDE",
|
||||
300: "#BAC3C5",
|
||||
400: "#9BA9AC",
|
||||
500: "#7C8F93",
|
||||
600: "#5D7479",
|
||||
700: "#3F5A60",
|
||||
800: "#204047",
|
||||
900: "#01262E",
|
||||
},
|
||||
background: {
|
||||
default: "#F4F4F4",
|
||||
},
|
||||
},
|
||||
typography: {
|
||||
fontFamily: ["Lato", "sans-serif"].join(","),
|
||||
h1: {
|
||||
fontWeight: "bold",
|
||||
color: "#01262E",
|
||||
},
|
||||
h2: {
|
||||
fontWeight: "bold",
|
||||
color: "#01262E",
|
||||
},
|
||||
h3: {
|
||||
fontWeight: "bold",
|
||||
color: "#01262E",
|
||||
},
|
||||
h4: {
|
||||
fontWeight: "bold",
|
||||
color: "#01262E",
|
||||
},
|
||||
h5: {
|
||||
fontWeight: "bold",
|
||||
color: "#01262E",
|
||||
},
|
||||
h6: {
|
||||
fontWeight: "bold",
|
||||
color: "#01262E",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default newTheme;
|
||||
Reference in New Issue
Block a user