First set of changes for settings pages new design (#493)

Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
Alex
2020-12-09 22:06:14 -06:00
committed by GitHub
parent a4ad341a18
commit 3f023f9771
15 changed files with 503 additions and 218 deletions

View File

@@ -22,7 +22,7 @@ import (
// endpoints definition
var (
configuration = "/configurations-list"
configuration = "/settings"
users = "/users"
groups = "/groups"
iamPolicies = "/policies"

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,53 @@
// 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/>.
import React from "react";
import { SvgIcon } from "@material-ui/core";
const BackSettingsIcon = () => (
<SvgIcon viewBox="0 0 10.847 6.572">
<g transform="translate(-84.793 -81.193)">
<line
x2="9.64"
transform="translate(85.5 84.5)"
fill="none"
stroke="#000"
strokeLinecap="round"
strokeWidth="1"
/>
<line
y1="2.558"
x2="2.645"
transform="translate(85.5 81.9)"
fill="none"
stroke="#000"
strokeLinecap="round"
strokeWidth="1"
/>
<line
x2="2.645"
y2="2.558"
transform="translate(85.5 84.5)"
fill="none"
stroke="#000"
strokeLinecap="round"
strokeWidth="1"
/>
</g>
</SvgIcon>
);
export default BackSettingsIcon;

View File

@@ -105,10 +105,9 @@ const Account = ({ classes }: IServiceAccountsProps) => {
newServiceAccount,
setNewServiceAccount,
] = useState<NewServiceAccount | null>(null);
const [
changePasswordModalOpen,
setChangePasswordModalOpen,
] = useState<boolean>(false);
const [changePasswordModalOpen, setChangePasswordModalOpen] = useState<
boolean
>(false);
useEffect(() => {
fetchRecords();

View File

@@ -217,10 +217,9 @@ const ViewBucket = ({ classes, match }: IViewBucketProps) => {
);
const [curTab, setCurTab] = useState<number>(0);
const [addScreenOpen, setAddScreenOpen] = useState<boolean>(false);
const [
enableEncryptionScreenOpen,
setEnableEncryptionScreenOpen,
] = useState<boolean>(false);
const [enableEncryptionScreenOpen, setEnableEncryptionScreenOpen] = useState<
boolean
>(false);
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
const [selectedEvent, setSelectedEvent] = useState<BucketEvent | null>(null);
const [bucketSize, setBucketSize] = useState<string>("0");

View File

@@ -238,3 +238,29 @@ export const selectorsCommon = {
height: 200,
},
};
export const settingsCommon = {
customTitle: {
fontSize: 18,
color: "#000",
fontWeight: 600,
padding: "12px 0",
borderBottom: "#eaedee 1px solid",
marginBottom: 10,
margin: "15px 38px 27px",
},
settingsFormContainer: {
height: "calc(100vh - 421px)",
padding: "15px 38px",
overflowY: "auto" as const,
scrollbarWidth: "none" as const,
"&::-webkit-scrollbar": {
display: "none",
},
},
settingsButtonContainer: {
borderTop: "1px solid #EAEAEA",
padding: "15px 38px",
textAlign: "right" as const,
},
};

View File

@@ -0,0 +1,80 @@
// This file is part of MinIO Console Server
// Copyright (c) 2019 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/>.
import React, { Fragment } from "react";
import { AutoSizer } from "react-virtualized";
import { createStyles, withStyles } from "@material-ui/core/styles";
interface ISlideOptions {
classes: any;
slideOptions: any;
currentSlide: number;
}
const styles = () =>
createStyles({
masterContainer: {
overflowX: "hidden",
overflowY: "auto",
},
sliderContainer: {
width: "auto",
transitionDuration: "0.3s",
position: "relative",
},
slide: {
float: "left",
},
});
const SlideOptions = ({
classes,
slideOptions,
currentSlide,
}: ISlideOptions) => {
return (
<AutoSizer>
{({ width, height }: any) => {
console.log(width, height);
const currentSliderPosition = currentSlide * width;
const containerSize = width * slideOptions.length;
return (
<Fragment>
<div className={classes.masterContainer} style={{ width, height }}>
<div
className={classes.sliderContainer}
style={{
left: `-${currentSliderPosition}px`,
width: `${containerSize}px`,
}}
>
{slideOptions.map((block: any) => {
return (
<div className={classes.slide} style={{ width }}>
{block}
</div>
);
})}
</div>
</div>
</Fragment>
);
}}
</AutoSizer>
);
};
export default withStyles(styles)(SlideOptions);

View File

@@ -66,6 +66,7 @@ interface TableWrapperProps {
radioSelection?: boolean;
customEmptyMessage?: string;
customPaperHeight?: string;
noBackground?: boolean;
}
const borderColor = "#9c9c9c80";
@@ -103,6 +104,10 @@ const styles = () =>
height: 3,
},
},
noBackground: {
backgroundColor: "transparent",
border: 0,
},
defaultPaperHeight: {
height: "calc(100vh - 205px)",
},
@@ -362,6 +367,7 @@ const TableWrapper = ({
radioSelection = false,
customEmptyMessage = "",
customPaperHeight = "",
noBackground = false,
}: TableWrapperProps) => {
const findView = itemActions
? itemActions.find((el) => el.type === "view")
@@ -385,6 +391,8 @@ const TableWrapper = ({
<Grid item xs={12}>
<Paper
className={`${classes.paper} ${
noBackground ? classes.noBackground : ""
} ${
customPaperHeight !== ""
? customPaperHeight
: classes.defaultPaperHeight

View File

@@ -161,7 +161,7 @@ const ConfTargetGeneric = ({
return (
<Grid container>
<Grid xs={12} item className={classes.formScrollable}>
<Grid xs={12} item>
{fieldsElements.map((field, item) => (
<React.Fragment key={field.name}>
<Grid item xs={12}>

View File

@@ -0,0 +1,61 @@
import React, { Fragment, useState } from "react";
import PageHeader from "../Common/PageHeader/PageHeader";
import { Grid } from "@material-ui/core";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
import Tab from "@material-ui/core/Tab";
import Tabs from "@material-ui/core/Tabs";
import ConfigurationsList from "./ConfigurationPanels/ConfigurationsList";
interface IConfigurationMain {
classes: any;
}
const styles = (theme: Theme) =>
createStyles({
headerLabel: {
fontSize: 22,
fontWeight: 600,
color: "#000",
marginTop: 4,
},
...containerForHeader(theme.spacing(4)),
});
const ConfigurationMain = ({ classes }: IConfigurationMain) => {
const [selectedTab, setSelectedTab] = useState<number>(0);
return (
<Fragment>
<PageHeader label="Settings" />
<Grid container>
<Grid item xs={12} className={classes.container}>
<Grid item xs={12} className={classes.headerLabel}>
All Settings
</Grid>
<Tabs
value={selectedTab}
indicatorColor="primary"
textColor="primary"
onChange={(_, newValue: number) => {
setSelectedTab(newValue);
}}
aria-label="tenant-tabs"
>
<Tab label="Configurations" />
<Tab label="Lambda Notifications" />
</Tabs>
<Grid item xs={12}>
{selectedTab === 0 && (
<Grid item xs={12}>
<ConfigurationsList />
</Grid>
)}
{selectedTab === 1 && <div>Lambda notifications</div>}
</Grid>
</Grid>
</Grid>
</Fragment>
);
};
export default withStyles(styles)(ConfigurationMain);

View File

@@ -14,7 +14,7 @@
// 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/>.
import React, { useState } from "react";
import React, { useState, Fragment } from "react";
import get from "lodash/get";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import Grid from "@material-ui/core/Grid";
@@ -30,8 +30,10 @@ import {
actionsTray,
containerForHeader,
searchField,
settingsCommon,
} from "../../Common/FormComponents/common/styleLibrary";
import PageHeader from "../../Common/PageHeader/PageHeader";
import SlideOptions from "../../Common/SlideOptions/SlideOptions";
import BackSettingsIcon from "../../../../icons/BackSettingsIcon";
interface IListConfiguration {
classes: any;
@@ -51,18 +53,59 @@ const styles = (theme: Theme) =>
iconText: {
lineHeight: "24px",
},
customConfigurationPage: {
height: "calc(100vh - 324px)",
scrollbarWidth: "none" as const,
"&::-webkit-scrollbar": {
display: "none",
},
},
settingsOptionsContainer: {
height: "calc(100vh - 244px)",
backgroundColor: "#fff",
border: "#EAEDEE 1px solid",
borderRadius: 3,
marginTop: 15,
},
backButton: {
cursor: "pointer",
fontSize: 10,
fontWeight: 600,
color: "#000",
backgroundColor: "transparent",
border: 0,
padding: 0,
display: "flex",
alignItems: "center",
"&:active, &:focus": {
outline: 0,
},
"& svg": {
width: 10,
marginRight: 4,
},
},
backContainer: {
margin: "20px 38px 0",
},
...searchField,
...actionsTray,
...settingsCommon,
...containerForHeader(theme.spacing(4)),
});
const initialConfiguration = {
configuration_id: "",
configuration_label: "",
};
const ConfigurationsList = ({ classes }: IListConfiguration) => {
const [editScreenOpen, setEditScreenOpen] = useState(false);
const [selectedConfiguration, setSelectedConfiguration] = useState({
configuration_id: "",
configuration_label: "",
});
const [selectedConfiguration, setSelectedConfiguration] = useState(
initialConfiguration
);
const [filter, setFilter] = useState("");
const [currentConfiguration, setCurrentConfiguration] = useState<number>(0);
const tableActions = [
{
@@ -73,8 +116,8 @@ const ConfigurationsList = ({ classes }: IListConfiguration) => {
// We redirect Browser
history.push(url);
} else {
setCurrentConfiguration(1);
setSelectedConfiguration(element);
setEditScreenOpen(true);
}
},
},
@@ -87,53 +130,64 @@ const ConfigurationsList = ({ classes }: IListConfiguration) => {
.includes(filter.toLocaleLowerCase())
);
const backToInitialConfig = () => {
setCurrentConfiguration(0);
setSelectedConfiguration(initialConfiguration);
};
return (
<React.Fragment>
{editScreenOpen && (
<EditConfiguration
open={editScreenOpen}
closeModalAndRefresh={() => {
setEditScreenOpen(false);
}}
selectedConfiguration={selectedConfiguration}
/>
)}
<PageHeader label="Configurations List" />
<Grid container>
<Grid item xs={12} className={classes.container}>
<Grid item xs={12} className={classes.actionsTray}>
<TextField
placeholder="Filter"
className={classes.searchField}
id="search-resource"
label=""
onChange={(event) => {
setFilter(event.target.value);
}}
InputProps={{
disableUnderline: true,
startAdornment: (
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
),
}}
/>
</Grid>
<Grid item xs={12}>
<Grid item xs={12}>
<br />
</Grid>
<Grid item xs={12}>
<TableWrapper
itemActions={tableActions}
columns={[
{ label: "Configuration", elementKey: "configuration_id" },
]}
isLoading={false}
records={filteredRecords}
entityName="Configurations"
idField="configuration_id"
/>
<div className={classes.settingsOptionsContainer}>
<SlideOptions
slideOptions={[
<Fragment>
<Grid item xs={12} className={classes.customTitle}>
Configuration Types
</Grid>
<TableWrapper
itemActions={tableActions}
columns={[
{
label: "Configuration",
elementKey: "configuration_id",
},
]}
isLoading={false}
records={filteredRecords}
entityName="Configurations"
idField="configuration_id"
customPaperHeight={classes.customConfigurationPage}
noBackground
/>
</Fragment>,
<Fragment>
<Grid item xs={12} className={classes.backContainer}>
<button
onClick={backToInitialConfig}
className={classes.backButton}
>
<BackSettingsIcon />
Back To Configurations
</button>
</Grid>
<Grid item xs={12}>
{currentConfiguration === 1 ? (
<EditConfiguration
closeModalAndRefresh={() => {
setCurrentConfiguration(0);
}}
selectedConfiguration={selectedConfiguration}
/>
) : null}
</Grid>
</Fragment>,
]}
currentSlide={currentConfiguration}
/>
</div>
</Grid>
</Grid>
</Grid>

View File

@@ -123,7 +123,6 @@ const WebhookPanel = ({ match, classes }: IWebhookPanel) => {
<React.Fragment>
{addWebhookOpen && (
<EditConfiguration
open={addWebhookOpen}
closeModalAndRefresh={() => {
setIsLoading(true);
setAddWebhookOpen(false);

View File

@@ -14,24 +14,27 @@
// 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/>.
import React, { useCallback, useEffect, useState } from "react";
import React, { Fragment, useCallback, useEffect, useState } from "react";
import get from "lodash/get";
import { connect } from "react-redux";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { Button, LinearProgress } from "@material-ui/core";
import Grid from "@material-ui/core/Grid";
import Typography from "@material-ui/core/Typography";
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
import api from "../../../../common/api";
import ConfTargetGeneric from "../ConfTargetGeneric";
import { serverNeedsRestart } from "../../../../actions";
import { fieldBasic } from "../../Common/FormComponents/common/styleLibrary";
import {
fieldBasic,
settingsCommon,
} from "../../Common/FormComponents/common/styleLibrary";
import { fieldsConfigurations, removeEmptyFields } from "../utils";
import { IConfigurationElement, IElementValue } from "../types";
const styles = (theme: Theme) =>
createStyles({
...fieldBasic,
...settingsCommon,
errorBlock: {
color: "red",
},
@@ -47,10 +50,14 @@ const styles = (theme: Theme) =>
logoButton: {
height: "80px",
},
customTitle: {
...settingsCommon.customTitle,
marginTop: 0,
},
});
interface IAddNotificationEndpointProps {
open: boolean;
closeModalAndRefresh: any;
serverNeedsRestart: typeof serverNeedsRestart;
selectedConfiguration: IConfigurationElement;
@@ -58,7 +65,6 @@ interface IAddNotificationEndpointProps {
}
const EditConfiguration = ({
open,
closeModalAndRefresh,
serverNeedsRestart,
selectedConfiguration,
@@ -134,32 +140,38 @@ const EditConfiguration = ({
);
return (
<ModalWrapper
modalOpen={open}
onClose={closeModalAndRefresh}
title={selectedConfiguration.configuration_label}
>
<React.Fragment>
{errorConfig !== "" && (
<Grid item xs={12}>
<Typography
component="p"
variant="body1"
className={classes.errorBlock}
>
{errorConfig}
</Typography>
</Grid>
)}
<Fragment>
<Grid item xs={12} className={classes.customTitle}>
{selectedConfiguration.configuration_label}
</Grid>
<Fragment>
<form noValidate onSubmit={submitForm}>
<ConfTargetGeneric
fields={
fieldsConfigurations[selectedConfiguration.configuration_id]
}
onChange={onValueChange}
defaultVals={configValues}
/>
<Grid item xs={12} className={classes.buttonContainer}>
<Grid item xs={12} className={classes.settingsFormContainer}>
{loadingConfig && (
<Grid item xs={12}>
<LinearProgress />
</Grid>
)}
{errorConfig !== "" && (
<Grid item xs={12}>
<Typography
component="p"
variant="body1"
className={classes.errorBlock}
>
{errorConfig}
</Typography>
</Grid>
)}
<ConfTargetGeneric
fields={
fieldsConfigurations[selectedConfiguration.configuration_id]
}
onChange={onValueChange}
defaultVals={configValues}
/>
</Grid>
<Grid item xs={12} className={classes.settingsButtonContainer}>
<Button
type="submit"
variant="contained"
@@ -169,15 +181,9 @@ const EditConfiguration = ({
Save
</Button>
</Grid>
{loadingConfig && (
<Grid item xs={12}>
<LinearProgress />
</Grid>
)}
<Grid item xs={9} />
</form>
</React.Fragment>
</ModalWrapper>
</Fragment>
</Fragment>
);
};

View File

@@ -14,9 +14,10 @@
// 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/>.
import React from "react";
import React, { Fragment } from "react";
import clsx from "clsx";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { Button, LinearProgress } from "@material-ui/core";
import CssBaseline from "@material-ui/core/CssBaseline";
import Drawer from "@material-ui/core/Drawer";
import Container from "@material-ui/core/Container";
@@ -29,6 +30,7 @@ import {
serverNeedsRestart,
setMenuOpen,
} from "../../actions";
import { ISessionResponse } from "./types";
import Buckets from "./Buckets/Buckets";
import Policies from "./Policies/Policies";
import Dashboard from "./Dashboard/Dashboard";
@@ -38,11 +40,9 @@ import Account from "./Account/Account";
import Users from "./Users/Users";
import Groups from "./Groups/Groups";
import ListNotificationEndpoints from "./NotificationEndopoints/ListNotificationEndpoints";
import ConfigurationsList from "./Configurations/ConfigurationPanels/ConfigurationsList";
import { Button, LinearProgress } from "@material-ui/core";
import ConfigurationMain from "./Configurations/ConfigurationMain";
import WebhookPanel from "./Configurations/ConfigurationPanels/WebhookPanel";
import ListTenants from "./Tenants/ListTenants/ListTenants";
import { ISessionResponse } from "./types";
import TenantDetails from "./Tenants/TenantDetails/TenantDetails";
import ObjectBrowser from "./ObjectBrowser/ObjectBrowser";
import ObjectRouting from "./Buckets/ListBuckets/Objects/ListObjects/ObjectRouting";
@@ -253,8 +253,8 @@ const Console = ({
path: "/notification-endpoints",
},
{
component: ConfigurationsList,
path: "/configurations-list",
component: ConfigurationMain,
path: "/settings",
},
{
component: Account,
@@ -284,7 +284,7 @@ const Console = ({
const allowedRoutes = routes.filter((route: any) => allowedPages[route.path]);
return (
<React.Fragment>
<Fragment>
{session.status === "ok" ? (
<div className={classes.root}>
<CssBaseline />
@@ -305,12 +305,12 @@ const Console = ({
{needsRestart && (
<div className={classes.warningBar}>
{isServerLoading ? (
<React.Fragment>
<Fragment>
The server is restarting.
<LinearProgress />
</React.Fragment>
</Fragment>
) : (
<React.Fragment>
<Fragment>
The instance needs to be restarted for configuration changes
to take effect.{" "}
<Button
@@ -322,7 +322,7 @@ const Console = ({
>
Restart
</Button>
</React.Fragment>
</Fragment>
)}
</div>
)}
@@ -346,7 +346,7 @@ const Console = ({
</main>
</div>
) : null}
</React.Fragment>
</Fragment>
);
};

View File

@@ -286,8 +286,8 @@ const Menu = ({ userLoggedIn, classes, pages }: IMenuProps) => {
group: "Admin",
type: "item",
component: NavLink,
to: "/configurations-list",
name: "Configurations List",
to: "/settings",
name: "Settings",
icon: <ConfigurationsListIcon />,
},
{