New Vertical Tab UX refactor (#1210)
Co-authored-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
33f13c4853
commit
258a9400d9
@@ -19,18 +19,27 @@ import { Link } from "react-router-dom";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import { BackSettingsIcon } from "../icons";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
link: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
textDecoration: "none",
|
||||
color: theme.palette.primary.main,
|
||||
fontSize: 18,
|
||||
fontWeight: 600,
|
||||
marginBottom: 10,
|
||||
marginTop: 10,
|
||||
maxWidth: "250px",
|
||||
padding: "2rem 2rem 0rem 2rem",
|
||||
color: theme.palette.primary.light,
|
||||
fontSize: ".8rem",
|
||||
"&:hover": {
|
||||
textDecoration: "underline",
|
||||
},
|
||||
},
|
||||
icon: {
|
||||
marginRight: ".3rem",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -43,12 +52,10 @@ interface IBackLink {
|
||||
const BackLink = ({ to, label, classes }: IBackLink) => {
|
||||
return (
|
||||
<Link to={to} className={classes.link}>
|
||||
<Grid container spacing={1}>
|
||||
<Grid item>
|
||||
<BackSettingsIcon />
|
||||
</Grid>
|
||||
<Grid item>{label}</Grid>
|
||||
</Grid>
|
||||
<div className={classes.icon}>
|
||||
<BackSettingsIcon />
|
||||
</div>
|
||||
<div>{label}</div>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -61,11 +61,11 @@ const HelpBox = ({ classes, iconComponent, title, help }: IHelpBox) => {
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<Grid container>
|
||||
<Grid xs={12} className={classes.icon}>
|
||||
<Grid item xs={12} className={classes.icon}>
|
||||
{iconComponent}
|
||||
{title}
|
||||
</Grid>
|
||||
<Grid xs={12} className={classes.helpText}>
|
||||
<Grid item xs={12} className={classes.helpText}>
|
||||
{help}
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
@@ -42,6 +42,7 @@ import { ErrorResponseHandler } from "../../../common/types";
|
||||
import ChangePasswordModal from "./ChangePasswordModal";
|
||||
import SearchIcon from "../../../icons/SearchIcon";
|
||||
import HelpBox from "../../../common/HelpBox";
|
||||
import PageLayout from "../Common/Layout/PageLayout";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -75,9 +76,6 @@ const styles = (theme: Theme) =>
|
||||
},
|
||||
},
|
||||
},
|
||||
twHeight: {
|
||||
minHeight: 600,
|
||||
},
|
||||
imageIcon: {
|
||||
height: "100%",
|
||||
},
|
||||
@@ -234,7 +232,7 @@ const Account = ({
|
||||
</React.Fragment>
|
||||
}
|
||||
/>
|
||||
<Grid container className={classes.container}>
|
||||
<PageLayout>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Search Service Accounts"
|
||||
@@ -277,7 +275,6 @@ const Account = ({
|
||||
idField={""}
|
||||
columns={[{ label: "Service Account", elementKey: "" }]}
|
||||
itemActions={tableActions}
|
||||
customPaperHeight={classes.twHeight}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
@@ -308,7 +305,7 @@ const Account = ({
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</PageLayout>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -16,9 +16,6 @@
|
||||
|
||||
import React, { Fragment, useEffect, useState } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
import { Paper } from "@mui/material";
|
||||
import Tabs from "@mui/material/Tabs";
|
||||
import Tab from "@mui/material/Tab";
|
||||
@@ -42,8 +39,6 @@ import {
|
||||
} from "../../../../types";
|
||||
import PanelTitle from "../../Common/PanelTitle/PanelTitle";
|
||||
|
||||
const styles = (theme: Theme) => createStyles({});
|
||||
|
||||
const mapState = (state: AppState) => ({
|
||||
session: state.console.session,
|
||||
loadingBucket: state.buckets.bucketDetails.loadingBucket,
|
||||
@@ -206,4 +201,4 @@ const AccessDetails = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(connector(AccessDetails));
|
||||
export default connector(AccessDetails);
|
||||
|
||||
@@ -99,9 +99,7 @@ const BrowserHandler = ({
|
||||
)
|
||||
}
|
||||
/>
|
||||
<Grid container className={classes.container}>
|
||||
{fileMode ? <ObjectDetails /> : <ListObjects />}
|
||||
</Grid>
|
||||
<Grid>{fileMode ? <ObjectDetails /> : <ListObjects />}</Grid>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
buttonsStyles,
|
||||
containerForHeader,
|
||||
hrClass,
|
||||
pageContentStyles,
|
||||
searchField,
|
||||
} from "../../Common/FormComponents/common/styleLibrary";
|
||||
import { setErrorSnackMessage } from "../../../../actions";
|
||||
@@ -44,9 +45,6 @@ import BucketSummaryPanel from "./BucketSummaryPanel";
|
||||
import BucketEventsPanel from "./BucketEventsPanel";
|
||||
import BucketReplicationPanel from "./BucketReplicationPanel";
|
||||
import BucketLifecyclePanel from "./BucketLifecyclePanel";
|
||||
import List from "@mui/material/List";
|
||||
import ListItem from "@mui/material/ListItem";
|
||||
import ListItemText from "@mui/material/ListItemText";
|
||||
import ScreenTitle from "../../Common/ScreenTitle/ScreenTitle";
|
||||
import { IconButton, Tooltip } from "@mui/material";
|
||||
import { BucketsIcon, DeleteIcon, FolderIcon } from "../../../../icons";
|
||||
@@ -69,6 +67,9 @@ import {
|
||||
S3_PUT_REPLICATION_CONFIGURATION,
|
||||
} from "../../../../types";
|
||||
import { displayComponent } from "../../../../utils/permissions";
|
||||
import PageLayout from "../../Common/Layout/PageLayout";
|
||||
import VerticalTabs from "../../Common/VerticalTabs/VerticalTabs";
|
||||
import BackLink from "../../../../common/BackLink";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -80,6 +81,11 @@ const styles = (theme: Theme) =>
|
||||
overflow: "auto",
|
||||
flexDirection: "column",
|
||||
},
|
||||
pageContainer: {
|
||||
border: "1px solid #EAEAEA",
|
||||
height: "100%",
|
||||
},
|
||||
...pageContentStyles,
|
||||
addSideBar: {
|
||||
width: "320px",
|
||||
padding: "20px",
|
||||
@@ -216,6 +222,15 @@ const BucketDetails = ({
|
||||
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
|
||||
const bucketName = match.params["bucketName"];
|
||||
|
||||
let selTab = match?.params["0"];
|
||||
selTab = selTab ? selTab : "summary";
|
||||
|
||||
const [activeTab, setActiveTab] = useState(selTab);
|
||||
|
||||
useEffect(() => {
|
||||
setActiveTab(selTab);
|
||||
}, [selTab]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!iniLoad) {
|
||||
setBucketDetailsLoad(true);
|
||||
@@ -244,45 +259,25 @@ const BucketDetails = ({
|
||||
setErrorSnackMessage,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
let matchURL = match.params ? match.params["0"] : "browse";
|
||||
let topLevelRoute = `/buckets/${bucketName}`;
|
||||
const defaultRoute = "/admin/summary";
|
||||
|
||||
if (!matchURL) {
|
||||
matchURL = "";
|
||||
const manageBucketRoutes: Record<string, any> = {
|
||||
events: "/admin/events",
|
||||
replication: "/admin/replication",
|
||||
lifecycle: "/admin/lifecycle",
|
||||
access: "/admin/access",
|
||||
prefix: "/admin/prefix",
|
||||
};
|
||||
|
||||
const getRoutePath = (routeKey: string) => {
|
||||
let path = manageBucketRoutes[routeKey];
|
||||
if (!path) {
|
||||
path = `${topLevelRoute}${defaultRoute}`;
|
||||
} else {
|
||||
path = `${topLevelRoute}${path}`;
|
||||
}
|
||||
|
||||
const splitMatch = matchURL.split("/");
|
||||
|
||||
if (selectedTab !== splitMatch[0]) {
|
||||
setBucketDetailsTab(splitMatch[0]);
|
||||
}
|
||||
}, [match, bucketName, setBucketDetailsTab, selectedTab]);
|
||||
|
||||
const changeRoute = (newTab: string) => {
|
||||
let mainRoute = `/buckets/${bucketName}`;
|
||||
|
||||
switch (newTab) {
|
||||
case "events":
|
||||
mainRoute += "/admin/events";
|
||||
break;
|
||||
case "replication":
|
||||
mainRoute += "/admin/replication";
|
||||
break;
|
||||
case "lifecycle":
|
||||
mainRoute += "/admin/lifecycle";
|
||||
break;
|
||||
case "access":
|
||||
mainRoute += "/admin/access";
|
||||
break;
|
||||
case "prefix":
|
||||
mainRoute += "/admin/prefix";
|
||||
break;
|
||||
default:
|
||||
mainRoute += "/admin/summary";
|
||||
}
|
||||
|
||||
setBucketDetailsTab(newTab);
|
||||
history.push(mainRoute);
|
||||
return path;
|
||||
};
|
||||
|
||||
const closeDeleteModalAndRefresh = (refresh: boolean) => {
|
||||
@@ -331,7 +326,8 @@ const BucketDetails = ({
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
<Grid container className={classes.container}>
|
||||
<BackLink to={"/buckets"} label={"Back to Buckets"} />
|
||||
<PageLayout className={classes.pageContainer}>
|
||||
<Grid item xs={12}>
|
||||
<ScreenTitle
|
||||
icon={
|
||||
@@ -390,138 +386,133 @@ const BucketDetails = ({
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={2}>
|
||||
<List component="nav" dense={true}>
|
||||
<ListItem
|
||||
button
|
||||
selected={selectedTab === "summary"}
|
||||
onClick={() => {
|
||||
changeRoute("summary");
|
||||
}}
|
||||
>
|
||||
<ListItemText primary="Summary" />
|
||||
</ListItem>
|
||||
<ListItem
|
||||
disabled={
|
||||
!displayComponent(bucketInfo?.allowedActions, [
|
||||
S3_GET_BUCKET_NOTIFICATIONS,
|
||||
S3_PUT_BUCKET_NOTIFICATIONS,
|
||||
])
|
||||
}
|
||||
button
|
||||
selected={selectedTab === "events"}
|
||||
onClick={() => {
|
||||
changeRoute("events");
|
||||
}}
|
||||
>
|
||||
<ListItemText primary="Events" />
|
||||
</ListItem>
|
||||
<ListItem
|
||||
button
|
||||
disabled={
|
||||
<VerticalTabs
|
||||
selectedTab={activeTab}
|
||||
isRouteTabs
|
||||
routes={
|
||||
<div className={classes.contentSpacer}>
|
||||
<Router history={history}>
|
||||
<Switch>
|
||||
<Route
|
||||
exact
|
||||
path="/buckets/:bucketName/admin/summary"
|
||||
component={BucketSummaryPanel}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path="/buckets/:bucketName/admin/events"
|
||||
component={BucketEventsPanel}
|
||||
/>
|
||||
{distributedSetup && (
|
||||
<Route
|
||||
exact
|
||||
path="/buckets/:bucketName/admin/replication"
|
||||
component={BucketReplicationPanel}
|
||||
/>
|
||||
)}
|
||||
{distributedSetup && (
|
||||
<Route
|
||||
exact
|
||||
path="/buckets/:bucketName/admin/lifecycle"
|
||||
component={BucketLifecyclePanel}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Route
|
||||
exact
|
||||
path="/buckets/:bucketName/admin/access"
|
||||
component={AccessDetailsPanel}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path="/buckets/:bucketName/admin/prefix"
|
||||
component={AccessRulePanel}
|
||||
/>
|
||||
<Route
|
||||
path="/buckets/:bucketName"
|
||||
component={() => (
|
||||
<Redirect to={`/buckets/${bucketName}/admin/summary`} />
|
||||
)}
|
||||
/>
|
||||
</Switch>
|
||||
</Router>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{{
|
||||
tabConfig: {
|
||||
label: "Summary",
|
||||
value: "summary",
|
||||
component: Link,
|
||||
to: getRoutePath("summary"),
|
||||
},
|
||||
}}
|
||||
{{
|
||||
tabConfig: {
|
||||
label: "Events",
|
||||
value: "events",
|
||||
component: Link,
|
||||
disabled: !displayComponent(bucketInfo?.allowedActions, [
|
||||
S3_GET_BUCKET_NOTIFICATIONS,
|
||||
S3_PUT_BUCKET_NOTIFICATIONS,
|
||||
]),
|
||||
to: getRoutePath("events"),
|
||||
},
|
||||
}}
|
||||
{{
|
||||
tabConfig: {
|
||||
label: "Replication",
|
||||
value: "replication",
|
||||
component: Link,
|
||||
disabled:
|
||||
!distributedSetup ||
|
||||
!displayComponent(bucketInfo?.allowedActions, [
|
||||
S3_GET_REPLICATION_CONFIGURATION,
|
||||
S3_PUT_REPLICATION_CONFIGURATION,
|
||||
])
|
||||
}
|
||||
selected={selectedTab === "replication"}
|
||||
onClick={() => {
|
||||
changeRoute("replication");
|
||||
}}
|
||||
>
|
||||
<ListItemText primary="Replication" />
|
||||
</ListItem>
|
||||
<ListItem
|
||||
button
|
||||
disabled={
|
||||
]),
|
||||
to: getRoutePath("replication"),
|
||||
},
|
||||
}}
|
||||
{{
|
||||
tabConfig: {
|
||||
label: "Lifecycle",
|
||||
value: "lifecycle",
|
||||
component: Link,
|
||||
disabled:
|
||||
!distributedSetup ||
|
||||
!displayComponent(bucketInfo?.allowedActions, [
|
||||
S3_GET_LIFECYCLE_CONFIGURATION,
|
||||
S3_PUT_LIFECYCLE_CONFIGURATION,
|
||||
])
|
||||
}
|
||||
selected={selectedTab === "lifecycle"}
|
||||
onClick={() => {
|
||||
changeRoute("lifecycle");
|
||||
}}
|
||||
>
|
||||
<ListItemText primary="Lifecycle" />
|
||||
</ListItem>
|
||||
<ListItem
|
||||
button
|
||||
disabled={
|
||||
!displayComponent(bucketInfo?.allowedActions, [
|
||||
ADMIN_GET_POLICY,
|
||||
ADMIN_LIST_USER_POLICIES,
|
||||
ADMIN_LIST_USERS,
|
||||
])
|
||||
}
|
||||
selected={selectedTab === "access"}
|
||||
onClick={() => {
|
||||
changeRoute("access");
|
||||
}}
|
||||
>
|
||||
<ListItemText primary="Access Audit" />
|
||||
</ListItem>
|
||||
<ListItem
|
||||
button
|
||||
disabled={
|
||||
!displayComponent(bucketInfo?.allowedActions, [
|
||||
S3_GET_BUCKET_POLICY,
|
||||
])
|
||||
}
|
||||
selected={selectedTab === "prefix"}
|
||||
onClick={() => {
|
||||
changeRoute("prefix");
|
||||
}}
|
||||
>
|
||||
<ListItemText primary="Access Rules" />
|
||||
</ListItem>
|
||||
</List>
|
||||
</Grid>
|
||||
<Grid item xs={10}>
|
||||
<Router history={history}>
|
||||
<Switch>
|
||||
<Route
|
||||
path="/buckets/:bucketName/admin/summary"
|
||||
component={BucketSummaryPanel}
|
||||
/>
|
||||
<Route
|
||||
path="/buckets/:bucketName/admin/events"
|
||||
component={BucketEventsPanel}
|
||||
/>
|
||||
{distributedSetup && (
|
||||
<Route
|
||||
path="/buckets/:bucketName/admin/replication"
|
||||
component={BucketReplicationPanel}
|
||||
/>
|
||||
)}
|
||||
{distributedSetup && (
|
||||
<Route
|
||||
path="/buckets/:bucketName/admin/lifecycle"
|
||||
component={BucketLifecyclePanel}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Route
|
||||
path="/buckets/:bucketName/admin/access"
|
||||
component={AccessDetailsPanel}
|
||||
/>
|
||||
<Route
|
||||
path="/buckets/:bucketName/admin/prefix"
|
||||
component={AccessRulePanel}
|
||||
/>
|
||||
<Route
|
||||
path="/buckets/:bucketName"
|
||||
component={() => (
|
||||
<Redirect to={`/buckets/${bucketName}/admin/summary`} />
|
||||
)}
|
||||
/>
|
||||
</Switch>
|
||||
</Router>
|
||||
</Grid>
|
||||
</Grid>
|
||||
]),
|
||||
to: getRoutePath("lifecycle"),
|
||||
},
|
||||
}}
|
||||
{{
|
||||
tabConfig: {
|
||||
label: "Access Audit",
|
||||
value: "access",
|
||||
component: Link,
|
||||
disabled: !displayComponent(bucketInfo?.allowedActions, [
|
||||
ADMIN_GET_POLICY,
|
||||
ADMIN_LIST_USER_POLICIES,
|
||||
ADMIN_LIST_USERS,
|
||||
]),
|
||||
to: getRoutePath("access"),
|
||||
},
|
||||
}}
|
||||
{{
|
||||
tabConfig: {
|
||||
label: "Access Rules",
|
||||
value: "prefix",
|
||||
component: Link,
|
||||
disabled: !displayComponent(bucketInfo?.allowedActions, [
|
||||
S3_GET_BUCKET_POLICY,
|
||||
]),
|
||||
to: getRoutePath("prefix"),
|
||||
},
|
||||
}}
|
||||
</VerticalTabs>
|
||||
</PageLayout>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -48,9 +48,6 @@ const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
...searchField,
|
||||
...actionsTray,
|
||||
actionsTray: {
|
||||
...actionsTray.actionsTray,
|
||||
},
|
||||
twHeight: {
|
||||
minHeight: 400,
|
||||
},
|
||||
|
||||
@@ -34,7 +34,7 @@ import {
|
||||
BucketReplication,
|
||||
BucketVersioning,
|
||||
} from "../types";
|
||||
import { encodeFileName, niceBytes } from "../../../../common/utils";
|
||||
import { niceBytes } from "../../../../common/utils";
|
||||
import { Bucket, BucketList } from "../../Watch/types";
|
||||
import {
|
||||
buttonsStyles,
|
||||
|
||||
@@ -102,6 +102,7 @@ import {
|
||||
} from "../../../../../../types";
|
||||
import { setBucketDetailsLoad, setBucketInfo } from "../../../actions";
|
||||
import { AppState } from "../../../../../../store";
|
||||
import PageLayout from "../../../../Common/Layout/PageLayout";
|
||||
import BoxIconButton from "../../../../Common/BoxIconButton/BoxIconButton";
|
||||
|
||||
const commonIcon = {
|
||||
@@ -1063,7 +1064,7 @@ const ListObjects = ({
|
||||
/>
|
||||
)}
|
||||
|
||||
<Grid container>
|
||||
<PageLayout>
|
||||
<Grid item xs={12}>
|
||||
<ScreenTitle
|
||||
icon={
|
||||
@@ -1227,7 +1228,7 @@ const ListObjects = ({
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</PageLayout>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -26,16 +26,12 @@ import withStyles from "@mui/styles/withStyles";
|
||||
import {
|
||||
CircularProgress,
|
||||
LinearProgress,
|
||||
Paper,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableRow,
|
||||
Tooltip,
|
||||
} from "@mui/material";
|
||||
import ListItem from "@mui/material/ListItem";
|
||||
import ListItemText from "@mui/material/ListItemText";
|
||||
import List from "@mui/material/List";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import Chip from "@mui/material/Chip";
|
||||
import TextField from "@mui/material/TextField";
|
||||
@@ -53,7 +49,6 @@ import {
|
||||
} from "../../../../Common/FormComponents/common/styleLibrary";
|
||||
import { FileInfoResponse, IFileInfo } from "./types";
|
||||
import { download, extensionPreview } from "../utils";
|
||||
import { TabPanel } from "../../../../../shared/tabs";
|
||||
import history from "../../../../../../history";
|
||||
import api from "../../../../../../common/api";
|
||||
import ShareIcon from "../../../../../../icons/ShareIcon";
|
||||
@@ -93,6 +88,8 @@ import SearchIcon from "../../../../../../icons/SearchIcon";
|
||||
import ObjectBrowserIcon from "../../../../../../icons/ObjectBrowserIcon";
|
||||
import PreviewFileContent from "../Preview/PreviewFileContent";
|
||||
import RestoreFileVersion from "./RestoreFileVersion";
|
||||
import PageLayout from "../../../../Common/Layout/PageLayout";
|
||||
import VerticalTabs from "../../../../Common/VerticalTabs/VerticalTabs";
|
||||
import BoxIconButton from "../../../../Common/BoxIconButton/BoxIconButton";
|
||||
import { RecoverIcon } from "../../../../../../icons";
|
||||
|
||||
@@ -101,6 +98,10 @@ const styles = (theme: Theme) =>
|
||||
currentItemContainer: {
|
||||
marginBottom: 8,
|
||||
},
|
||||
pageContainer: {
|
||||
border: "1px solid #EAEAEA",
|
||||
height: "100%",
|
||||
},
|
||||
objectPathContainer: {
|
||||
marginBottom: 26,
|
||||
fontSize: 10,
|
||||
@@ -271,7 +272,6 @@ const ObjectDetails = ({
|
||||
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
|
||||
const [metadataLoad, setMetadataLoad] = useState<boolean>(true);
|
||||
const [metadata, setMetadata] = useState<any>({});
|
||||
const [selectedTab, setSelectedTab] = useState<number>(0);
|
||||
const [loadingBucket, setLoadingBucket] = useState<boolean>(false);
|
||||
const [bucketInfo, setBucketInfo] = useState<any>(null);
|
||||
const [restoreVersionOpen, setRestoreVersionOpen] = useState<boolean>(false);
|
||||
@@ -615,7 +615,7 @@ const ObjectDetails = ({
|
||||
/>
|
||||
)}
|
||||
|
||||
<Grid container>
|
||||
<PageLayout className={classes.pageContainer}>
|
||||
{!actualInfo && (
|
||||
<Grid item xs={12}>
|
||||
<LinearProgress />
|
||||
@@ -705,176 +705,135 @@ const ObjectDetails = ({
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={2}>
|
||||
<List component="nav" dense={true}>
|
||||
<ListItem
|
||||
button
|
||||
selected={selectedTab === 0}
|
||||
onClick={() => {
|
||||
setSelectedTab(0);
|
||||
}}
|
||||
>
|
||||
<ListItemText primary="Details" />
|
||||
</ListItem>
|
||||
<ListItem
|
||||
button
|
||||
selected={selectedTab === 1}
|
||||
onClick={() => {
|
||||
setSelectedTab(1);
|
||||
}}
|
||||
disabled={
|
||||
!(actualInfo.version_id && actualInfo.version_id !== "null")
|
||||
}
|
||||
>
|
||||
<ListItemText primary="Versions" />
|
||||
</ListItem>
|
||||
<ListItem
|
||||
button
|
||||
selected={selectedTab === 2}
|
||||
onClick={() => {
|
||||
setSelectedTab(2);
|
||||
}}
|
||||
disabled={extensionPreview(currentItem) === "none"}
|
||||
>
|
||||
<ListItemText primary="Preview" />
|
||||
</ListItem>
|
||||
</List>
|
||||
</Grid>
|
||||
<Grid item xs={10}>
|
||||
<Grid item xs={12}>
|
||||
<TabPanel index={0} value={selectedTab}>
|
||||
<div className={classes.actionsTray}>
|
||||
<h1 className={classes.sectionTitle}>Details</h1>
|
||||
</div>
|
||||
<br />
|
||||
{(displayObjectLegalHold ||
|
||||
displayObjectRetention ||
|
||||
displayObjectTag) && (
|
||||
<Fragment>
|
||||
<Paper className={classes.paperContainer}>
|
||||
<Grid container>
|
||||
<Grid item xs={10}>
|
||||
<table width={"100%"}>
|
||||
<tbody>
|
||||
{displayObjectLegalHold && (
|
||||
<tr>
|
||||
<td className={classes.titleCol}>
|
||||
Legal Hold:
|
||||
</td>
|
||||
<td className={classes.capitalizeFirst}>
|
||||
{actualInfo.version_id &&
|
||||
actualInfo.version_id !== "null" ? (
|
||||
<Fragment>
|
||||
{actualInfo.legal_hold_status
|
||||
? actualInfo.legal_hold_status.toLowerCase()
|
||||
: "Off"}
|
||||
{displayEditObjectLegalHold && (
|
||||
<IconButton
|
||||
color="primary"
|
||||
aria-label="legal-hold"
|
||||
size="small"
|
||||
className={classes.propertiesIcon}
|
||||
onClick={() => {
|
||||
setLegalholdOpen(true);
|
||||
}}
|
||||
>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
</Fragment>
|
||||
) : (
|
||||
"Disabled"
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{displayObjectRetention && (
|
||||
<tr>
|
||||
<td className={classes.titleCol}>
|
||||
Retention:
|
||||
</td>
|
||||
<td className={classes.capitalizeFirst}>
|
||||
{actualInfo.retention_mode
|
||||
? actualInfo.retention_mode.toLowerCase()
|
||||
: "None"}
|
||||
{displayEditObjectRetention && (
|
||||
<VerticalTabs>
|
||||
{{
|
||||
tabConfig: {
|
||||
label: "Details",
|
||||
},
|
||||
content: (
|
||||
<React.Fragment>
|
||||
<div className={classes.actionsTray}>
|
||||
<h1 className={classes.sectionTitle}>Details</h1>
|
||||
</div>
|
||||
<br />
|
||||
{(displayObjectLegalHold ||
|
||||
displayObjectRetention ||
|
||||
displayObjectTag) && (
|
||||
<Grid item xs={12}>
|
||||
<table width={"100%"}>
|
||||
<tbody>
|
||||
{displayObjectLegalHold && (
|
||||
<tr>
|
||||
<td className={classes.titleCol}>
|
||||
Legal Hold:
|
||||
</td>
|
||||
<td className={classes.capitalizeFirst}>
|
||||
{actualInfo.version_id &&
|
||||
actualInfo.version_id !== "null" ? (
|
||||
<Fragment>
|
||||
{actualInfo.legal_hold_status
|
||||
? actualInfo.legal_hold_status.toLowerCase()
|
||||
: "Off"}
|
||||
{displayEditObjectLegalHold && (
|
||||
<IconButton
|
||||
color="primary"
|
||||
aria-label="retention"
|
||||
aria-label="legal-hold"
|
||||
size="small"
|
||||
className={classes.propertiesIcon}
|
||||
onClick={() => {
|
||||
openRetentionModal();
|
||||
setLegalholdOpen(true);
|
||||
}}
|
||||
>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{displayObjectTag && (
|
||||
<tr>
|
||||
<td className={classes.titleCol}>Tags:</td>
|
||||
<td>
|
||||
{tagKeys &&
|
||||
tagKeys.map((tagKey, index) => {
|
||||
const tag = get(
|
||||
actualInfo,
|
||||
`tags.${tagKey}`,
|
||||
""
|
||||
);
|
||||
if (tag !== "") {
|
||||
return displayRemoveObjectTagging ? (
|
||||
<Chip
|
||||
key={`chip-${index}`}
|
||||
className={classes.tag}
|
||||
size="small"
|
||||
label={`${tagKey} : ${tag}`}
|
||||
color="primary"
|
||||
deleteIcon={<CloseIcon />}
|
||||
onDelete={() => {
|
||||
deleteTag(tagKey, tag);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Chip
|
||||
key={`chip-${index}`}
|
||||
className={classes.tag}
|
||||
size="small"
|
||||
label={`${tagKey} : ${tag}`}
|
||||
color="primary"
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})}
|
||||
{displayEditObjectTagging && (
|
||||
<Chip
|
||||
className={classes.tag}
|
||||
icon={<AddIcon />}
|
||||
clickable
|
||||
size="small"
|
||||
label="Add tag"
|
||||
color="primary"
|
||||
variant="outlined"
|
||||
onClick={() => {
|
||||
setTagModalOpen(true);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
<br />
|
||||
</Fragment>
|
||||
)}
|
||||
<Paper className={classes.paperContainer}>
|
||||
</Fragment>
|
||||
) : (
|
||||
"Disabled"
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{displayObjectRetention && (
|
||||
<tr>
|
||||
<td className={classes.titleCol}>Retention:</td>
|
||||
<td className={classes.capitalizeFirst}>
|
||||
{actualInfo.retention_mode
|
||||
? actualInfo.retention_mode.toLowerCase()
|
||||
: "None"}
|
||||
{displayEditObjectRetention && (
|
||||
<IconButton
|
||||
color="primary"
|
||||
aria-label="retention"
|
||||
size="small"
|
||||
className={classes.propertiesIcon}
|
||||
onClick={() => {
|
||||
openRetentionModal();
|
||||
}}
|
||||
>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
{displayObjectTag && (
|
||||
<tr>
|
||||
<td className={classes.titleCol}>Tags:</td>
|
||||
<td>
|
||||
{tagKeys &&
|
||||
tagKeys.map((tagKey, index) => {
|
||||
const tag = get(
|
||||
actualInfo,
|
||||
`tags.${tagKey}`,
|
||||
""
|
||||
);
|
||||
if (tag !== "") {
|
||||
return displayRemoveObjectTagging ? (
|
||||
<Chip
|
||||
key={`chip-${index}`}
|
||||
className={classes.tag}
|
||||
size="small"
|
||||
label={`${tagKey} : ${tag}`}
|
||||
color="primary"
|
||||
deleteIcon={<CloseIcon />}
|
||||
onDelete={() => {
|
||||
deleteTag(tagKey, tag);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<Chip
|
||||
key={`chip-${index}`}
|
||||
className={classes.tag}
|
||||
size="small"
|
||||
label={`${tagKey} : ${tag}`}
|
||||
color="primary"
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})}
|
||||
{displayEditObjectTagging && (
|
||||
<Chip
|
||||
className={classes.tag}
|
||||
icon={<AddIcon />}
|
||||
clickable
|
||||
size="small"
|
||||
label="Add tag"
|
||||
color="primary"
|
||||
variant="outlined"
|
||||
onClick={() => {
|
||||
setTagModalOpen(true);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</Grid>
|
||||
)}
|
||||
<Grid item xs={12}>
|
||||
<Grid item xs={12}>
|
||||
<h2>Object Metadata</h2>
|
||||
@@ -907,9 +866,17 @@ const ObjectDetails = ({
|
||||
</Table>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
</TabPanel>
|
||||
<TabPanel index={1} value={selectedTab}>
|
||||
</React.Fragment>
|
||||
),
|
||||
}}
|
||||
{{
|
||||
tabConfig: {
|
||||
label: "Versions",
|
||||
disabled: !(
|
||||
actualInfo.version_id && actualInfo.version_id !== "null"
|
||||
),
|
||||
},
|
||||
content: (
|
||||
<Fragment>
|
||||
<div className={classes.actionsTray}>
|
||||
<h1 className={classes.sectionTitle}>Versions</h1>
|
||||
@@ -987,27 +954,35 @@ const ObjectDetails = ({
|
||||
)}
|
||||
</Grid>
|
||||
</Fragment>
|
||||
</TabPanel>
|
||||
<TabPanel index={2} value={selectedTab}>
|
||||
{selectedTab === 2 && actualInfo && (
|
||||
<PreviewFileContent
|
||||
bucketName={bucketName}
|
||||
object={{
|
||||
name: actualInfo.name,
|
||||
version_id: actualInfo.version_id || "null",
|
||||
size: parseInt(actualInfo.size || "0"),
|
||||
content_type: "",
|
||||
last_modified: new Date(actualInfo.last_modified),
|
||||
}}
|
||||
isFullscreen
|
||||
/>
|
||||
)}
|
||||
</TabPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
),
|
||||
}}
|
||||
{{
|
||||
tabConfig: {
|
||||
label: "Preview",
|
||||
disabled: extensionPreview(currentItem) === "none",
|
||||
},
|
||||
content: (
|
||||
<React.Fragment>
|
||||
{actualInfo && (
|
||||
<PreviewFileContent
|
||||
bucketName={bucketName}
|
||||
object={{
|
||||
name: actualInfo.name,
|
||||
version_id: actualInfo.version_id || "null",
|
||||
size: parseInt(actualInfo.size || "0"),
|
||||
content_type: "",
|
||||
last_modified: new Date(actualInfo.last_modified),
|
||||
}}
|
||||
isFullscreen
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
),
|
||||
}}
|
||||
</VerticalTabs>
|
||||
</Fragment>
|
||||
)}
|
||||
</Grid>
|
||||
</PageLayout>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -177,8 +177,9 @@ export const containerForHeader = (bottomSpacing: any) => ({
|
||||
},
|
||||
},
|
||||
sectionTitle: {
|
||||
padding: "0px",
|
||||
margin: "0px",
|
||||
margin: 0,
|
||||
marginBottom: ".8rem",
|
||||
fontSize: "1.3rem",
|
||||
},
|
||||
topSpacer: {
|
||||
height: "8px",
|
||||
@@ -221,6 +222,7 @@ export const actionsTray = {
|
||||
actionsTray: {
|
||||
display: "flex" as const,
|
||||
justifyContent: "space-between" as const,
|
||||
marginBottom: "1rem",
|
||||
"& button": {
|
||||
flexGrow: 0,
|
||||
marginLeft: 8,
|
||||
@@ -988,6 +990,12 @@ export const commonDashboardInfocard = {
|
||||
},
|
||||
};
|
||||
|
||||
export const pageContentStyles = {
|
||||
contentSpacer: {
|
||||
padding: "2rem",
|
||||
},
|
||||
};
|
||||
|
||||
export const linkStyles = (color: string) => ({
|
||||
link: {
|
||||
textDecoration: "underline",
|
||||
|
||||
34
portal-ui/src/screens/Console/Common/Layout/PageLayout.tsx
Normal file
34
portal-ui/src/screens/Console/Common/Layout/PageLayout.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import React from "react";
|
||||
import { Grid } from "@mui/material";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
import { pageContentStyles } from "../FormComponents/common/styleLibrary";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
pageContainer: {
|
||||
width: "100%",
|
||||
},
|
||||
...pageContentStyles,
|
||||
});
|
||||
|
||||
type PageLayoutProps = {
|
||||
className?: string;
|
||||
classes?: any;
|
||||
children: any;
|
||||
};
|
||||
|
||||
const PageLayout = ({ classes, className = "", children }: PageLayoutProps) => {
|
||||
return (
|
||||
<div className={classes.contentSpacer}>
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={className}>
|
||||
{children}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(PageLayout);
|
||||
@@ -25,6 +25,7 @@ const styles = (theme: Theme) =>
|
||||
root: {
|
||||
padding: 0,
|
||||
margin: 0,
|
||||
fontSize: "1.2rem",
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -32,18 +32,37 @@ interface IScreenTitle {
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
headerBarIcon: {
|
||||
float: "left",
|
||||
paddingTop: 10,
|
||||
marginRight: 12,
|
||||
marginRight: ".7rem",
|
||||
color: theme.palette.primary.main,
|
||||
"& .MuiSvgIcon-root": {
|
||||
width: 44,
|
||||
height: 44,
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
},
|
||||
},
|
||||
headerBarSubheader: {
|
||||
color: "grey",
|
||||
},
|
||||
screenTitle: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
padding: "1rem",
|
||||
borderBottom: "1px solid #EAEAEA",
|
||||
},
|
||||
titleColumn: {
|
||||
height: "auto",
|
||||
justifyContent: "center",
|
||||
display: "flex",
|
||||
flexFlow: "column",
|
||||
alignItems: "flex-start",
|
||||
"& h1": {
|
||||
fontSize: "1.4rem",
|
||||
},
|
||||
},
|
||||
leftItems: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
},
|
||||
});
|
||||
|
||||
const ScreenTitle = ({
|
||||
@@ -55,16 +74,16 @@ const ScreenTitle = ({
|
||||
}: IScreenTitle) => {
|
||||
return (
|
||||
<Grid container>
|
||||
<Grid item xs={12} style={{ paddingTop: 8 }}>
|
||||
<div className={classes.headerBarIcon}>{icon}</div>
|
||||
<div style={{ float: "left" }}>
|
||||
<h1 style={{ margin: 0 }}>{title}</h1>
|
||||
<span className={classes.headerBarSubheader}>{subTitle}</span>
|
||||
<Grid item xs={12} className={classes.screenTitle}>
|
||||
<div className={classes.leftItems}>
|
||||
{icon ? <div className={classes.headerBarIcon}>{icon}</div> : null}
|
||||
<div className={classes.titleColumn}>
|
||||
<h1 style={{ margin: 0 }}>{title}</h1>
|
||||
<span className={classes.headerBarSubheader}>{subTitle}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ float: "right", paddingTop: 12 }}>{actions}</div>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<hr style={{ border: 0, borderTop: "1px solid #EAEAEA" }} />
|
||||
|
||||
<div>{actions}</div>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,163 @@
|
||||
import React from "react";
|
||||
import { Box, Tab, TabProps } from "@mui/material";
|
||||
import { TabPanel, TabContext, TabList } from "@mui/lab";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import useMediaQuery from "@mui/material/useMediaQuery";
|
||||
|
||||
export type TabItemProps = {
|
||||
tabConfig: TabProps | any;
|
||||
content?: JSX.Element | JSX.Element[];
|
||||
};
|
||||
|
||||
type VerticalTabsProps = {
|
||||
classes: any;
|
||||
children: TabItemProps[];
|
||||
selectedTab?: string;
|
||||
routes?: any;
|
||||
isRouteTabs?: boolean;
|
||||
};
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
tabsContainer: {
|
||||
display: "flex",
|
||||
height: "100%",
|
||||
},
|
||||
tabsHeaderContainer: {
|
||||
width: "300px",
|
||||
background: "#FBFAFA",
|
||||
borderRight: "1px solid #EAEAEA",
|
||||
"& .MuiTabs-root": {
|
||||
"& .MuiTabs-indicator": {
|
||||
display: "none",
|
||||
},
|
||||
"& .MuiTab-root": {
|
||||
display: "flex",
|
||||
flexFlow: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "flex-start",
|
||||
borderBottom: "1px solid #EAEAEA",
|
||||
"& .MuiSvgIcon-root": {
|
||||
marginRight: ".3rem",
|
||||
marginBottom: 0,
|
||||
height: ".8rem",
|
||||
width: ".8rem",
|
||||
},
|
||||
"&.Mui-selected": {
|
||||
background: "#E5E5E5",
|
||||
},
|
||||
},
|
||||
|
||||
"&. MuiTabs-scroller": {
|
||||
display: "none",
|
||||
},
|
||||
},
|
||||
},
|
||||
tabContentContainer: {
|
||||
width: "100%",
|
||||
"& .MuiTabPanel-root": {
|
||||
height: "100%",
|
||||
},
|
||||
},
|
||||
tabPanel: {
|
||||
height: "100%",
|
||||
},
|
||||
/*Below md breakpoint make it horizontal and style it for scrolling tabs*/
|
||||
"@media (max-width: 900px)": {
|
||||
tabsContainer: {
|
||||
flexFlow: "column",
|
||||
flexDirection: "column",
|
||||
},
|
||||
tabsHeaderContainer: {
|
||||
width: "100%",
|
||||
borderBottom: " 1px solid #EAEAEA",
|
||||
"& .MuiTabs-root .MuiTabs-scroller .MuiButtonBase-root": {
|
||||
borderBottom: " 0px",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const VerticalTabs = ({
|
||||
children,
|
||||
classes,
|
||||
selectedTab = "0",
|
||||
routes,
|
||||
isRouteTabs,
|
||||
}: VerticalTabsProps) => {
|
||||
const [value, setValue] = React.useState(selectedTab);
|
||||
|
||||
const theme = useTheme();
|
||||
const isSmallScreen = useMediaQuery(theme.breakpoints.down("md"));
|
||||
|
||||
const handleChange = (event: React.SyntheticEvent, newValue: string) => {
|
||||
setValue(newValue);
|
||||
};
|
||||
|
||||
const headerList: TabProps[] = [];
|
||||
const contentList: React.ReactNode[] = [];
|
||||
|
||||
if (!children) return null;
|
||||
|
||||
children.forEach((child) => {
|
||||
headerList.push(child.tabConfig);
|
||||
contentList.push(child.content);
|
||||
});
|
||||
|
||||
return (
|
||||
<TabContext value={`${value}`}>
|
||||
<Box className={classes.tabsContainer}>
|
||||
<Box className={classes.tabsHeaderContainer}>
|
||||
<TabList
|
||||
onChange={handleChange}
|
||||
orientation={isSmallScreen ? "horizontal" : "vertical"}
|
||||
variant={isSmallScreen ? "scrollable" : "standard"}
|
||||
scrollButtons="auto"
|
||||
className={classes.tabList}
|
||||
>
|
||||
{headerList.map((item, index) => {
|
||||
if (item) {
|
||||
return (
|
||||
<Tab
|
||||
className={classes.tabHeader}
|
||||
key={`v-tab-${index}`}
|
||||
value={`${index}`}
|
||||
{...item}
|
||||
disableRipple
|
||||
disableTouchRipple
|
||||
focusRipple={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})}
|
||||
</TabList>
|
||||
</Box>
|
||||
|
||||
<Box className={classes.tabContentContainer}>
|
||||
{!isRouteTabs
|
||||
? contentList.map((item, index) => {
|
||||
return (
|
||||
<TabPanel
|
||||
classes={{ ...classes.tabPanel }}
|
||||
key={`v-tab-p-${index}`}
|
||||
value={`${index}`}
|
||||
>
|
||||
{item ? item : null}
|
||||
</TabPanel>
|
||||
);
|
||||
})
|
||||
: null}
|
||||
{isRouteTabs ? (
|
||||
<div className={classes.tabPanel}>{routes}</div>
|
||||
) : null}
|
||||
</Box>
|
||||
</Box>
|
||||
</TabContext>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(VerticalTabs);
|
||||
@@ -298,9 +298,6 @@ const DirectCSIMain = ({
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
{notAvailable && !loading ? (
|
||||
<div className={classes.notAvailableNotice}>
|
||||
|
||||
@@ -43,6 +43,7 @@ import SearchIcon from "../../../icons/SearchIcon";
|
||||
import HelpBox from "../../../common/HelpBox";
|
||||
import history from "../../../history";
|
||||
import AButton from "../Common/AButton/AButton";
|
||||
import PageLayout from "../Common/Layout/PageLayout";
|
||||
|
||||
interface IGroupsProps {
|
||||
classes: any;
|
||||
@@ -55,6 +56,10 @@ const styles = (theme: Theme) =>
|
||||
seeMore: {
|
||||
marginTop: theme.spacing(3),
|
||||
},
|
||||
pageContainer: {
|
||||
border: "1px solid #EAEAEA",
|
||||
width: "100%",
|
||||
},
|
||||
paper: {
|
||||
// padding: theme.spacing(2),
|
||||
display: "flex",
|
||||
@@ -187,125 +192,123 @@ const Groups = ({ classes, setErrorSnackMessage }: IGroupsProps) => {
|
||||
/>
|
||||
)}
|
||||
<PageHeader label={"Groups"} />
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.container}>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Search Groups"
|
||||
className={classes.searchField}
|
||||
id="search-resource"
|
||||
label=""
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
onChange={(e) => {
|
||||
setFilter(e.target.value);
|
||||
}}
|
||||
variant="standard"
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
endIcon={<AddIcon />}
|
||||
onClick={() => {
|
||||
setSelectedGroup(null);
|
||||
setGroupOpen(true);
|
||||
}}
|
||||
>
|
||||
Create Group
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
{loading && <LinearProgress />}
|
||||
{!loading && (
|
||||
<Fragment>
|
||||
{records.length > 0 && (
|
||||
<Fragment>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[{ label: "Name", elementKey: "" }]}
|
||||
isLoading={loading}
|
||||
records={filteredRecords}
|
||||
entityName="Groups"
|
||||
idField=""
|
||||
customPaperHeight={classes.twHeight}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<HelpBox
|
||||
title={"Groups"}
|
||||
iconComponent={<GroupsIcon />}
|
||||
help={
|
||||
<Fragment>
|
||||
A group can have one attached IAM policy, where all
|
||||
users with membership in that group inherit that
|
||||
policy. Groups support more simplified management of
|
||||
user permissions on the MinIO Tenant.
|
||||
<br />
|
||||
<br />
|
||||
You can learn more at our{" "}
|
||||
<a
|
||||
href="https://docs.min.io/minio/k8s/tutorials/group-management.html?ref=con"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
documentation
|
||||
</a>
|
||||
.
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
)}
|
||||
{records.length === 0 && (
|
||||
<Grid
|
||||
container
|
||||
justifyContent={"center"}
|
||||
alignContent={"center"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
<Grid item xs={8}>
|
||||
<HelpBox
|
||||
title={"Groups"}
|
||||
iconComponent={<UsersIcon />}
|
||||
help={
|
||||
<Fragment>
|
||||
A group can have one attached IAM policy, where all
|
||||
users with membership in that group inherit that
|
||||
policy. Groups support more simplified management of
|
||||
user permissions on the MinIO Tenant.
|
||||
<br />
|
||||
<br />
|
||||
To get started,{" "}
|
||||
<AButton
|
||||
onClick={() => {
|
||||
setSelectedGroup(null);
|
||||
setGroupOpen(true);
|
||||
}}
|
||||
>
|
||||
Create a Group
|
||||
</AButton>
|
||||
.
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
<PageLayout>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Search Groups"
|
||||
className={classes.searchField}
|
||||
id="search-resource"
|
||||
label=""
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
onChange={(e) => {
|
||||
setFilter(e.target.value);
|
||||
}}
|
||||
variant="standard"
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
endIcon={<AddIcon />}
|
||||
onClick={() => {
|
||||
setSelectedGroup(null);
|
||||
setGroupOpen(true);
|
||||
}}
|
||||
>
|
||||
Create Group
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
{loading && <LinearProgress />}
|
||||
{!loading && (
|
||||
<Fragment>
|
||||
{records.length > 0 && (
|
||||
<Fragment>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[{ label: "Name", elementKey: "" }]}
|
||||
isLoading={loading}
|
||||
records={filteredRecords}
|
||||
entityName="Groups"
|
||||
idField=""
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<HelpBox
|
||||
title={"Groups"}
|
||||
iconComponent={<GroupsIcon />}
|
||||
help={
|
||||
<Fragment>
|
||||
A group can have one attached IAM policy, where all
|
||||
users with membership in that group inherit that policy.
|
||||
Groups support more simplified management of user
|
||||
permissions on the MinIO Tenant.
|
||||
<br />
|
||||
<br />
|
||||
You can learn more at our{" "}
|
||||
<a
|
||||
href="https://docs.min.io/minio/k8s/tutorials/group-management.html?ref=con"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
documentation
|
||||
</a>
|
||||
.
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
)}
|
||||
{records.length === 0 && (
|
||||
<Grid
|
||||
container
|
||||
justifyContent={"center"}
|
||||
alignContent={"center"}
|
||||
alignItems={"center"}
|
||||
>
|
||||
<Grid item xs={8}>
|
||||
<HelpBox
|
||||
title={"Groups"}
|
||||
iconComponent={<UsersIcon />}
|
||||
help={
|
||||
<Fragment>
|
||||
A group can have one attached IAM policy, where all
|
||||
users with membership in that group inherit that policy.
|
||||
Groups support more simplified management of user
|
||||
permissions on the MinIO Tenant.
|
||||
<br />
|
||||
<br />
|
||||
To get started,{" "}
|
||||
<AButton
|
||||
onClick={() => {
|
||||
setSelectedGroup(null);
|
||||
setGroupOpen(true);
|
||||
}}
|
||||
>
|
||||
Create a Group
|
||||
</AButton>
|
||||
.
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
</PageLayout>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -17,10 +17,6 @@ import withStyles from "@mui/styles/withStyles";
|
||||
import { Button, Grid, IconButton, Tooltip } from "@mui/material";
|
||||
import ScreenTitle from "../Common/ScreenTitle/ScreenTitle";
|
||||
import { DeleteIcon, IAMPoliciesIcon, UsersIcon } from "../../../icons";
|
||||
import List from "@mui/material/List";
|
||||
import ListItem from "@mui/material/ListItem";
|
||||
import ListItemText from "@mui/material/ListItemText";
|
||||
import { TabPanel } from "../../shared/tabs";
|
||||
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||
import history from "../../../history";
|
||||
import api from "../../../common/api";
|
||||
@@ -28,17 +24,33 @@ import SetPolicy from "../Policies/SetPolicy";
|
||||
import AddGroupMember from "./AddGroupMember";
|
||||
import { ErrorResponseHandler } from "../../../common/types";
|
||||
import DeleteGroup from "./DeleteGroup";
|
||||
import VerticalTabs from "../Common/VerticalTabs/VerticalTabs";
|
||||
import FormSwitchWrapper from "../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
|
||||
import PageLayout from "../Common/Layout/PageLayout";
|
||||
import BackLink from "../../../common/BackLink";
|
||||
import PanelTitle from "../Common/PanelTitle/PanelTitle";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
pageContainer: {
|
||||
border: "1px solid #EAEAEA",
|
||||
width: "100%",
|
||||
},
|
||||
breadcrumLink: {
|
||||
textDecoration: "none",
|
||||
color: "black",
|
||||
},
|
||||
statusLabel: {
|
||||
fontSize: ".8rem",
|
||||
marginRight: ".5rem",
|
||||
},
|
||||
statusValue: {
|
||||
fontWeight: "bold",
|
||||
fontSize: ".9rem",
|
||||
marginRight: ".5rem",
|
||||
},
|
||||
...actionsTray,
|
||||
...searchField,
|
||||
actionsTray: { ...actionsTray.actionsTray },
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
|
||||
@@ -48,11 +60,6 @@ interface IGroupDetailsProps {
|
||||
setErrorSnackMessage: typeof setErrorSnackMessage;
|
||||
}
|
||||
|
||||
type TabItemsProps = {
|
||||
activeTab: number;
|
||||
onTabChange: (tab: number) => void;
|
||||
};
|
||||
|
||||
type DetailsHeaderProps = {
|
||||
classes: any;
|
||||
};
|
||||
@@ -64,31 +71,6 @@ type GroupInfo = {
|
||||
status?: string;
|
||||
};
|
||||
|
||||
const TabItems = ({ activeTab, onTabChange }: TabItemsProps) => {
|
||||
return (
|
||||
<List component="nav" dense={true}>
|
||||
<ListItem
|
||||
button
|
||||
selected={activeTab === 0}
|
||||
onClick={() => {
|
||||
onTabChange(0);
|
||||
}}
|
||||
>
|
||||
<ListItemText primary="Members" />
|
||||
</ListItem>
|
||||
<ListItem
|
||||
button
|
||||
selected={activeTab === 1}
|
||||
onClick={() => {
|
||||
onTabChange(1);
|
||||
}}
|
||||
>
|
||||
<ListItemText primary="Policies" />
|
||||
</ListItem>
|
||||
</List>
|
||||
);
|
||||
};
|
||||
|
||||
export const formatPolicy = (policy: string = ""): string[] => {
|
||||
if (policy.length <= 0) return [];
|
||||
return policy.split(",");
|
||||
@@ -114,7 +96,6 @@ const GroupDetailsHeader = ({ classes }: DetailsHeaderProps) => {
|
||||
};
|
||||
|
||||
const GroupsDetails = ({ classes }: IGroupDetailsProps) => {
|
||||
const [currentTab, setCurrentTab] = useState<number>(0);
|
||||
const [groupDetails, setGroupDetails] = useState<GroupInfo>({});
|
||||
|
||||
/*Modals*/
|
||||
@@ -143,7 +124,8 @@ const GroupsDetails = ({ classes }: IGroupDetailsProps) => {
|
||||
.then((res: any) => {
|
||||
setGroupDetails(res);
|
||||
})
|
||||
.catch(() => {
|
||||
.catch((err) => {
|
||||
setModalErrorSnackMessage(err);
|
||||
setGroupDetails({});
|
||||
});
|
||||
}
|
||||
@@ -163,10 +145,74 @@ const GroupsDetails = ({ classes }: IGroupDetailsProps) => {
|
||||
});
|
||||
}
|
||||
|
||||
const groupsTabContent = (
|
||||
<React.Fragment>
|
||||
<div className={classes.actionsTray}>
|
||||
<PanelTitle>Members</PanelTitle>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
endIcon={<UsersIcon />}
|
||||
size="medium"
|
||||
onClick={() => {
|
||||
setUsersOpen(true);
|
||||
}}
|
||||
>
|
||||
{memberActionText}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<TableWrapper
|
||||
columns={[{ label: "Access Key", elementKey: "" }]}
|
||||
selectedItems={[]}
|
||||
isLoading={false}
|
||||
records={members}
|
||||
entityName="Users"
|
||||
idField=""
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
const policiesTabContent = (
|
||||
<React.Fragment>
|
||||
<div className={classes.actionsTray}>
|
||||
<PanelTitle>Policies</PanelTitle>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
endIcon={<IAMPoliciesIcon />}
|
||||
size="medium"
|
||||
onClick={() => {
|
||||
setPolicyOpen(true);
|
||||
}}
|
||||
>
|
||||
Set Policies
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<TableWrapper
|
||||
itemActions={[
|
||||
{
|
||||
type: "view",
|
||||
onClick: (policy) => {
|
||||
history.push(`/policies/${policy}`);
|
||||
},
|
||||
},
|
||||
]}
|
||||
columns={[{ label: "Policy", elementKey: "" }]}
|
||||
isLoading={false}
|
||||
records={groupPolicies}
|
||||
entityName="Policies"
|
||||
idField=""
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
return (
|
||||
<React.Fragment>
|
||||
<GroupDetailsHeader classes={classes} />
|
||||
<Grid container className={classes.container}>
|
||||
<BackLink to={"/groups"} label={"Return to Groups"} />
|
||||
|
||||
<PageLayout className={classes.pageContainer}>
|
||||
<Grid item xs={12}>
|
||||
<ScreenTitle
|
||||
icon={
|
||||
@@ -175,21 +221,24 @@ const GroupsDetails = ({ classes }: IGroupDetailsProps) => {
|
||||
</Fragment>
|
||||
}
|
||||
title={groupName}
|
||||
subTitle={
|
||||
<Fragment>
|
||||
Status: {isGroupEnabled ? "Enabled" : "Disabled"}
|
||||
</Fragment>
|
||||
}
|
||||
subTitle={null}
|
||||
actions={
|
||||
<Fragment>
|
||||
<Button
|
||||
onClick={() => {
|
||||
<span className={classes.statusLabel}>Group Status:</span>
|
||||
<span className={classes.statusValue}>
|
||||
{isGroupEnabled ? "Enabled" : "Disabled"}
|
||||
</span>
|
||||
<FormSwitchWrapper
|
||||
indicatorLabels={["Enabled", "Disabled"]}
|
||||
checked={isGroupEnabled}
|
||||
value={"group_enabled"}
|
||||
id="group-status"
|
||||
name="group-status"
|
||||
onChange={() => {
|
||||
toggleGroupStatus(!isGroupEnabled);
|
||||
}}
|
||||
color={"primary"}
|
||||
>
|
||||
{isGroupEnabled ? "Disable" : "Enable"}
|
||||
</Button>
|
||||
switchOnly
|
||||
/>
|
||||
<Tooltip title="Delete User">
|
||||
<IconButton
|
||||
color="primary"
|
||||
@@ -208,80 +257,19 @@ const GroupsDetails = ({ classes }: IGroupDetailsProps) => {
|
||||
/>
|
||||
</Grid>
|
||||
|
||||
<Grid item xs={2}>
|
||||
<TabItems
|
||||
activeTab={currentTab}
|
||||
onTabChange={(num) => {
|
||||
setCurrentTab(num);
|
||||
<Grid item xs={12}>
|
||||
<VerticalTabs>
|
||||
{{
|
||||
tabConfig: { label: "Members" },
|
||||
content: groupsTabContent,
|
||||
}}
|
||||
/>
|
||||
{{
|
||||
tabConfig: { label: "Policies" },
|
||||
content: policiesTabContent,
|
||||
}}
|
||||
</VerticalTabs>
|
||||
</Grid>
|
||||
<Grid item xs={10}>
|
||||
<Grid item xs={12}>
|
||||
<TabPanel index={0} value={currentTab}>
|
||||
<div className={classes.actionsTray}>
|
||||
<PanelTitle>Members</PanelTitle>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
endIcon={<UsersIcon />}
|
||||
size="medium"
|
||||
onClick={() => {
|
||||
setUsersOpen(true);
|
||||
}}
|
||||
>
|
||||
{memberActionText}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<TableWrapper
|
||||
//itemActions={tableActions}
|
||||
columns={[{ label: "Access Key", elementKey: "" }]}
|
||||
// onSelect={selectionChanged}
|
||||
selectedItems={[]}
|
||||
isLoading={false}
|
||||
records={members}
|
||||
entityName="Users"
|
||||
idField=""
|
||||
customPaperHeight={classes.twHeight}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel index={1} value={currentTab}>
|
||||
<div className={classes.actionsTray}>
|
||||
<PanelTitle>Policies</PanelTitle>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
endIcon={<IAMPoliciesIcon />}
|
||||
size="medium"
|
||||
onClick={() => {
|
||||
setPolicyOpen(true);
|
||||
}}
|
||||
>
|
||||
Set Policies
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<TableWrapper
|
||||
itemActions={[
|
||||
{
|
||||
type: "view",
|
||||
onClick: (policy) => {
|
||||
history.push(`/policies/${policy}`);
|
||||
},
|
||||
},
|
||||
]}
|
||||
columns={[{ label: "Policy", elementKey: "" }]}
|
||||
isLoading={false}
|
||||
records={groupPolicies}
|
||||
entityName="Policies"
|
||||
idField=""
|
||||
/>
|
||||
</TabPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
</PageLayout>
|
||||
{/*Modals*/}
|
||||
{policyOpen ? (
|
||||
<SetPolicy
|
||||
|
||||
@@ -41,6 +41,7 @@ import api from "../../../common/api";
|
||||
import history from "../../../history";
|
||||
import SearchIcon from "../../../icons/SearchIcon";
|
||||
import HelpBox from "../../../common/HelpBox";
|
||||
import PageLayout from "../Common/Layout/PageLayout";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -52,10 +53,9 @@ const styles = (theme: Theme) =>
|
||||
overflow: "auto",
|
||||
flexDirection: "column",
|
||||
},
|
||||
|
||||
addSideBar: {
|
||||
width: "320px",
|
||||
padding: "20px",
|
||||
width: 320,
|
||||
padding: 20,
|
||||
},
|
||||
tableToolbar: {
|
||||
paddingLeft: theme.spacing(2),
|
||||
@@ -179,7 +179,7 @@ const ListPolicies = ({ classes, setErrorSnackMessage }: IPoliciesProps) => {
|
||||
/>
|
||||
)}
|
||||
<PageHeader label="IAM Policies" />
|
||||
<Grid container className={classes.container}>
|
||||
<PageLayout className={classes.pageContainer}>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Search Policies"
|
||||
@@ -222,7 +222,6 @@ const ListPolicies = ({ classes, setErrorSnackMessage }: IPoliciesProps) => {
|
||||
records={filteredRecords}
|
||||
entityName="Policies"
|
||||
idField="name"
|
||||
customPaperHeight={classes.twHeight}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
@@ -258,7 +257,7 @@ const ListPolicies = ({ classes, setErrorSnackMessage }: IPoliciesProps) => {
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</PageLayout>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -39,14 +39,14 @@ import CodeMirrorWrapper from "../Common/FormComponents/CodeMirrorWrapper/CodeMi
|
||||
import history from "../../../history";
|
||||
import InputAdornment from "@mui/material/InputAdornment";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import ListItem from "@mui/material/ListItem";
|
||||
import ListItemText from "@mui/material/ListItemText";
|
||||
import List from "@mui/material/List";
|
||||
import ScreenTitle from "../Common/ScreenTitle/ScreenTitle";
|
||||
import IAMPoliciesIcon from "../../../icons/IAMPoliciesIcon";
|
||||
import RefreshIcon from "../../../icons/RefreshIcon";
|
||||
import SearchIcon from "../../../icons/SearchIcon";
|
||||
import TrashIcon from "../../../icons/TrashIcon";
|
||||
import PageLayout from "../Common/Layout/PageLayout";
|
||||
import VerticalTabs from "../Common/VerticalTabs/VerticalTabs";
|
||||
import BackLink from "../../../common/BackLink";
|
||||
import BoxIconButton from "../Common/BoxIconButton/BoxIconButton";
|
||||
|
||||
interface IPolicyDetailsProps {
|
||||
@@ -61,6 +61,10 @@ const styles = (theme: Theme) =>
|
||||
buttonContainer: {
|
||||
textAlign: "right",
|
||||
},
|
||||
pageContainer: {
|
||||
border: "1px solid #EAEAEA",
|
||||
height: "100%",
|
||||
},
|
||||
multiContainer: {
|
||||
display: "flex",
|
||||
alignItems: "center" as const,
|
||||
@@ -75,6 +79,7 @@ const styles = (theme: Theme) =>
|
||||
},
|
||||
paperContainer: {
|
||||
padding: "15px 15px 15px 50px",
|
||||
minHeight: "450px",
|
||||
},
|
||||
infoGrid: {
|
||||
display: "grid",
|
||||
@@ -190,7 +195,6 @@ const PolicyDetails = ({
|
||||
setErrorSnackMessage,
|
||||
setSnackBarMessage,
|
||||
}: IPolicyDetailsProps) => {
|
||||
const [selectedTab, setSelectedTab] = useState<number>(0);
|
||||
const [policy, setPolicy] = useState<Policy | null>(null);
|
||||
const [policyStatements, setPolicyStatements] = useState<IAMStatement[]>([]);
|
||||
const [userList, setUserList] = useState<string[]>([]);
|
||||
@@ -352,7 +356,12 @@ const PolicyDetails = ({
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
<Grid container className={classes.container}>
|
||||
<BackLink
|
||||
to={"/policies"}
|
||||
label={"Return to Policies"}
|
||||
classes={classes}
|
||||
/>
|
||||
<PageLayout className={classes.pageContainer}>
|
||||
<Grid item xs={12}>
|
||||
<ScreenTitle
|
||||
icon={
|
||||
@@ -392,245 +401,227 @@ const PolicyDetails = ({
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={2}>
|
||||
<List component="nav" dense={true}>
|
||||
<ListItem
|
||||
button
|
||||
selected={selectedTab === 0}
|
||||
onClick={() => {
|
||||
setSelectedTab(0);
|
||||
}}
|
||||
>
|
||||
<ListItemText primary="Summary" />
|
||||
</ListItem>
|
||||
<ListItem
|
||||
button
|
||||
selected={selectedTab === 1}
|
||||
onClick={() => {
|
||||
setSelectedTab(1);
|
||||
}}
|
||||
>
|
||||
<ListItemText primary="Users" />
|
||||
</ListItem>
|
||||
<ListItem
|
||||
button
|
||||
selected={selectedTab === 2}
|
||||
onClick={() => {
|
||||
setSelectedTab(2);
|
||||
}}
|
||||
>
|
||||
<ListItemText primary="Groups" />
|
||||
</ListItem>
|
||||
<ListItem
|
||||
button
|
||||
selected={selectedTab === 3}
|
||||
onClick={() => {
|
||||
setSelectedTab(3);
|
||||
}}
|
||||
>
|
||||
<ListItemText primary="JSON" />
|
||||
</ListItem>
|
||||
</List>
|
||||
</Grid>
|
||||
<Grid item xs={10}>
|
||||
{selectedTab === 0 && (
|
||||
<Fragment>
|
||||
<h1 className={classes.sectionTitle}>Summary</h1>
|
||||
<Paper className={classes.paperContainer}>
|
||||
<form
|
||||
noValidate
|
||||
autoComplete="off"
|
||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||
saveRecord(e);
|
||||
}}
|
||||
>
|
||||
<Grid container>
|
||||
<Grid item xs={8}>
|
||||
<h4>Statements</h4>
|
||||
</Grid>
|
||||
<Grid item xs={4} />
|
||||
|
||||
<Fragment>
|
||||
{policyStatements.map((stmt, i) => {
|
||||
return (
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
className={classes.statement}
|
||||
key={`s-${i}`}
|
||||
>
|
||||
<Grid container>
|
||||
<Grid item xs={2} className={classes.labelCol}>
|
||||
Effect
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<Fragment>{stmt.Effect}</Fragment>
|
||||
</Grid>
|
||||
<Grid item xs={2} className={classes.labelCol} />
|
||||
<Grid item xs={4} />
|
||||
<Grid item xs={2} className={classes.labelCol}>
|
||||
Actions
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<ul>
|
||||
{stmt.Action &&
|
||||
stmt.Action.map((act, actIndex) => (
|
||||
<li key={`${i}-r-${actIndex}`}>{act}</li>
|
||||
))}
|
||||
</ul>
|
||||
</Grid>
|
||||
<Grid item xs={2} className={classes.labelCol}>
|
||||
Resources
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<ul>
|
||||
{stmt.Resource &&
|
||||
stmt.Resource.map((res, resIndex) => (
|
||||
<li key={`${i}-r-${resIndex}`}>{res}</li>
|
||||
))}
|
||||
</ul>
|
||||
<VerticalTabs>
|
||||
{{
|
||||
tabConfig: { label: "Summary" },
|
||||
content: (
|
||||
<Fragment>
|
||||
<div className={classes.sectionTitle}>Policy Summary</div>
|
||||
<Paper className={classes.paperContainer}>
|
||||
<form
|
||||
noValidate
|
||||
autoComplete="off"
|
||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||
saveRecord(e);
|
||||
}}
|
||||
>
|
||||
<Grid container>
|
||||
<Grid item xs={8}>
|
||||
<h4>Statements</h4>
|
||||
</Grid>
|
||||
<Grid item xs={4} />
|
||||
|
||||
<Fragment>
|
||||
{policyStatements.map((stmt, i) => {
|
||||
return (
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
className={classes.statement}
|
||||
key={`s-${i}`}
|
||||
>
|
||||
<Grid container>
|
||||
<Grid item xs={2} className={classes.labelCol}>
|
||||
Effect
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<Fragment>{stmt.Effect}</Fragment>
|
||||
</Grid>
|
||||
<Grid
|
||||
item
|
||||
xs={2}
|
||||
className={classes.labelCol}
|
||||
/>
|
||||
<Grid item xs={4} />
|
||||
<Grid item xs={2} className={classes.labelCol}>
|
||||
Actions
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<ul>
|
||||
{stmt.Action &&
|
||||
stmt.Action.map((act, actIndex) => (
|
||||
<li key={`${i}-r-${actIndex}`}>
|
||||
{act}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Grid>
|
||||
<Grid item xs={2} className={classes.labelCol}>
|
||||
Resources
|
||||
</Grid>
|
||||
<Grid item xs={4}>
|
||||
<ul>
|
||||
{stmt.Resource &&
|
||||
stmt.Resource.map((res, resIndex) => (
|
||||
<li key={`${i}-r-${resIndex}`}>
|
||||
{res}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
})}
|
||||
</Fragment>
|
||||
</Grid>
|
||||
</form>
|
||||
</Paper>
|
||||
</Fragment>
|
||||
)}
|
||||
{selectedTab === 1 && (
|
||||
<Fragment>
|
||||
<h1 className={classes.sectionTitle}>Users</h1>
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Search Users"
|
||||
className={classes.searchField}
|
||||
id="search-resource"
|
||||
label=""
|
||||
onChange={(val) => {
|
||||
setFilterUsers(val.target.value);
|
||||
}}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
variant="standard"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<br />
|
||||
</Grid>
|
||||
<TableWrapper
|
||||
itemActions={userTableActions}
|
||||
columns={[{ label: "Name", elementKey: "name" }]}
|
||||
isLoading={loadingUsers}
|
||||
records={filteredUsers}
|
||||
entityName="Users"
|
||||
idField="name"
|
||||
/>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
)}
|
||||
{selectedTab === 2 && (
|
||||
<Fragment>
|
||||
<h1 className={classes.sectionTitle}>Groups</h1>
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Search Groups"
|
||||
className={classes.searchField}
|
||||
id="search-resource"
|
||||
label=""
|
||||
onChange={(val) => {
|
||||
setFilterGroups(val.target.value);
|
||||
}}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
variant="standard"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<br />
|
||||
</Grid>
|
||||
<TableWrapper
|
||||
itemActions={[]}
|
||||
columns={[{ label: "Name", elementKey: "name" }]}
|
||||
isLoading={loadingGroups}
|
||||
records={filteredGroups}
|
||||
entityName="Groups"
|
||||
idField="name"
|
||||
/>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
)}
|
||||
{selectedTab === 3 && (
|
||||
<Fragment>
|
||||
<h1 className={classes.sectionTitle}>Raw Policy</h1>
|
||||
<Paper className={classes.paperContainer}>
|
||||
<form
|
||||
noValidate
|
||||
autoComplete="off"
|
||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||
saveRecord(e);
|
||||
}}
|
||||
>
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.formScrollable}>
|
||||
<CodeMirrorWrapper
|
||||
value={policyDefinition}
|
||||
onBeforeChange={(editor, data, value) => {
|
||||
setPolicyDefinition(value);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Fragment>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.buttonContainer}>
|
||||
{!policy && (
|
||||
<button
|
||||
type="button"
|
||||
color="primary"
|
||||
className={classes.clearButton}
|
||||
onClick={() => {
|
||||
resetForm();
|
||||
</form>
|
||||
</Paper>
|
||||
</Fragment>
|
||||
),
|
||||
}}
|
||||
{{
|
||||
tabConfig: { label: "Users" },
|
||||
content: (
|
||||
<Fragment>
|
||||
<div className={classes.sectionTitle}>Users</div>
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Search Users"
|
||||
className={classes.searchField}
|
||||
id="search-resource"
|
||||
label=""
|
||||
onChange={(val) => {
|
||||
setFilterUsers(val.target.value);
|
||||
}}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
variant="standard"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<br />
|
||||
</Grid>
|
||||
<TableWrapper
|
||||
itemActions={userTableActions}
|
||||
columns={[{ label: "Name", elementKey: "name" }]}
|
||||
isLoading={loadingUsers}
|
||||
records={filteredUsers}
|
||||
entityName="Users"
|
||||
idField="name"
|
||||
/>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
),
|
||||
}}
|
||||
{{
|
||||
tabConfig: { label: "Groups" },
|
||||
content: (
|
||||
<Fragment>
|
||||
<div className={classes.sectionTitle}>Groups</div>
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Search Groups"
|
||||
className={classes.searchField}
|
||||
id="search-resource"
|
||||
label=""
|
||||
onChange={(val) => {
|
||||
setFilterGroups(val.target.value);
|
||||
}}
|
||||
InputProps={{
|
||||
disableUnderline: true,
|
||||
startAdornment: (
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
variant="standard"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<br />
|
||||
</Grid>
|
||||
<TableWrapper
|
||||
itemActions={[]}
|
||||
columns={[{ label: "Name", elementKey: "name" }]}
|
||||
isLoading={loadingGroups}
|
||||
records={filteredGroups}
|
||||
entityName="Groups"
|
||||
idField="name"
|
||||
/* customPaperHeight={classes.tableHeight}*/
|
||||
/>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
),
|
||||
}}
|
||||
{{
|
||||
tabConfig: { label: "Raw Policy" },
|
||||
content: (
|
||||
<Fragment>
|
||||
<div className={classes.sectionTitle}>Raw Policy</div>
|
||||
<Paper className={classes.paperContainer}>
|
||||
<form
|
||||
noValidate
|
||||
autoComplete="off"
|
||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||
saveRecord(e);
|
||||
}}
|
||||
>
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.formScrollable}>
|
||||
<CodeMirrorWrapper
|
||||
value={policyDefinition}
|
||||
onBeforeChange={(editor, data, value) => {
|
||||
setPolicyDefinition(value);
|
||||
}}
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
)}
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={addLoading || !validSave}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Grid>
|
||||
{addLoading && (
|
||||
<Grid item xs={12}>
|
||||
<LinearProgress />
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</form>
|
||||
</Paper>
|
||||
</Fragment>
|
||||
)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.buttonContainer}>
|
||||
{!policy && (
|
||||
<button
|
||||
type="button"
|
||||
color="primary"
|
||||
className={classes.clearButton}
|
||||
onClick={() => {
|
||||
resetForm();
|
||||
}}
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
)}
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={addLoading || !validSave}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Grid>
|
||||
{addLoading && (
|
||||
<Grid item xs={12}>
|
||||
<LinearProgress />
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</form>
|
||||
</Paper>
|
||||
</Fragment>
|
||||
),
|
||||
}}
|
||||
</VerticalTabs>
|
||||
</PageLayout>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -18,18 +18,19 @@ import React, { Fragment, useState, useEffect } from "react";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
import { Grid, ListItem, ListItemText } from "@mui/material";
|
||||
import { Route, Router, Switch, Redirect } from "react-router-dom";
|
||||
import { Route, Router, Switch, Redirect, Link } from "react-router-dom";
|
||||
import {
|
||||
actionsTray,
|
||||
containerForHeader,
|
||||
pageContentStyles,
|
||||
searchField,
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
import history from "../../../history";
|
||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
import StoragePVCs from "./StoragePVCs";
|
||||
import DirectCSIDrives from "../DirectCSI/DirectCSIDrives";
|
||||
import List from "@mui/material/List";
|
||||
import PageLayout from "../Common/Layout/PageLayout";
|
||||
import VerticalTabs from "../Common/VerticalTabs/VerticalTabs";
|
||||
|
||||
interface IStorageProps {
|
||||
classes: any;
|
||||
@@ -44,6 +45,11 @@ const styles = (theme: Theme) =>
|
||||
color: "#000",
|
||||
marginTop: 4,
|
||||
},
|
||||
pageContainer: {
|
||||
border: "1px solid #EAEAEA",
|
||||
height: "100%",
|
||||
},
|
||||
...pageContentStyles,
|
||||
...actionsTray,
|
||||
...searchField,
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
@@ -52,53 +58,52 @@ const styles = (theme: Theme) =>
|
||||
const routes = ["/storage/volumes", "/storage/drives"];
|
||||
|
||||
const Storage = ({ classes, match }: IStorageProps) => {
|
||||
const [selectedTab, setSelectedTab] = useState<number>(0);
|
||||
let selTab = match?.path;
|
||||
selTab = selTab ? selTab : routes[0];
|
||||
|
||||
const [activeTab, setActiveTab] = useState(selTab);
|
||||
|
||||
useEffect(() => {
|
||||
const index = routes.findIndex((route) => route === match.path);
|
||||
setSelectedTab(index);
|
||||
}, [match]);
|
||||
|
||||
const routeChange = (newValue: number) => {
|
||||
history.push(routes[newValue]);
|
||||
};
|
||||
setActiveTab(selTab);
|
||||
}, [selTab]);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<PageHeader label={"Storage"} />
|
||||
<Grid container className={classes.container}>
|
||||
<Grid item xs={2}>
|
||||
<List component="nav" dense={true}>
|
||||
<ListItem
|
||||
button
|
||||
selected={selectedTab === 0}
|
||||
onClick={() => {
|
||||
routeChange(0);
|
||||
}}
|
||||
>
|
||||
<ListItemText primary="Volumes" />
|
||||
</ListItem>
|
||||
<ListItem
|
||||
button
|
||||
selected={selectedTab === 1}
|
||||
onClick={() => {
|
||||
routeChange(1);
|
||||
}}
|
||||
>
|
||||
<ListItemText primary="Drives" />
|
||||
</ListItem>
|
||||
</List>
|
||||
</Grid>
|
||||
<Grid item xs={10}>
|
||||
<Router history={history}>
|
||||
<Switch>
|
||||
<Route path={routes[0]} component={StoragePVCs} />
|
||||
<Route path={routes[1]} component={DirectCSIDrives} />
|
||||
<Route render={() => <Redirect to="/storage/volumes" />} />
|
||||
</Switch>
|
||||
</Router>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<PageLayout className={classes.pageContainer}>
|
||||
<VerticalTabs
|
||||
selectedTab={activeTab}
|
||||
isRouteTabs
|
||||
routes={
|
||||
<div className={classes.contentSpacer}>
|
||||
<Router history={history}>
|
||||
<Switch>
|
||||
<Route exact path={routes[0]} component={StoragePVCs} />
|
||||
<Route exact path={routes[1]} component={DirectCSIDrives} />
|
||||
<Route render={() => <Redirect to={routes[0]} />} />
|
||||
</Switch>
|
||||
</Router>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{{
|
||||
tabConfig: {
|
||||
label: "Volumes",
|
||||
value: routes[0],
|
||||
component: Link,
|
||||
to: routes[0],
|
||||
},
|
||||
}}
|
||||
{{
|
||||
tabConfig: {
|
||||
label: "Drives",
|
||||
value: routes[1],
|
||||
component: Link,
|
||||
to: routes[1],
|
||||
},
|
||||
}}
|
||||
</VerticalTabs>
|
||||
</PageLayout>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -125,7 +125,6 @@ const PodsSummary = ({
|
||||
closeDeleteModalAndRefresh={closeDeleteModalAndRefresh}
|
||||
/>
|
||||
)}
|
||||
<div className={classes.topSpacer} />
|
||||
<h1 className={classes.sectionTitle}>Pods</h1>
|
||||
<TableWrapper
|
||||
columns={[
|
||||
|
||||
@@ -105,7 +105,7 @@ const PoolsSummary = ({
|
||||
tenant={tenant}
|
||||
/>
|
||||
)}
|
||||
<div className={classes.topSpacer} />
|
||||
|
||||
<h1 className={classes.sectionTitle}>Pools</h1>
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
|
||||
@@ -20,7 +20,7 @@ import { Link, Redirect, Route, Router, Switch } from "react-router-dom";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
import { Box, Tab, Tabs, Tooltip } from "@mui/material";
|
||||
import { Tooltip } from "@mui/material";
|
||||
import get from "lodash/get";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import { setErrorSnackMessage, setSnackBarMessage } from "../../../../actions";
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
import { ITenant } from "../ListTenants/types";
|
||||
import {
|
||||
containerForHeader,
|
||||
pageContentStyles,
|
||||
tenantDetailsStyles,
|
||||
} from "../../Common/FormComponents/common/styleLibrary";
|
||||
import { AppState } from "../../../../store";
|
||||
@@ -47,9 +48,6 @@ import PodsSummary from "./PodsSummary";
|
||||
import VolumesSummary from "./VolumesSummary";
|
||||
import TenantMetrics from "./TenantMetrics";
|
||||
import TenantSecurity from "./TenantSecurity";
|
||||
import List from "@mui/material/List";
|
||||
import ListItem from "@mui/material/ListItem";
|
||||
import ListItemText from "@mui/material/ListItemText";
|
||||
import { CircleIcon, DeleteIcon } from "../../../../icons";
|
||||
import DeleteTenant from "../ListTenants/DeleteTenant";
|
||||
import PodDetails from "./pods/PodDetails";
|
||||
@@ -58,6 +56,9 @@ import ScreenTitle from "../../Common/ScreenTitle/ScreenTitle";
|
||||
import EditIcon from "../../../../icons/EditIcon";
|
||||
import RefreshIcon from "../../../../icons/RefreshIcon";
|
||||
import TenantsIcon from "../../../../icons/TenantsIcon";
|
||||
import PageLayout from "../../Common/Layout/PageLayout";
|
||||
import BackLink from "../../../../common/BackLink";
|
||||
import VerticalTabs from "../../Common/VerticalTabs/VerticalTabs";
|
||||
import BoxIconButton from "../../Common/BoxIconButton/BoxIconButton";
|
||||
|
||||
interface ITenantDetailsProps {
|
||||
@@ -80,6 +81,15 @@ interface ITenantDetailsProps {
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
...tenantDetailsStyles,
|
||||
pageContainer: {
|
||||
border: "1px solid #EAEAEA",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
},
|
||||
contentSpacer: {
|
||||
...pageContentStyles.contentSpacer,
|
||||
minHeight: 400,
|
||||
},
|
||||
redState: {
|
||||
color: theme.palette.error.main,
|
||||
"& .MuiSvgIcon-root": {
|
||||
@@ -210,25 +220,19 @@ const TenantDetails = ({
|
||||
setErrorSnackMessage,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
const path = get(match, "path", "/");
|
||||
const splitSections = path.split("/");
|
||||
const section = splitSections[splitSections.length - 1];
|
||||
const path = get(match, "path", "/");
|
||||
const splitSections = path.split("/");
|
||||
|
||||
switch (section) {
|
||||
case "pools":
|
||||
case "pods":
|
||||
case ":podName":
|
||||
case "volumes":
|
||||
case "metrics":
|
||||
case "license":
|
||||
case "security":
|
||||
setTenantTab(section);
|
||||
break;
|
||||
default:
|
||||
setTenantTab("summary");
|
||||
}
|
||||
}, [match, setTenantTab]);
|
||||
let highlightedTab = splitSections[splitSections.length - 1] || "summary";
|
||||
if (highlightedTab === ":podName" || highlightedTab === "pods") {
|
||||
// It has SUB Route
|
||||
highlightedTab = "pods";
|
||||
}
|
||||
const [activeTab, setActiveTab] = useState(highlightedTab);
|
||||
|
||||
useEffect(() => {
|
||||
setActiveTab(highlightedTab);
|
||||
}, [highlightedTab]);
|
||||
|
||||
const editYaml = () => {
|
||||
setYamlScreenOpen(true);
|
||||
@@ -239,11 +243,8 @@ const TenantDetails = ({
|
||||
setTenantDetailsLoad(true);
|
||||
};
|
||||
|
||||
const changeRoute = (newValue: string) => {
|
||||
setTenantTab(newValue);
|
||||
history.push(
|
||||
`/namespaces/${tenantNamespace}/tenants/${tenantName}/${newValue}`
|
||||
);
|
||||
const getRoutePath = (newValue: string) => {
|
||||
return `/namespaces/${tenantNamespace}/tenants/${tenantName}/${newValue}`;
|
||||
};
|
||||
|
||||
const confirmDeleteTenant = () => {
|
||||
@@ -269,94 +270,6 @@ const TenantDetails = ({
|
||||
: classes.greyState;
|
||||
};
|
||||
|
||||
interface ListMenuItem {
|
||||
label: string;
|
||||
value: string;
|
||||
onclick: (val: string) => void;
|
||||
selected: () => boolean;
|
||||
}
|
||||
|
||||
const menu: ListMenuItem[] = [
|
||||
{
|
||||
label: "Summary",
|
||||
value: "summary",
|
||||
onclick: (val) => {
|
||||
changeRoute(val);
|
||||
},
|
||||
selected: () => {
|
||||
return currentTab === "summary";
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Metrics",
|
||||
value: "metrics",
|
||||
onclick: (val) => {
|
||||
changeRoute("metrics");
|
||||
},
|
||||
selected: () => {
|
||||
return currentTab === "metrics";
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Security",
|
||||
value: "security",
|
||||
onclick: (val) => {
|
||||
changeRoute("security");
|
||||
},
|
||||
selected: () => {
|
||||
return currentTab === "security";
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Pools",
|
||||
value: "pools",
|
||||
onclick: (val) => {
|
||||
changeRoute("pools");
|
||||
},
|
||||
selected: () => {
|
||||
return currentTab === "pools";
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Pods",
|
||||
value: "pods",
|
||||
onclick: (val) => {
|
||||
changeRoute("pods");
|
||||
},
|
||||
selected: () => {
|
||||
return currentTab === "pods" || currentTab === ":podName";
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "Volumes",
|
||||
value: "volumes",
|
||||
onclick: (val) => {
|
||||
changeRoute("volumes");
|
||||
},
|
||||
selected: () => {
|
||||
return currentTab === "volumes";
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "License",
|
||||
value: "license",
|
||||
onclick: (val) => {
|
||||
changeRoute("license");
|
||||
},
|
||||
selected: () => {
|
||||
return currentTab === "license";
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
let value = menu[0].value;
|
||||
for (const mli of menu) {
|
||||
if (mli.selected()) {
|
||||
value = mli.value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{yamlScreenOpen && (
|
||||
@@ -383,7 +296,8 @@ const TenantDetails = ({
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
<Grid container className={classes.container}>
|
||||
<BackLink to={"/tenants"} label={"Return to Tenants"} />
|
||||
<PageLayout className={classes.pageContainer}>
|
||||
<Grid item xs={12}>
|
||||
<ScreenTitle
|
||||
icon={
|
||||
@@ -450,94 +364,118 @@ const TenantDetails = ({
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={12} md={2}>
|
||||
<Box display={{ xs: "none", sm: "none", md: "block" }}>
|
||||
<List component="nav" dense={true}>
|
||||
{menu.map((mli) => {
|
||||
return (
|
||||
<ListItem
|
||||
button
|
||||
selected={mli.selected()}
|
||||
onClick={() => {
|
||||
mli.onclick(mli.value);
|
||||
}}
|
||||
>
|
||||
<ListItemText primary={mli.label} />
|
||||
</ListItem>
|
||||
);
|
||||
})}
|
||||
</List>
|
||||
</Box>
|
||||
<Box display={{ xs: "block", sm: "block", md: "none" }}>
|
||||
<Tabs
|
||||
value={value}
|
||||
indicatorColor="primary"
|
||||
textColor="primary"
|
||||
variant="scrollable"
|
||||
scrollButtons="auto"
|
||||
aria-label="scrollable auto tabs example"
|
||||
>
|
||||
{menu.map((mli) => {
|
||||
return (
|
||||
<Tab
|
||||
label={mli.label}
|
||||
value={mli.value}
|
||||
onClick={() => {
|
||||
mli.onclick(mli.value);
|
||||
}}
|
||||
|
||||
<VerticalTabs
|
||||
selectedTab={activeTab}
|
||||
isRouteTabs
|
||||
routes={
|
||||
<div className={classes.contentSpacer}>
|
||||
<Router history={history}>
|
||||
<Switch>
|
||||
<Route
|
||||
path="/namespaces/:tenantNamespace/tenants/:tenantName/summary"
|
||||
component={TenantSummary}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Tabs>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={12} md={10}>
|
||||
<Router history={history}>
|
||||
<Switch>
|
||||
<Route
|
||||
path="/namespaces/:tenantNamespace/tenants/:tenantName/summary"
|
||||
component={TenantSummary}
|
||||
/>
|
||||
<Route
|
||||
path="/namespaces/:tenantNamespace/tenants/:tenantName/metrics"
|
||||
component={TenantMetrics}
|
||||
/>
|
||||
<Route
|
||||
path="/namespaces/:tenantNamespace/tenants/:tenantName/security"
|
||||
component={TenantSecurity}
|
||||
/>
|
||||
<Route
|
||||
path="/namespaces/:tenantNamespace/tenants/:tenantName/pools"
|
||||
component={PoolsSummary}
|
||||
/>
|
||||
<Route
|
||||
path="/namespaces/:tenantNamespace/tenants/:tenantName/pods/:podName"
|
||||
component={PodDetails}
|
||||
/>
|
||||
<Route
|
||||
path="/namespaces/:tenantNamespace/tenants/:tenantName/pods"
|
||||
component={PodsSummary}
|
||||
/>
|
||||
<Route
|
||||
path="/namespaces/:tenantNamespace/tenants/:tenantName/volumes"
|
||||
component={VolumesSummary}
|
||||
/>
|
||||
<Route
|
||||
path="/namespaces/:tenantNamespace/tenants/:tenantName/license"
|
||||
component={TenantLicense}
|
||||
/>
|
||||
<Route
|
||||
path="/namespaces/:tenantNamespace/tenants/:tenantName"
|
||||
component={() => (
|
||||
<Redirect
|
||||
to={`/namespaces/${tenantNamespace}/tenants/${tenantName}/summary`}
|
||||
<Route
|
||||
path="/namespaces/:tenantNamespace/tenants/:tenantName/metrics"
|
||||
component={TenantMetrics}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Switch>
|
||||
</Router>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Route
|
||||
path="/namespaces/:tenantNamespace/tenants/:tenantName/security"
|
||||
component={TenantSecurity}
|
||||
/>
|
||||
<Route
|
||||
path="/namespaces/:tenantNamespace/tenants/:tenantName/pools"
|
||||
component={PoolsSummary}
|
||||
/>
|
||||
<Route
|
||||
path="/namespaces/:tenantNamespace/tenants/:tenantName/pods/:podName"
|
||||
component={PodDetails}
|
||||
/>
|
||||
<Route
|
||||
path="/namespaces/:tenantNamespace/tenants/:tenantName/pods"
|
||||
component={PodsSummary}
|
||||
/>
|
||||
<Route
|
||||
path="/namespaces/:tenantNamespace/tenants/:tenantName/volumes"
|
||||
component={VolumesSummary}
|
||||
/>
|
||||
<Route
|
||||
path="/namespaces/:tenantNamespace/tenants/:tenantName/license"
|
||||
component={TenantLicense}
|
||||
/>
|
||||
<Route
|
||||
path="/namespaces/:tenantNamespace/tenants/:tenantName"
|
||||
component={() => (
|
||||
<Redirect
|
||||
to={`/namespaces/${tenantNamespace}/tenants/${tenantName}/summary`}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Switch>
|
||||
</Router>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{{
|
||||
tabConfig: {
|
||||
label: "Summary",
|
||||
value: "summary",
|
||||
component: Link,
|
||||
to: getRoutePath("summary"),
|
||||
},
|
||||
}}
|
||||
{{
|
||||
tabConfig: {
|
||||
label: "Metrics",
|
||||
value: "metrics",
|
||||
component: Link,
|
||||
to: getRoutePath("metrics"),
|
||||
},
|
||||
}}
|
||||
{{
|
||||
tabConfig: {
|
||||
label: "Security",
|
||||
value: "security",
|
||||
component: Link,
|
||||
to: getRoutePath("security"),
|
||||
},
|
||||
}}
|
||||
{{
|
||||
tabConfig: {
|
||||
label: "Pools",
|
||||
value: "pools",
|
||||
component: Link,
|
||||
to: getRoutePath("pools"),
|
||||
},
|
||||
}}
|
||||
{{
|
||||
tabConfig: {
|
||||
label: "Pods",
|
||||
value: "pods",
|
||||
component: Link,
|
||||
to: getRoutePath("pods"),
|
||||
},
|
||||
}}
|
||||
{{
|
||||
tabConfig: {
|
||||
label: "Volumes",
|
||||
value: "volumes",
|
||||
component: Link,
|
||||
to: getRoutePath("volumes"),
|
||||
},
|
||||
}}
|
||||
|
||||
{{
|
||||
tabConfig: {
|
||||
label: "License",
|
||||
value: "license",
|
||||
component: Link,
|
||||
to: getRoutePath("license"),
|
||||
},
|
||||
}}
|
||||
</VerticalTabs>
|
||||
</PageLayout>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -99,7 +99,6 @@ const TenantLicense = ({
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<div className={classes.topSpacer} />
|
||||
<h1 className={classes.sectionTitle}>License</h1>
|
||||
{loadingTenant ? (
|
||||
<div className={classes.loaderAlign}>
|
||||
|
||||
@@ -321,7 +321,6 @@ const TenantSecurity = ({
|
||||
cancelLabel="Cancel"
|
||||
okLabel={"Restart"}
|
||||
/>
|
||||
<div className={classes.topSpacer} />
|
||||
{loadingTenant ? (
|
||||
<Paper className={classes.paperContainer}>
|
||||
<div className={classes.loaderAlign}>
|
||||
|
||||
@@ -169,7 +169,6 @@ const TenantSummary = ({
|
||||
namespace={tenantNamespace}
|
||||
/>
|
||||
)}
|
||||
<div className={classes.topSpacer} />
|
||||
<h1 className={classes.sectionTitle}>Summary</h1>
|
||||
<Paper className={classes.paperContainer}>
|
||||
<Grid container>
|
||||
|
||||
@@ -52,7 +52,7 @@ const styles = (theme: Theme) =>
|
||||
color: "black",
|
||||
},
|
||||
tableWrapper: {
|
||||
height: "calc(100vh - 267px)",
|
||||
height: "450px",
|
||||
},
|
||||
...actionsTray,
|
||||
...searchField,
|
||||
|
||||
@@ -65,7 +65,6 @@ const PodDetails = ({ classes, match }: IPodDetailsProps) => {
|
||||
return (
|
||||
<Fragment>
|
||||
<Grid item xs={12}>
|
||||
<div className={classes.topSpacer} />
|
||||
<h1 className={classes.sectionTitle}>
|
||||
<Link
|
||||
to={`/namespaces/${tenantNamespace}/tenants/${tenantName}/pods`}
|
||||
|
||||
@@ -48,6 +48,7 @@ import SearchIcon from "../../../icons/SearchIcon";
|
||||
import { decodeFileName } from "../../../common/utils";
|
||||
import HelpBox from "../../../common/HelpBox";
|
||||
import AButton from "../Common/AButton/AButton";
|
||||
import PageLayout from "../Common/Layout/PageLayout";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -81,9 +82,6 @@ const styles = (theme: Theme) =>
|
||||
},
|
||||
},
|
||||
},
|
||||
twHeight: {
|
||||
minHeight: 600,
|
||||
},
|
||||
...actionsTray,
|
||||
...searchField,
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
@@ -229,7 +227,7 @@ const ListUsers = ({ classes, setErrorSnackMessage, history }: IUsersProps) => {
|
||||
/>
|
||||
)}
|
||||
<PageHeader label={"Users"} />
|
||||
<Grid container className={classes.container}>
|
||||
<PageLayout>
|
||||
<Grid item xs={12} className={classes.actionsTray}>
|
||||
<TextField
|
||||
placeholder="Search Users"
|
||||
@@ -293,7 +291,6 @@ const ListUsers = ({ classes, setErrorSnackMessage, history }: IUsersProps) => {
|
||||
records={filteredRecords}
|
||||
entityName="Users"
|
||||
idField="accessKey"
|
||||
customPaperHeight={classes.twHeight}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
@@ -374,7 +371,7 @@ const ListUsers = ({ classes, setErrorSnackMessage, history }: IUsersProps) => {
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
</Grid>
|
||||
</PageLayout>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -39,7 +39,6 @@ import {
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
import { IPolicyItem } from "./types";
|
||||
import { ErrorResponseHandler } from "../../../common/types";
|
||||
import { TabPanel } from "../../shared/tabs";
|
||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
import api from "../../../common/api";
|
||||
import TableWrapper from "../Common/TableWrapper/TableWrapper";
|
||||
@@ -49,16 +48,29 @@ import history from "../../../history";
|
||||
import UserServiceAccountsPanel from "./UserServiceAccountsPanel";
|
||||
import ChangeUserPasswordModal from "../Account/ChangeUserPasswordModal";
|
||||
import DeleteUserString from "./DeleteUserString";
|
||||
import ListItem from "@mui/material/ListItem";
|
||||
import ListItemText from "@mui/material/ListItemText";
|
||||
import List from "@mui/material/List";
|
||||
import LockIcon from "@mui/icons-material/Lock";
|
||||
import ScreenTitle from "../Common/ScreenTitle/ScreenTitle";
|
||||
import BoxIconButton from "../Common/BoxIconButton/BoxIconButton";
|
||||
import PanelTitle from "../Common/PanelTitle/PanelTitle";
|
||||
import PageLayout from "../Common/Layout/PageLayout";
|
||||
import VerticalTabs from "../Common/VerticalTabs/VerticalTabs";
|
||||
import FormSwitchWrapper from "../Common/FormComponents/FormSwitchWrapper/FormSwitchWrapper";
|
||||
import BackLink from "../../../common/BackLink";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
pageContainer: {
|
||||
border: "1px solid #EAEAEA",
|
||||
},
|
||||
statusLabel: {
|
||||
fontSize: ".8rem",
|
||||
marginRight: ".5rem",
|
||||
},
|
||||
statusValue: {
|
||||
fontWeight: "bold",
|
||||
fontSize: ".9rem",
|
||||
marginRight: ".5rem",
|
||||
},
|
||||
seeMore: {
|
||||
marginTop: theme.spacing(3),
|
||||
},
|
||||
@@ -126,7 +138,6 @@ const styles = (theme: Theme) =>
|
||||
},
|
||||
...actionsTray,
|
||||
...searchField,
|
||||
actionsTray: { ...actionsTray.actionsTray },
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
|
||||
@@ -141,7 +152,6 @@ interface IGroupItem {
|
||||
}
|
||||
|
||||
const UserDetails = ({ classes, match }: IUserDetailsProps) => {
|
||||
const [curTab, setCurTab] = useState<number>(0);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [addGroupOpen, setAddGroupOpen] = useState<boolean>(false);
|
||||
const [policyOpen, setPolicyOpen] = useState<boolean>(false);
|
||||
@@ -281,7 +291,8 @@ const UserDetails = ({ classes, match }: IUserDetailsProps) => {
|
||||
closeModal={() => setChangeUserPasswordModalOpen(false)}
|
||||
/>
|
||||
)}
|
||||
<Grid container className={classes.container}>
|
||||
<BackLink label={"Return to Users"} to={"/users"} />
|
||||
<PageLayout className={classes.pageContainer}>
|
||||
<Grid item xs={12}>
|
||||
<ScreenTitle
|
||||
icon={
|
||||
@@ -290,20 +301,25 @@ const UserDetails = ({ classes, match }: IUserDetailsProps) => {
|
||||
</Fragment>
|
||||
}
|
||||
title={userName}
|
||||
subTitle={
|
||||
<Fragment>Status: {enabled ? "Enabled" : "Disabled"}</Fragment>
|
||||
}
|
||||
actions={
|
||||
<Fragment>
|
||||
<Button
|
||||
onClick={() => {
|
||||
<span className={classes.statusLabel}>User Status:</span>
|
||||
<span className={classes.statusValue}>
|
||||
{enabled ? "Enabled" : "Disabled"}
|
||||
</span>
|
||||
<FormSwitchWrapper
|
||||
indicatorLabels={["Enabled", "Disabled"]}
|
||||
checked={enabled}
|
||||
value={"group_enabled"}
|
||||
id="group-status"
|
||||
name="group-status"
|
||||
onChange={() => {
|
||||
setEnabled(!enabled);
|
||||
saveRecord(!enabled);
|
||||
}}
|
||||
color={"primary"}
|
||||
>
|
||||
{enabled ? "Disable" : "Enable"}
|
||||
</Button>
|
||||
switchOnly
|
||||
/>
|
||||
|
||||
<Tooltip title="Delete User">
|
||||
<BoxIconButton
|
||||
color="primary"
|
||||
@@ -328,104 +344,93 @@ const UserDetails = ({ classes, match }: IUserDetailsProps) => {
|
||||
}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={2}>
|
||||
<List component="nav" dense={true}>
|
||||
<ListItem
|
||||
button
|
||||
selected={curTab === 0}
|
||||
onClick={() => {
|
||||
setCurTab(0);
|
||||
}}
|
||||
>
|
||||
<ListItemText primary="Groups" />
|
||||
</ListItem>
|
||||
<ListItem
|
||||
button
|
||||
selected={curTab === 1}
|
||||
onClick={() => {
|
||||
setCurTab(1);
|
||||
}}
|
||||
>
|
||||
<ListItemText primary="Service Accounts" />
|
||||
</ListItem>
|
||||
<ListItem
|
||||
button
|
||||
selected={curTab === 2}
|
||||
onClick={() => {
|
||||
setCurTab(2);
|
||||
}}
|
||||
>
|
||||
<ListItemText primary="Policies" />
|
||||
</ListItem>
|
||||
</List>
|
||||
|
||||
<Grid item xs={12}>
|
||||
<VerticalTabs>
|
||||
{{
|
||||
tabConfig: {
|
||||
label: "Groups",
|
||||
},
|
||||
content: (
|
||||
<React.Fragment>
|
||||
<div className={classes.actionsTray}>
|
||||
<PanelTitle>Groups</PanelTitle>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
endIcon={<AddIcon />}
|
||||
size="medium"
|
||||
onClick={() => {
|
||||
setAddGroupOpen(true);
|
||||
}}
|
||||
>
|
||||
Add to Groups
|
||||
</Button>
|
||||
</div>
|
||||
<TableWrapper
|
||||
// itemActions={userTableActions}
|
||||
columns={[{ label: "Name", elementKey: "group" }]}
|
||||
isLoading={loading}
|
||||
records={currentGroups}
|
||||
entityName="Groups"
|
||||
idField="group"
|
||||
/>
|
||||
</React.Fragment>
|
||||
),
|
||||
}}
|
||||
{{
|
||||
tabConfig: {
|
||||
label: "Service Accounts",
|
||||
},
|
||||
content: (
|
||||
<UserServiceAccountsPanel
|
||||
user={userName}
|
||||
classes={classes}
|
||||
hasPolicy={hasPolicy}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
{{
|
||||
tabConfig: {
|
||||
label: "Policies",
|
||||
},
|
||||
content: (
|
||||
<React.Fragment>
|
||||
<div className={classes.actionsTray}>
|
||||
<PanelTitle>Policies</PanelTitle>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
endIcon={<IAMPoliciesIcon />}
|
||||
size="medium"
|
||||
onClick={() => {
|
||||
setPolicyOpen(true);
|
||||
}}
|
||||
>
|
||||
Assign Policies
|
||||
</Button>
|
||||
</div>
|
||||
<TableWrapper
|
||||
itemActions={[
|
||||
{
|
||||
type: "view",
|
||||
onClick: (policy: IPolicyItem) => {
|
||||
history.push(`/policies/${policy.policy}`);
|
||||
},
|
||||
},
|
||||
]}
|
||||
columns={[{ label: "Name", elementKey: "policy" }]}
|
||||
isLoading={loading}
|
||||
records={currentPolicies}
|
||||
entityName="Policies"
|
||||
idField="policy"
|
||||
/>
|
||||
</React.Fragment>
|
||||
),
|
||||
}}
|
||||
</VerticalTabs>
|
||||
</Grid>
|
||||
<Grid item xs={10}>
|
||||
<Grid item xs={12}>
|
||||
<TabPanel index={0} value={curTab}>
|
||||
<div className={classes.actionsTray}>
|
||||
<PanelTitle>Groups</PanelTitle>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
endIcon={<AddIcon />}
|
||||
size="medium"
|
||||
onClick={() => {
|
||||
setAddGroupOpen(true);
|
||||
}}
|
||||
>
|
||||
Add to Groups
|
||||
</Button>
|
||||
</div>
|
||||
<TableWrapper
|
||||
// itemActions={userTableActions}
|
||||
columns={[{ label: "Name", elementKey: "group" }]}
|
||||
isLoading={loading}
|
||||
records={currentGroups}
|
||||
entityName="Groups"
|
||||
idField="group"
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel index={1} value={curTab}>
|
||||
<UserServiceAccountsPanel
|
||||
user={userName}
|
||||
classes={classes}
|
||||
hasPolicy={hasPolicy}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel index={2} value={curTab}>
|
||||
<div className={classes.actionsTray}>
|
||||
<PanelTitle>Policies</PanelTitle>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
endIcon={<IAMPoliciesIcon />}
|
||||
size="medium"
|
||||
onClick={() => {
|
||||
setPolicyOpen(true);
|
||||
}}
|
||||
>
|
||||
Assign Policies
|
||||
</Button>
|
||||
</div>
|
||||
<TableWrapper
|
||||
itemActions={[
|
||||
{
|
||||
type: "view",
|
||||
onClick: (policy: IPolicyItem) => {
|
||||
history.push(`/policies/${policy.policy}`);
|
||||
},
|
||||
},
|
||||
]}
|
||||
columns={[{ label: "Name", elementKey: "policy" }]}
|
||||
isLoading={loading}
|
||||
records={currentPolicies}
|
||||
entityName="Policies"
|
||||
idField="policy"
|
||||
/>
|
||||
</TabPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</PageLayout>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user