-
Pod Affinity
+
Pod Placement
Configure how pods will be assigned to nodes
@@ -208,11 +208,11 @@ const Affinity = ({
}}
selectorOptions={[
{ label: "None", value: "none" },
- { label: "Default (Pod Anti-afinnity)", value: "default" },
+ { label: "Default (Pod Anti-Affinnity)", value: "default" },
{ label: "Node Selector", value: "nodeSelector" },
]}
/>
- MinIO supports multiple configurations for Pod Afinnity
+ MinIO supports multiple configurations for Pod Affinity
{podAffinity === "nodeSelector" && (
@@ -371,7 +371,7 @@ const Affinity = ({
)}
-
+
);
};
diff --git a/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Configure.tsx b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Configure.tsx
index 54f2bbb60..dcced5cc7 100644
--- a/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Configure.tsx
+++ b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Configure.tsx
@@ -17,7 +17,7 @@
import React, { Fragment, useCallback, useEffect, useState } from "react";
import { connect } from "react-redux";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
-import { Grid } from "@material-ui/core";
+import { Grid, Paper } from "@material-ui/core";
import {
modalBasic,
wizardCommon,
@@ -304,208 +304,13 @@ const Configure = ({
};
return (
-
+
Configure
Basic configurations for tenant management
-
-
- {
- const targetD = e.target;
- const checked = targetD.checked;
- updateField("customImage", checked);
- }}
- label={"Use custom image"}
- />
-
- {customImage && (
-
- Please enter the MinIO docker image to use
-
- ) => {
- updateField("imageName", e.target.value);
- cleanValidation("image");
- }}
- label="MinIO's Image"
- value={imageName}
- error={validationErrors["image"] || ""}
- placeholder="E.g. minio/minio:RELEASE.2021-08-20T18-32-01Z"
- />
-
-
- ) => {
- updateField("logSearchImage", e.target.value);
- cleanValidation("logSearchImage");
- }}
- label="Log Search API's Image"
- value={logSearchImage}
- error={validationErrors["logSearchImage"] || ""}
- placeholder="E.g. minio/logsearchapi:v4.1.1"
- />
-
-
- ) => {
- updateField("kesImage", e.target.value);
- cleanValidation("kesImage");
- }}
- label="KES Image"
- value={kesImage}
- error={validationErrors["kesImage"] || ""}
- placeholder="E.g. minio/kes:v0.14.0"
- />
-
-
- ) => {
- updateField("logSearchPostgresImage", e.target.value);
- cleanValidation("logSearchPostgresImage");
- }}
- label="Log Search Postgres's Image"
- value={logSearchPostgresImage}
- error={validationErrors["logSearchPostgresImage"] || ""}
- placeholder="E.g. library/postgres:13"
- />
-
-
- ) => {
- updateField("logSearchPostgresInitImage", e.target.value);
- cleanValidation("logSearchPostgresInitImage");
- }}
- label="Log Search Postgres's Init Image"
- value={logSearchPostgresInitImage}
- error={validationErrors["logSearchPostgresInitImage"] || ""}
- placeholder="E.g. library/busybox:1.33.1"
- />
-
-
- ) => {
- updateField("prometheusImage", e.target.value);
- cleanValidation("prometheusImage");
- }}
- label="Prometheus Image"
- value={prometheusImage}
- error={validationErrors["prometheusImage"] || ""}
- placeholder="E.g. quay.io/prometheus/prometheus:latest"
- />
-
-
- ) => {
- updateField("prometheusSidecarImage", e.target.value);
- cleanValidation("prometheusSidecarImage");
- }}
- label="Prometheus Sidecar Image"
- value={prometheusSidecarImage}
- error={validationErrors["prometheusSidecarImage"] || ""}
- placeholder="E.g. quay.io/prometheus/prometheus:latest"
- />
-
-
- ) => {
- updateField("prometheusInitImage", e.target.value);
- cleanValidation("prometheusInitImage");
- }}
- label="Prometheus Init Image"
- value={prometheusInitImage}
- error={validationErrors["prometheusInitImage"] || ""}
- placeholder="E.g. quay.io/prometheus/prometheus:latest"
- />
-
-
- )}
- {customImage && (
-
-
- {
- const targetD = e.target;
- const checked = targetD.checked;
-
- updateField("customDockerhub", checked);
- }}
- label={"Set/Update Image Registry"}
- />
-
-
- )}
- {customDockerhub && (
-
-
- ) => {
- updateField("imageRegistry", e.target.value);
- }}
- label="Endpoint"
- value={imageRegistry}
- error={validationErrors["registry"] || ""}
- placeholder="E.g. https://index.docker.io/v1/"
- required
- />
-
-
- ) => {
- updateField("imageRegistryUsername", e.target.value);
- }}
- label="Username"
- value={imageRegistryUsername}
- error={validationErrors["registryUsername"] || ""}
- required
- />
-
-
- ) => {
- updateField("imageRegistryPassword", e.target.value);
- }}
- label="Password"
- value={imageRegistryPassword}
- error={validationErrors["registryPassword"] || ""}
- required
- />
-
-
- )}
Expose Services
@@ -662,7 +467,7 @@ const Configure = ({
)}
-
+
);
};
diff --git a/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Encryption.tsx b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Encryption.tsx
index 6e7cfc7bb..221c5f09a 100644
--- a/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Encryption.tsx
+++ b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Encryption.tsx
@@ -17,7 +17,7 @@
import React, { Fragment, useState, useEffect, useCallback } from "react";
import { connect } from "react-redux";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
-import { Typography } from "@material-ui/core";
+import { Paper, Typography } from "@material-ui/core";
import Grid from "@material-ui/core/Grid";
import {
updateAddField,
@@ -328,7 +328,7 @@ const Encryption = ({
]);
return (
-
+
Encryption
@@ -862,7 +862,7 @@ const Encryption = ({
)}
)}
-
+
);
};
diff --git a/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/IdentityProvider.tsx b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/IdentityProvider.tsx
index b0d59e275..ce7c57c13 100644
--- a/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/IdentityProvider.tsx
+++ b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/IdentityProvider.tsx
@@ -17,7 +17,13 @@
import React, { Fragment, useCallback, useEffect, useState } from "react";
import { connect } from "react-redux";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
-import { Grid, IconButton, Tooltip, Typography } from "@material-ui/core";
+import {
+ Grid,
+ IconButton,
+ Paper,
+ Tooltip,
+ Typography,
+} from "@material-ui/core";
import CasinoIcon from "@material-ui/icons/Casino";
import DeleteIcon from "@material-ui/icons/Delete";
import {
@@ -378,7 +384,7 @@ const IdentityProvider = ({
});
}
return (
-
+
Identity Provider
@@ -696,7 +702,7 @@ const IdentityProvider = ({
)}
-
+
);
};
diff --git a/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Images.tsx b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Images.tsx
new file mode 100644
index 000000000..491779c01
--- /dev/null
+++ b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Images.tsx
@@ -0,0 +1,543 @@
+// This file is part of MinIO Console Server
+// Copyright (c) 2021 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 .
+
+import React, { Fragment, useCallback, useEffect, useState } from "react";
+import { connect } from "react-redux";
+import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
+import { Grid, Paper } from "@material-ui/core";
+import {
+ modalBasic,
+ wizardCommon,
+} from "../../../Common/FormComponents/common/styleLibrary";
+import { isPageValid, updateAddField } from "../../actions";
+import { AppState } from "../../../../../store";
+import { clearValidationError } from "../../utils";
+import {
+ commonFormValidation,
+ IValidation,
+} from "../../../../../utils/validationFunctions";
+import FormSwitchWrapper from "../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
+import InputBoxWrapper from "../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
+
+interface IImagesProps {
+ updateAddField: typeof updateAddField;
+ isPageValid: typeof isPageValid;
+ storageClasses: any;
+ classes: any;
+ customImage: boolean;
+ imageName: string;
+ customDockerhub: boolean;
+ imageRegistry: string;
+ imageRegistryUsername: string;
+ imageRegistryPassword: string;
+ exposeMinIO: boolean;
+ exposeConsole: boolean;
+ prometheusCustom: boolean;
+ logSearchCustom: boolean;
+ logSearchVolumeSize: string;
+ logSearchSizeFactor: string;
+ prometheusVolumeSize: string;
+ prometheusSizeFactor: string;
+ logSearchSelectedStorageClass: string;
+ logSearchImage: string;
+ kesImage: string;
+ logSearchPostgresImage: string;
+ logSearchPostgresInitImage: string;
+ prometheusSelectedStorageClass: string;
+ prometheusImage: string;
+ prometheusSidecarImage: string;
+ prometheusInitImage: string;
+ selectedStorageClass: string;
+}
+
+const styles = (theme: Theme) =>
+ createStyles({
+ buttonContainer: {
+ textAlign: "right",
+ },
+ ...modalBasic,
+ ...wizardCommon,
+ });
+
+const Images = ({
+ classes,
+ storageClasses,
+ customImage,
+ imageName,
+ customDockerhub,
+ imageRegistry,
+ imageRegistryUsername,
+ imageRegistryPassword,
+ exposeMinIO,
+ exposeConsole,
+ prometheusCustom,
+ logSearchCustom,
+ logSearchVolumeSize,
+ logSearchSizeFactor,
+ logSearchImage,
+ kesImage,
+ logSearchPostgresImage,
+ logSearchPostgresInitImage,
+ prometheusVolumeSize,
+ prometheusSizeFactor,
+ logSearchSelectedStorageClass,
+ prometheusSelectedStorageClass,
+ prometheusImage,
+ prometheusSidecarImage,
+ prometheusInitImage,
+ updateAddField,
+ isPageValid,
+ selectedStorageClass,
+}: IImagesProps) => {
+ const [validationErrors, setValidationErrors] = useState({});
+
+ // Common
+ const updateField = useCallback(
+ (field: string, value: any) => {
+ updateAddField("configure", field, value);
+ },
+ [updateAddField]
+ );
+
+ // Validation
+ useEffect(() => {
+ let customAccountValidation: IValidation[] = [];
+
+ if (prometheusCustom) {
+ customAccountValidation = [
+ ...customAccountValidation,
+ {
+ fieldKey: "prometheus_storage_class",
+ required: true,
+ value: prometheusSelectedStorageClass,
+ customValidation: prometheusSelectedStorageClass === "",
+ customValidationMessage: "Field cannot be empty",
+ },
+ {
+ fieldKey: "prometheus_volume_size",
+ required: true,
+ value: prometheusVolumeSize,
+ customValidation:
+ prometheusVolumeSize === "" || parseInt(prometheusVolumeSize) <= 0,
+ customValidationMessage: `Volume size must be present and be greatter than 0`,
+ },
+ ];
+ }
+ if (logSearchCustom) {
+ customAccountValidation = [
+ ...customAccountValidation,
+ {
+ fieldKey: "log_search_storage_class",
+ required: true,
+ value: logSearchSelectedStorageClass,
+ customValidation: logSearchSelectedStorageClass === "",
+ customValidationMessage: "Field cannot be empty",
+ },
+ {
+ fieldKey: "log_search_volume_size",
+ required: true,
+ value: logSearchVolumeSize,
+ customValidation:
+ logSearchVolumeSize === "" || parseInt(logSearchVolumeSize) <= 0,
+ customValidationMessage: `Volume size must be present and be greatter than 0`,
+ },
+ ];
+ }
+
+ if (customImage) {
+ customAccountValidation = [
+ ...customAccountValidation,
+ {
+ fieldKey: "image",
+ required: false,
+ value: imageName,
+ pattern: /^((.*?)\/(.*?):(.+))$/,
+ customPatternMessage: "Format must be of form: 'minio/minio:VERSION'",
+ },
+ {
+ fieldKey: "logSearchImage",
+ required: false,
+ value: logSearchImage,
+ pattern: /^((.*?)\/(.*?):(.+))$/,
+ customPatternMessage:
+ "Format must be of form: 'minio/logsearchapi:VERSION'",
+ },
+ {
+ fieldKey: "kesImage",
+ required: false,
+ value: kesImage,
+ pattern: /^((.*?)\/(.*?):(.+))$/,
+ customPatternMessage: "Format must be of form: 'minio/kes:VERSION'",
+ },
+ {
+ fieldKey: "logSearchPostgresImage",
+ required: false,
+ value: logSearchPostgresImage,
+ pattern: /^((.*?)\/(.*?):(.+))$/,
+ customPatternMessage:
+ "Format must be of form: 'library/postgres:VERSION'",
+ },
+ {
+ fieldKey: "logSearchPostgresInitImage",
+ required: false,
+ value: logSearchPostgresInitImage,
+ pattern: /^((.*?)\/(.*?):(.+))$/,
+ customPatternMessage:
+ "Format must be of form: 'library/busybox:VERSION'",
+ },
+ {
+ fieldKey: "prometheusImage",
+ required: false,
+ value: prometheusImage,
+ pattern: /^((.*?)\/(.*?):(.+))$/,
+ customPatternMessage:
+ "Format must be of form: 'minio/prometheus:VERSION'",
+ },
+ {
+ fieldKey: "prometheusSidecarImage",
+ required: false,
+ value: prometheusSidecarImage,
+ pattern: /^((.*?)\/(.*?):(.+))$/,
+ customPatternMessage:
+ "Format must be of form: 'project/container:VERSION'",
+ },
+ {
+ fieldKey: "prometheusInitImage",
+ required: false,
+ value: prometheusInitImage,
+ pattern: /^((.*?)\/(.*?):(.+))$/,
+ customPatternMessage:
+ "Format must be of form: 'library/busybox:VERSION'",
+ },
+ ];
+ if (customDockerhub) {
+ customAccountValidation = [
+ ...customAccountValidation,
+ {
+ fieldKey: "registry",
+ required: true,
+ value: imageRegistry,
+ },
+ {
+ fieldKey: "registryUsername",
+ required: true,
+ value: imageRegistryUsername,
+ },
+ {
+ fieldKey: "registryPassword",
+ required: true,
+ value: imageRegistryPassword,
+ },
+ ];
+ }
+ }
+
+ const commonVal = commonFormValidation(customAccountValidation);
+
+ isPageValid("configure", Object.keys(commonVal).length === 0);
+
+ setValidationErrors(commonVal);
+ }, [
+ customImage,
+ imageName,
+ logSearchImage,
+ kesImage,
+ logSearchPostgresImage,
+ logSearchPostgresInitImage,
+ prometheusImage,
+ prometheusSidecarImage,
+ prometheusInitImage,
+ customDockerhub,
+ imageRegistry,
+ imageRegistryUsername,
+ imageRegistryPassword,
+ isPageValid,
+ prometheusCustom,
+ logSearchCustom,
+ prometheusSelectedStorageClass,
+ prometheusVolumeSize,
+ logSearchSelectedStorageClass,
+ logSearchVolumeSize,
+ ]);
+
+ useEffect(() => {
+ // New default values is current selection is invalid
+ if (storageClasses.length > 0) {
+ const filterPrometheus = storageClasses.filter(
+ (item: any) => item.value === prometheusSelectedStorageClass
+ );
+ if (filterPrometheus.length === 0) {
+ updateField("prometheusSelectedStorageClass", selectedStorageClass);
+ }
+
+ const filterLogSearch = storageClasses.filter(
+ (item: any) => item.value === logSearchSelectedStorageClass
+ );
+ if (filterLogSearch.length === 0) {
+ updateField("logSearchSelectedStorageClass", selectedStorageClass);
+ }
+ }
+ }, [
+ logSearchSelectedStorageClass,
+ prometheusSelectedStorageClass,
+ selectedStorageClass,
+ storageClasses,
+ updateField,
+ ]);
+
+ const cleanValidation = (fieldName: string) => {
+ setValidationErrors(clearValidationError(validationErrors, fieldName));
+ };
+
+ return (
+
+
+
Container Images
+
+ Images used by the Tenant Deployment
+
+
+
+
+
+ ) => {
+ updateField("imageName", e.target.value);
+ cleanValidation("image");
+ }}
+ label="MinIO's Image"
+ value={imageName}
+ error={validationErrors["image"] || ""}
+ placeholder="E.g. minio/minio:RELEASE.2021-08-20T18-32-01Z"
+ />
+
+
+ ) => {
+ updateField("logSearchImage", e.target.value);
+ cleanValidation("logSearchImage");
+ }}
+ label="Log Search API's Image"
+ value={logSearchImage}
+ error={validationErrors["logSearchImage"] || ""}
+ placeholder="E.g. minio/logsearchapi:v4.1.1"
+ />
+
+
+ ) => {
+ updateField("kesImage", e.target.value);
+ cleanValidation("kesImage");
+ }}
+ label="KES Image"
+ value={kesImage}
+ error={validationErrors["kesImage"] || ""}
+ placeholder="E.g. minio/kes:v0.14.0"
+ />
+
+
+ ) => {
+ updateField("logSearchPostgresImage", e.target.value);
+ cleanValidation("logSearchPostgresImage");
+ }}
+ label="Log Search Postgres's Image"
+ value={logSearchPostgresImage}
+ error={validationErrors["logSearchPostgresImage"] || ""}
+ placeholder="E.g. library/postgres:13"
+ />
+
+
+ ) => {
+ updateField("logSearchPostgresInitImage", e.target.value);
+ cleanValidation("logSearchPostgresInitImage");
+ }}
+ label="Log Search Postgres's Init Image"
+ value={logSearchPostgresInitImage}
+ error={validationErrors["logSearchPostgresInitImage"] || ""}
+ placeholder="E.g. library/busybox:1.33.1"
+ />
+
+
+ ) => {
+ updateField("prometheusImage", e.target.value);
+ cleanValidation("prometheusImage");
+ }}
+ label="Prometheus Image"
+ value={prometheusImage}
+ error={validationErrors["prometheusImage"] || ""}
+ placeholder="E.g. quay.io/prometheus/prometheus:latest"
+ />
+
+
+ ) => {
+ updateField("prometheusSidecarImage", e.target.value);
+ cleanValidation("prometheusSidecarImage");
+ }}
+ label="Prometheus Sidecar Image"
+ value={prometheusSidecarImage}
+ error={validationErrors["prometheusSidecarImage"] || ""}
+ placeholder="E.g. quay.io/prometheus/prometheus:latest"
+ />
+
+
+ ) => {
+ updateField("prometheusInitImage", e.target.value);
+ cleanValidation("prometheusInitImage");
+ }}
+ label="Prometheus Init Image"
+ value={prometheusInitImage}
+ error={validationErrors["prometheusInitImage"] || ""}
+ placeholder="E.g. quay.io/prometheus/prometheus:latest"
+ />
+
+
+
+ {customImage && (
+
+
+ {
+ const targetD = e.target;
+ const checked = targetD.checked;
+
+ updateField("customDockerhub", checked);
+ }}
+ label={"Set/Update Image Registry"}
+ />
+
+
+ )}
+ {customDockerhub && (
+
+
+ ) => {
+ updateField("imageRegistry", e.target.value);
+ }}
+ label="Endpoint"
+ value={imageRegistry}
+ error={validationErrors["registry"] || ""}
+ placeholder="E.g. https://index.docker.io/v1/"
+ required
+ />
+
+
+ ) => {
+ updateField("imageRegistryUsername", e.target.value);
+ }}
+ label="Username"
+ value={imageRegistryUsername}
+ error={validationErrors["registryUsername"] || ""}
+ required
+ />
+
+
+ ) => {
+ updateField("imageRegistryPassword", e.target.value);
+ }}
+ label="Password"
+ value={imageRegistryPassword}
+ error={validationErrors["registryPassword"] || ""}
+ required
+ />
+
+
+ )}
+
+ );
+};
+
+const mapState = (state: AppState) => ({
+ storageClasses: state.tenants.createTenant.storageClasses,
+ customImage: state.tenants.createTenant.fields.configure.customImage,
+ imageName: state.tenants.createTenant.fields.configure.imageName,
+ customDockerhub: state.tenants.createTenant.fields.configure.customDockerhub,
+ imageRegistry: state.tenants.createTenant.fields.configure.imageRegistry,
+ imageRegistryUsername:
+ state.tenants.createTenant.fields.configure.imageRegistryUsername,
+ imageRegistryPassword:
+ state.tenants.createTenant.fields.configure.imageRegistryPassword,
+ exposeMinIO: state.tenants.createTenant.fields.configure.exposeMinIO,
+ exposeConsole: state.tenants.createTenant.fields.configure.exposeConsole,
+ prometheusCustom:
+ state.tenants.createTenant.fields.configure.prometheusCustom,
+ logSearchCustom: state.tenants.createTenant.fields.configure.logSearchCustom,
+ logSearchVolumeSize:
+ state.tenants.createTenant.fields.configure.logSearchVolumeSize,
+ logSearchSizeFactor:
+ state.tenants.createTenant.fields.configure.logSearchSizeFactor,
+ prometheusVolumeSize:
+ state.tenants.createTenant.fields.configure.prometheusVolumeSize,
+ prometheusSizeFactor:
+ state.tenants.createTenant.fields.configure.prometheusSizeFactor,
+ logSearchSelectedStorageClass:
+ state.tenants.createTenant.fields.configure.logSearchSelectedStorageClass,
+ logSearchImage: state.tenants.createTenant.fields.configure.logSearchImage,
+ kesImage: state.tenants.createTenant.fields.configure.kesImage,
+ logSearchPostgresImage:
+ state.tenants.createTenant.fields.configure.logSearchPostgresImage,
+ logSearchPostgresInitImage:
+ state.tenants.createTenant.fields.configure.logSearchPostgresInitImage,
+ prometheusSelectedStorageClass:
+ state.tenants.createTenant.fields.configure.prometheusSelectedStorageClass,
+ prometheusImage: state.tenants.createTenant.fields.configure.prometheusImage,
+ prometheusSidecarImage:
+ state.tenants.createTenant.fields.configure.prometheusSidecarImage,
+ prometheusInitImage:
+ state.tenants.createTenant.fields.configure.prometheusInitImage,
+ selectedStorageClass:
+ state.tenants.createTenant.fields.nameTenant.selectedStorageClass,
+});
+
+const connector = connect(mapState, {
+ updateAddField,
+ isPageValid,
+});
+
+export default withStyles(styles)(connector(Images));
diff --git a/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/NameTenant.tsx b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/NameTenant.tsx
index 6e5d859ee..f0f2fd777 100644
--- a/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/NameTenant.tsx
+++ b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/NameTenant.tsx
@@ -14,7 +14,13 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
-import React, { useEffect, useState, useMemo, useCallback } from "react";
+import React, {
+ Fragment,
+ useCallback,
+ useEffect,
+ useMemo,
+ useState,
+} from "react";
import { connect } from "react-redux";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import get from "lodash/get";
@@ -26,17 +32,16 @@ import {
} from "../../../Common/FormComponents/common/styleLibrary";
import { setModalErrorSnackMessage } from "../../../../../actions";
import {
- setAdvancedMode,
- updateAddField,
isPageValid,
- setStorageClassesList,
setLimitSize,
+ setStorageClassesList,
+ updateAddField,
} from "../../actions";
import {
+ getLimitSizes,
IQuotaElement,
IQuotas,
Opts,
- getLimitSizes,
} from "../../ListTenants/utils";
import { AppState } from "../../../../../store";
import { commonFormValidation } from "../../../../../utils/validationFunctions";
@@ -45,15 +50,20 @@ import { ErrorResponseHandler } from "../../../../../common/types";
import api from "../../../../../common/api";
import InputBoxWrapper from "../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import SelectWrapper from "../../../Common/FormComponents/SelectWrapper/SelectWrapper";
-import FormSwitchWrapper from "../../../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
import AddIcon from "../../../../../icons/AddIcon";
import AddNamespaceModal from "./helpers/AddNamespaceModal";
+import SizePreview from "./SizePreview";
+import TenantSize from "./TenantSize";
+import { Paper } from "@material-ui/core";
const styles = (theme: Theme) =>
createStyles({
buttonContainer: {
textAlign: "right",
},
+ sizePreview: {
+ position: "fixed",
+ },
...modalBasic,
...wizardCommon,
});
@@ -62,7 +72,6 @@ interface INameTenantScreen {
classes: any;
storageClasses: Opts[];
setModalErrorSnackMessage: typeof setModalErrorSnackMessage;
- setAdvancedMode: typeof setAdvancedMode;
updateAddField: typeof updateAddField;
isPageValid: typeof isPageValid;
setStorageClassesList: typeof setStorageClassesList;
@@ -70,17 +79,14 @@ interface INameTenantScreen {
tenantName: string;
namespace: string;
selectedStorageClass: string;
- advancedMode: boolean;
}
const NameTenant = ({
classes,
storageClasses,
- advancedMode,
tenantName,
namespace,
selectedStorageClass,
- setAdvancedMode,
updateAddField,
setStorageClassesList,
setLimitSize,
@@ -250,7 +256,7 @@ const NameTenant = ({
};
return (
-
+
{openAddNSConfirm && (
)}
-
-
Name Tenant
-
- How would you like to name this new tenant?
-
-
-
- ) => {
- updateField("tenantName", e.target.value);
- frmValidationCleanup("tenant-name");
- }}
- label="Name"
- value={tenantName}
- required
- error={validationErrors["tenant-name"] || ""}
- />
+
+
+
+
+
+
+
Name Tenant
+
+ How would you like to name this new tenant?
+
+
+ ) => {
+ updateField("tenantName", e.target.value);
+ frmValidationCleanup("tenant-name");
+ }}
+ label="Name"
+ value={tenantName}
+ required
+ error={validationErrors["tenant-name"] || ""}
+ />
+
+
+ ) => {
+ updateField("namespace", e.target.value);
+ frmValidationCleanup("namespace");
+ }}
+ label="Namespace"
+ value={namespace}
+ error={validationErrors["namespace"] || ""}
+ overlayIcon={showCreateButton ? : null}
+ overlayAction={addNamespace}
+ required
+ />
+
+
+ ) => {
+ updateField(
+ "selectedStorageClass",
+ e.target.value as string
+ );
+ }}
+ label="Storage Class"
+ value={selectedStorageClass}
+ options={storageClasses}
+ disabled={storageClasses.length < 1}
+ />
+
+
+
+
+
+
+
+
+
+
-
- ) => {
- updateField("namespace", e.target.value);
- frmValidationCleanup("namespace");
- }}
- label="Namespace"
- value={namespace}
- error={validationErrors["namespace"] || ""}
- overlayIcon={showCreateButton ? : null}
- overlayAction={addNamespace}
- required
- />
-
-
- ) => {
- updateField("selectedStorageClass", e.target.value as string);
- }}
- label="Storage Class"
- value={selectedStorageClass}
- options={storageClasses}
- disabled={storageClasses.length < 1}
- />
-
-
-
-
- Check 'Advanced Mode' for additional configuration options, such as
- configuring an Identity Provider, Encryption at rest, and customized
- TLS/SSL Certificates.
-
- Leave 'Advanced Mode' unchecked to use the secure default settings for
- the tenant.
-
-
-
- {
- const targetD = e.target;
- const checked = targetD.checked;
-
- setAdvancedMode(checked);
- }}
- label={"Advanced Mode"}
- />
-
-
+
);
};
const mapState = (state: AppState) => ({
- advancedMode: state.tenants.createTenant.advancedModeOn,
tenantName: state.tenants.createTenant.fields.nameTenant.tenantName,
namespace: state.tenants.createTenant.fields.nameTenant.namespace,
selectedStorageClass:
@@ -348,7 +344,6 @@ const mapState = (state: AppState) => ({
const connector = connect(mapState, {
setModalErrorSnackMessage,
- setAdvancedMode,
updateAddField,
setStorageClassesList,
setLimitSize,
diff --git a/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Preview.tsx b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Preview.tsx
index b62bf668a..d1c701d8c 100644
--- a/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Preview.tsx
+++ b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Preview.tsx
@@ -26,6 +26,7 @@ import {
modalBasic,
wizardCommon,
} from "../../../Common/FormComponents/common/styleLibrary";
+import { Paper } from "@material-ui/core";
interface IPreviewProps {
classes: any;
@@ -62,7 +63,7 @@ const Preview = ({
enableTLS,
}: IPreviewProps) => {
return (
-
+
Review
@@ -125,7 +126,7 @@ const Preview = ({
)}
-
+
);
};
diff --git a/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Security.tsx b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Security.tsx
index 75ad3b66f..2b1ccd4ae 100644
--- a/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Security.tsx
+++ b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/Security.tsx
@@ -17,7 +17,7 @@
import React, { useEffect, useCallback, Fragment } from "react";
import { connect } from "react-redux";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
-import { Button, Divider, Grid, Typography } from "@material-ui/core";
+import { Button, Divider, Grid, Paper, Typography } from "@material-ui/core";
import {
modalBasic,
wizardCommon,
@@ -106,7 +106,7 @@ const Security = ({
}, [enableTLS, enableAutoCert, enableCustomCerts, isPageValid]);
return (
-
+
Security
@@ -286,7 +286,7 @@ const Security = ({
)}
)}
-
+
);
};
diff --git a/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/SizePreview.tsx b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/SizePreview.tsx
new file mode 100644
index 000000000..5ed220a4d
--- /dev/null
+++ b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/SizePreview.tsx
@@ -0,0 +1,405 @@
+// This file is part of MinIO Console Server
+// Copyright (c) 2021 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 .
+
+import React, { Fragment, useCallback, useEffect, useState } from "react";
+import { connect } from "react-redux";
+import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
+import { AppState } from "../../../../../store";
+import { isPageValid, updateAddField } from "../../actions";
+import {
+ modalBasic,
+ wizardCommon,
+} from "../../../Common/FormComponents/common/styleLibrary";
+import Table from "@material-ui/core/Table";
+import TableBody from "@material-ui/core/TableBody";
+import TableCell from "@material-ui/core/TableCell";
+import TableRow from "@material-ui/core/TableRow";
+import {
+ calculateDistribution,
+ erasureCodeCalc,
+ getBytes,
+ niceBytes,
+ setMemoryResource,
+} from "../../../../../common/utils";
+import { ecListTransform, Opts } from "../../ListTenants/utils";
+import { IMemorySize } from "../../ListTenants/types";
+import {
+ ErrorResponseHandler,
+ ICapacity,
+ IErasureCodeCalc,
+} from "../../../../../common/types";
+import { commonFormValidation } from "../../../../../utils/validationFunctions";
+import api from "../../../../../common/api";
+import { Divider } from "@material-ui/core";
+
+interface ISizePreviewProps {
+ classes: any;
+ updateAddField: typeof updateAddField;
+ isPageValid: typeof isPageValid;
+ advancedMode: boolean;
+ volumeSize: string;
+ sizeFactor: string;
+ drivesPerServer: string;
+ nodes: string;
+ memoryNode: string;
+ ecParity: string;
+ ecParityChoices: Opts[];
+ cleanECChoices: string[];
+ maxAllocableMemo: number;
+ memorySize: IMemorySize;
+ distribution: any;
+ ecParityCalc: IErasureCodeCalc;
+ limitSize: any;
+ selectedStorageClass: string;
+}
+
+const styles = (theme: Theme) =>
+ createStyles({
+ buttonContainer: {
+ textAlign: "right",
+ },
+ root: {
+ margin: 4,
+ },
+ table: {
+ "& .MuiTableCell-root": {
+ fontSize: 13,
+ },
+ },
+ ...modalBasic,
+ ...wizardCommon,
+ });
+
+const SizePreview = ({
+ classes,
+ updateAddField,
+ isPageValid,
+ advancedMode,
+ volumeSize,
+ sizeFactor,
+ drivesPerServer,
+ nodes,
+ memoryNode,
+ ecParity,
+ ecParityChoices,
+ cleanECChoices,
+ maxAllocableMemo,
+ memorySize,
+ distribution,
+ ecParityCalc,
+ limitSize,
+ selectedStorageClass,
+}: ISizePreviewProps) => {
+ const [errorFlag, setErrorFlag] = useState(false);
+ const [nodeError, setNodeError] = useState("");
+ const usableInformation = ecParityCalc.storageFactors.find(
+ (element) => element.erasureCode === ecParity
+ );
+
+ // Common
+ const updateField = useCallback(
+ (field: string, value: any) => {
+ updateAddField("tenantSize", field, value);
+ },
+ [updateAddField]
+ );
+
+ /*Debounce functions*/
+
+ // Storage Quotas
+
+ const validateMemorySize = useCallback(() => {
+ const memSize = parseInt(memoryNode) || 0;
+ const clusterSize = volumeSize || 0;
+ const maxMemSize = maxAllocableMemo || 0;
+ const clusterSizeFactor = sizeFactor;
+
+ const clusterSizeBytes = getBytes(
+ clusterSize.toString(10),
+ clusterSizeFactor
+ );
+ const memoSize = setMemoryResource(memSize, clusterSizeBytes, maxMemSize);
+ updateField("memorySize", memoSize);
+ }, [maxAllocableMemo, memoryNode, sizeFactor, updateField, volumeSize]);
+
+ const getMaxAllocableMemory = (nodes: string) => {
+ if (nodes !== "" && !isNaN(parseInt(nodes))) {
+ setNodeError("");
+ api
+ .invoke(
+ "GET",
+ `/api/v1/cluster/max-allocatable-memory?num_nodes=${nodes}`
+ )
+ .then((res: { max_memory: number }) => {
+ const maxMemory = res.max_memory ? res.max_memory : 0;
+ updateField("maxAllocableMemo", maxMemory);
+ })
+ .catch((err: ErrorResponseHandler) => {
+ setErrorFlag(true);
+ setNodeError(err.errorMessage);
+ });
+ }
+ };
+
+ useEffect(() => {
+ validateMemorySize();
+ }, [memoryNode, validateMemorySize]);
+
+ useEffect(() => {
+ validateMemorySize();
+ }, [maxAllocableMemo, validateMemorySize]);
+
+ useEffect(() => {
+ if (ecParityChoices.length > 0 && distribution.error === "") {
+ const ecCodeValidated = erasureCodeCalc(
+ cleanECChoices,
+ distribution.persistentVolumes,
+ distribution.pvSize,
+ distribution.nodes
+ );
+
+ updateField("ecParityCalc", ecCodeValidated);
+ updateField("ecParity", ecCodeValidated.defaultEC);
+ }
+ }, [ecParityChoices.length, distribution, cleanECChoices, updateField]);
+ /*End debounce functions*/
+
+ /*Calculate Allocation*/
+ useEffect(() => {
+ validateClusterSize();
+ getECValue();
+ getMaxAllocableMemory(nodes);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [nodes, volumeSize, sizeFactor, drivesPerServer]);
+
+ const validateClusterSize = () => {
+ const size = volumeSize;
+ const factor = sizeFactor;
+ const limitSize = getBytes("12", "Ti", true);
+
+ const clusterCapacity: ICapacity = {
+ unit: factor,
+ value: size.toString(),
+ };
+
+ const distrCalculate = calculateDistribution(
+ clusterCapacity,
+ parseInt(nodes),
+ parseInt(limitSize),
+ parseInt(drivesPerServer)
+ );
+
+ updateField("distribution", distrCalculate);
+ };
+
+ const getECValue = () => {
+ updateField("ecParity", "");
+
+ if (nodes.trim() !== "" && drivesPerServer.trim() !== "") {
+ api
+ .invoke("GET", `/api/v1/get-parity/${nodes}/${drivesPerServer}`)
+ .then((ecList: string[]) => {
+ updateField("ecParityChoices", ecListTransform(ecList));
+ updateField("cleanECChoices", ecList);
+ })
+ .catch((err: ErrorResponseHandler) => {
+ updateField("ecparityChoices", []);
+ isPageValid("tenantSize", false);
+ updateField("ecParity", "");
+ });
+ }
+ };
+ /*Calculate Allocation End*/
+
+ /* Validations of pages */
+
+ useEffect(() => {
+ const parsedSize = getBytes(volumeSize, sizeFactor, true);
+ const commonValidation = commonFormValidation([
+ {
+ fieldKey: "nodes",
+ required: true,
+ value: nodes,
+ customValidation: errorFlag,
+ customValidationMessage: nodeError,
+ },
+ {
+ fieldKey: "volume_size",
+ required: true,
+ value: volumeSize,
+ customValidation:
+ parseInt(parsedSize) < 1073741824 ||
+ parseInt(parsedSize) > limitSize[selectedStorageClass],
+ customValidationMessage: `Volume size must be greater than 1Gi and less than ${niceBytes(
+ limitSize[selectedStorageClass],
+ true
+ )}`,
+ },
+ {
+ fieldKey: "memory_per_node",
+ required: true,
+ value: memoryNode,
+ customValidation: parseInt(memoryNode) < 2,
+ customValidationMessage: "Memory size must be greater than 2Gi",
+ },
+ {
+ fieldKey: "drivesps",
+ required: true,
+ value: drivesPerServer,
+ customValidation: parseInt(drivesPerServer) < 1,
+ customValidationMessage: "There must be at least one drive",
+ },
+ ]);
+
+ isPageValid(
+ "tenantSize",
+ !("nodes" in commonValidation) &&
+ !("volume_size" in commonValidation) &&
+ !("memory_per_node" in commonValidation) &&
+ !("drivesps" in commonValidation) &&
+ distribution.error === "" &&
+ ecParityCalc.error === 0 &&
+ memorySize.error === ""
+ );
+ }, [
+ nodes,
+ volumeSize,
+ sizeFactor,
+ memoryNode,
+ distribution,
+ drivesPerServer,
+ ecParityCalc,
+ memorySize,
+ limitSize,
+ selectedStorageClass,
+ isPageValid,
+ errorFlag,
+ nodeError,
+ ]);
+
+ /* End Validation of pages */
+
+ return (
+
+
Resource Allocation
+
+
+
+
+ Number of Servers
+
+ {parseInt(nodes) > 0 ? nodes : "-"}
+
+
+
+ Drives per Server
+
+ {distribution ? distribution.disks : "-"}
+
+
+
+ Drive Capacity
+
+ {distribution ? niceBytes(distribution.pvSize) : "-"}
+
+
+
+ Total Volumes
+
+ {distribution ? distribution.persistentVolumes : "-"}
+
+
+ {!advancedMode && (
+
+ Memory per Node
+ {memoryNode} Gi
+
+ )}
+
+
+ {ecParityCalc.error === 0 && usableInformation && (
+
+ Erasure Code Configuration
+
+
+
+
+ EC Parity
+
+ {ecParity !== "" ? ecParity : "-"}
+
+
+
+ Raw Capacity
+
+ {niceBytes(ecParityCalc.rawCapacity)}
+
+
+
+ Usable Capacity
+
+ {niceBytes(usableInformation.maxCapacity)}
+
+
+
+ Server Failures Tolerated
+
+ {distribution
+ ? Math.floor(
+ usableInformation.maxFailureTolerations /
+ distribution.disks
+ )
+ : "-"}
+
+
+
+
+
+ )}
+
+ );
+};
+
+const mapState = (state: AppState) => ({
+ advancedMode: state.tenants.createTenant.advancedModeOn,
+ volumeSize: state.tenants.createTenant.fields.tenantSize.volumeSize,
+ sizeFactor: state.tenants.createTenant.fields.tenantSize.sizeFactor,
+ drivesPerServer: state.tenants.createTenant.fields.tenantSize.drivesPerServer,
+ nodes: state.tenants.createTenant.fields.tenantSize.nodes,
+ memoryNode: state.tenants.createTenant.fields.tenantSize.memoryNode,
+ ecParity: state.tenants.createTenant.fields.tenantSize.ecParity,
+ ecParityChoices: state.tenants.createTenant.fields.tenantSize.ecParityChoices,
+ cleanECChoices: state.tenants.createTenant.fields.tenantSize.cleanECChoices,
+ maxAllocableMemo:
+ state.tenants.createTenant.fields.tenantSize.maxAllocableMemo,
+ memorySize: state.tenants.createTenant.fields.tenantSize.memorySize,
+ distribution: state.tenants.createTenant.fields.tenantSize.distribution,
+ ecParityCalc: state.tenants.createTenant.fields.tenantSize.ecParityCalc,
+ limitSize: state.tenants.createTenant.fields.tenantSize.limitSize,
+ selectedStorageClass:
+ state.tenants.createTenant.fields.nameTenant.selectedStorageClass,
+});
+
+const connector = connect(mapState, {
+ updateAddField,
+ isPageValid,
+});
+
+export default withStyles(styles)(connector(SizePreview));
diff --git a/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/TenantSize.tsx b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/TenantSize.tsx
index 6b97d263f..03ce292c5 100644
--- a/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/TenantSize.tsx
+++ b/portal-ui/src/screens/Console/Tenants/AddTenant/Steps/TenantSize.tsx
@@ -24,10 +24,6 @@ import {
wizardCommon,
} from "../../../Common/FormComponents/common/styleLibrary";
import Grid from "@material-ui/core/Grid";
-import Table from "@material-ui/core/Table";
-import TableBody from "@material-ui/core/TableBody";
-import TableCell from "@material-ui/core/TableCell";
-import TableRow from "@material-ui/core/TableRow";
import {
calculateDistribution,
erasureCodeCalc,
@@ -102,9 +98,6 @@ const TenantSize = ({
const [validationErrors, setValidationErrors] = useState({});
const [errorFlag, setErrorFlag] = useState(false);
const [nodeError, setNodeError] = useState("");
- const usableInformation = ecParityCalc.storageFactors.find(
- (element) => element.erasureCode === ecParity
- );
// Common
const updateField = useCallback(
@@ -297,17 +290,23 @@ const TenantSize = ({
return (
-
-
Tenant Size
-
- Please select the desired capacity
-
-
+
+
+
Tenant Size
+
+ Please select the desired capacity
+
+
+
{distribution.error !== "" && (
- {distribution.error}
+
+ {distribution.error}
+
)}
{memorySize.error !== "" && (
- {memorySize.error}
+
+ {memorySize.error}
+
)}
) => {
updateField("sizeFactor", e.target.value as string);
}}
@@ -373,133 +376,43 @@ const TenantSize = ({
- {advancedMode && (
-
-
- ) => {
- updateField("memoryNode", e.target.value);
- cleanValidation("memory_per_node");
- }}
- label="Memory per Node [Gi]"
- value={memoryNode}
- required
- error={validationErrors["memory_per_node"] || ""}
- min="2"
- />
-
-
- ) => {
- updateField("ecParity", e.target.value as string);
- }}
- label="Erasure Code Parity"
- value={ecParity}
- options={ecParityChoices}
- />
-
- Please select the desired parity. This setting will change the max
- usable capacity in the cluster
-
-
-
- )}
- Resource Allocation
-
-
-
-
- Number of Servers
-
-
- {parseInt(nodes) > 0 ? nodes : "-"}
-
-
-
-
- Drives per Server
-
-
- {distribution ? distribution.disks : "-"}
-
-
-
-
- Drive Capacity
-
-
- {distribution ? niceBytes(distribution.pvSize) : "-"}
-
-
-
-
- Total Number of Volumes
-
-
- {distribution ? distribution.persistentVolumes : "-"}
-
-
- {!advancedMode && (
-
-
- Memory per Node
-
- {memoryNode} Gi
-
- )}
-
-
- {ecParityCalc.error === 0 && usableInformation && (
-
- Erasure Code Configuration
-
-
-
-
- EC Parity
-
-
- {ecParity !== "" ? ecParity : "-"}
-
-
-
-
- Raw Capacity
-
-
- {niceBytes(ecParityCalc.rawCapacity)}
-
-
-
-
- Usable Capacity
-
-
- {niceBytes(usableInformation.maxCapacity)}
-
-
-
-
- Number of server failures to tolerate
-
-
- {distribution
- ? Math.floor(
- usableInformation.maxFailureTolerations /
- distribution.disks
- )
- : "-"}
-
-
-
-
-
- )}
+
+
+
+ ) => {
+ updateField("memoryNode", e.target.value);
+ cleanValidation("memory_per_node");
+ }}
+ label="Memory per Node [Gi]"
+ value={memoryNode}
+ disabled={selectedStorageClass === ""}
+ required
+ error={validationErrors["memory_per_node"] || ""}
+ min="2"
+ />
+
+
+ ) => {
+ updateField("ecParity", e.target.value as string);
+ }}
+ label="Erasure Code Parity"
+ disabled={selectedStorageClass === ""}
+ value={ecParity}
+ options={ecParityChoices}
+ />
+
+ Please select the desired parity. This setting will change the max
+ usable capacity in the cluster
+
+
+
);
};
diff --git a/portal-ui/src/screens/Console/Tenants/ListTenants/ListTenants.tsx b/portal-ui/src/screens/Console/Tenants/ListTenants/ListTenants.tsx
index b98f0a6de..25ccc2389 100644
--- a/portal-ui/src/screens/Console/Tenants/ListTenants/ListTenants.tsx
+++ b/portal-ui/src/screens/Console/Tenants/ListTenants/ListTenants.tsx
@@ -19,7 +19,7 @@ import { connect } from "react-redux";
import Grid from "@material-ui/core/Grid";
import TextField from "@material-ui/core/TextField";
import InputAdornment from "@material-ui/core/InputAdornment";
-import { Button, IconButton } from "@material-ui/core";
+import { IconButton } from "@material-ui/core";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { ITenant, ITenantsResponse } from "./types";
import { niceBytes } from "../../../../common/utils";
@@ -27,77 +27,28 @@ import { NewServiceAccount } from "../../Common/CredentialsPrompt/types";
import {
actionsTray,
searchField,
- settingsCommon,
} from "../../Common/FormComponents/common/styleLibrary";
import { setErrorSnackMessage } from "../../../../actions";
-import { AddIcon, CircleIcon } from "../../../../icons";
-import { resetAddTenantForm } from "../actions";
+import { CircleIcon, CreateIcon } from "../../../../icons";
import { ErrorResponseHandler } from "../../../../common/types";
import api from "../../../../common/api";
import TableWrapper from "../../Common/TableWrapper/TableWrapper";
import DeleteTenant from "./DeleteTenant";
-import AddTenant from "../AddTenant/AddTenant";
import CredentialsPrompt from "../../Common/CredentialsPrompt/CredentialsPrompt";
import history from "../../../../history";
-import SlideOptions from "../../Common/SlideOptions/SlideOptions";
-import BackSettingsIcon from "../../../../icons/BackSettingsIcon";
import RefreshIcon from "../../../../icons/RefreshIcon";
import SearchIcon from "../../../../icons/SearchIcon";
interface ITenantsList {
classes: any;
setErrorSnackMessage: typeof setErrorSnackMessage;
- resetAddTenantForm: typeof resetAddTenantForm;
}
const styles = (theme: Theme) =>
createStyles({
...actionsTray,
...searchField,
- ...settingsCommon,
- settingsOptionsContainer: {
- ...settingsCommon.settingsOptionsContainer,
- height: "calc(100vh - 150px)",
- },
- seeMore: {
- marginTop: theme.spacing(3),
- },
- paper: {
- display: "flex",
- overflow: "auto",
- flexDirection: "column",
- },
- addSideBar: {
- width: "320px",
- padding: "20px",
- },
- tableToolbar: {
- paddingLeft: theme.spacing(2),
- paddingRight: theme.spacing(0),
- },
- minTableHeader: {
- color: "#393939",
- "& tr": {
- "& th": {
- fontWeight: "bold",
- },
- },
- },
- actionsTray: {
- ...actionsTray.actionsTray,
- padding: "0 38px",
- },
- tenantsContainer: {
- padding: "15px 0",
- },
- customConfigurationPage: {
- height: "calc(100vh - 260px)",
- scrollbarWidth: "none" as const,
- "&::-webkit-scrollbar": {
- display: "none",
- },
- },
redState: {
color: theme.palette.error.main,
"& .MuiSvgIcon-root": {
@@ -136,12 +87,7 @@ const styles = (theme: Theme) =>
},
});
-const ListTenants = ({
- classes,
- setErrorSnackMessage,
- resetAddTenantForm,
-}: ITenantsList) => {
- const [currentPanel, setCurrentPanel] = useState(0);
+const ListTenants = ({ classes, setErrorSnackMessage }: ITenantsList) => {
const [deleteOpen, setDeleteOpen] = useState(false);
const [selectedTenant, setSelectedTenant] = useState(null);
const [isLoading, setIsLoading] = useState(false);
@@ -151,15 +97,6 @@ const ListTenants = ({
const [createdAccount, setCreatedAccount] =
useState(null);
- const closeAddModalAndRefresh = (reloadData: boolean) => {
- setCurrentPanel(0);
- resetAddTenantForm();
-
- if (reloadData) {
- setIsLoading(true);
- }
- };
-
const closeDeleteModalAndRefresh = (reloadData: boolean) => {
setDeleteOpen(false);
@@ -183,11 +120,6 @@ const ListTenants = ({
setCreatedAccount(null);
};
- const backClick = () => {
- setCurrentPanel(currentPanel - 1);
- resetAddTenantForm();
- };
-
const tableActions = [
{ type: "view", onClick: redirectToTenantDetails },
{ type: "delete", onClick: confirmDeleteTenant },
@@ -240,10 +172,6 @@ const ListTenants = ({
setIsLoading(true);
}, []);
- const createTenant = () => {
- setCurrentPanel(1);
- };
-
const healthStatusToClass = (health_status: string) => {
switch (health_status) {
case "red":
@@ -277,104 +205,74 @@ const ListTenants = ({
/>
)}
+
+ {
+ setFilterTenants(val.target.value);
+ }}
+ InputProps={{
+ disableUnderline: true,
+ startAdornment: (
+
+
+
+ ),
+ }}
+ />
+ {
+ setIsLoading(true);
+ }}
+ >
+
+
+ {
+ history.push("/tenants/add");
+ }}
+ >
+
+
+
-
-
-
-
- {
- setFilterTenants(val.target.value);
- }}
- InputProps={{
- disableUnderline: true,
- startAdornment: (
-
-
-
- ),
- }}
- />
- {
- setIsLoading(true);
- }}
- >
-
-
- }
- onClick={createTenant}
- >
- Create Tenant
-
-
-
- {
- return (
-
-
-
-
- {t.name}
-
- );
- },
- },
- { label: "Namespace", elementKey: "namespace" },
- { label: "Capacity", elementKey: "capacity" },
- { label: "# of Pools", elementKey: "pool_count" },
- { label: "State", elementKey: "currentState" },
- ]}
- isLoading={isLoading}
- records={filteredRecords}
- entityName="Tenants"
- idField="name"
- customPaperHeight={classes.customConfigurationPage}
- noBackground
- />
-
-
- ,
-
-
-
-
-
- {currentPanel === 1 && (
-
- )}
-
- ,
- ]}
- currentSlide={currentPanel}
- />
-
+ {
+ return (
+
+
+
+
+ {t.name}
+
+ );
+ },
+ },
+ { label: "Namespace", elementKey: "namespace" },
+ { label: "Capacity", elementKey: "capacity" },
+ { label: "# of Pools", elementKey: "pool_count" },
+ { label: "State", elementKey: "currentState" },
+ ]}
+ isLoading={isLoading}
+ records={filteredRecords}
+ entityName="Tenants"
+ idField="name"
+ />
@@ -383,7 +281,6 @@ const ListTenants = ({
const connector = connect(null, {
setErrorSnackMessage,
- resetAddTenantForm,
});
export default withStyles(styles)(connector(ListTenants));
diff --git a/portal-ui/src/screens/Console/Tenants/reducer.ts b/portal-ui/src/screens/Console/Tenants/reducer.ts
index 25001d62c..c0a38ca53 100644
--- a/portal-ui/src/screens/Console/Tenants/reducer.ts
+++ b/portal-ui/src/screens/Console/Tenants/reducer.ts
@@ -61,7 +61,7 @@ const initialState: ITenantState = {
selectedStorageClass: "",
},
configure: {
- customImage: false,
+ customImage: true,
imageName: "",
customDockerhub: false,
imageRegistry: "",