diff --git a/portal-ui/src/common/SecureComponent/permissions.ts b/portal-ui/src/common/SecureComponent/permissions.ts index 31cba791d8..2a293741a3 100644 --- a/portal-ui/src/common/SecureComponent/permissions.ts +++ b/portal-ui/src/common/SecureComponent/permissions.ts @@ -190,6 +190,8 @@ export const IAM_PAGES = { "/namespaces/:tenantNamespace/tenants/:tenantName/monitoring", NAMESPACE_TENANT_LOGGING: "/namespaces/:tenantNamespace/tenants/:tenantName/logging", + NAMESPACE_TENANT_EVENTS: + "/namespaces/:tenantNamespace/tenants/:tenantName/events", }; // roles diff --git a/portal-ui/src/screens/Console/Console.tsx b/portal-ui/src/screens/Console/Console.tsx index fdb8d6d9c0..81803e8bc5 100644 --- a/portal-ui/src/screens/Console/Console.tsx +++ b/portal-ui/src/screens/Console/Console.tsx @@ -468,6 +468,11 @@ const Console = ({ path: IAM_PAGES.NAMESPACE_TENANT_LOGGING, forceDisplay: true, }, + { + component: TenantDetails, + path: IAM_PAGES.NAMESPACE_TENANT_EVENTS, + forceDisplay: true, + }, { component: License, path: IAM_PAGES.LICENSE, diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantDetails.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantDetails.tsx index 3e321222be..63b0270e02 100644 --- a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantDetails.tsx +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantDetails.tsx @@ -58,6 +58,7 @@ const TenantLicense = withSuspense(React.lazy(() => import("./TenantLicense"))); const PoolsSummary = withSuspense(React.lazy(() => import("./PoolsSummary"))); const PodsSummary = withSuspense(React.lazy(() => import("./PodsSummary"))); const TenantLogging = withSuspense(React.lazy(() => import("./TenantLogging"))); +const TenantEvents = withSuspense(React.lazy(() => import("./TenantEvents"))); const VolumesSummary = withSuspense( React.lazy(() => import("./VolumesSummary")) ); @@ -459,6 +460,10 @@ const TenantDetails = ({ path="/namespaces/:tenantNamespace/tenants/:tenantName/logging" component={TenantLogging} /> + ( @@ -537,6 +542,14 @@ const TenantDetails = ({ to: getRoutePath("volumes"), }, }} + {{ + tabConfig: { + label: "Events", + value: "events", + component: Link, + to: getRoutePath("events"), + }, + }} {{ tabConfig: { label: "License", diff --git a/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantEvents.tsx b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantEvents.tsx new file mode 100644 index 0000000000..d1f302836f --- /dev/null +++ b/portal-ui/src/screens/Console/Tenants/TenantDetails/TenantEvents.tsx @@ -0,0 +1,123 @@ +// This file is part of MinIO Console Server +// Copyright (c) 2021 MinIO, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import React, { 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 { + actionsTray, + containerForHeader, + searchField, tableStyles, +} from "../../Common/FormComponents/common/styleLibrary"; +import Grid from "@mui/material/Grid"; +import { IEvent } from "../ListTenants/types"; +import { setErrorSnackMessage } from "../../../../actions"; +import { niceDays } from "../../../../common/utils"; +import { ErrorResponseHandler } from "../../../../common/types"; +import TableWrapper from "../../Common/TableWrapper/TableWrapper"; +import api from "../../../../common/api"; +import { AppState } from "../../../../store"; + +interface ITenantEventsProps { + classes: any; + match: any; + loadingTenant: boolean; + setErrorSnackMessage: typeof setErrorSnackMessage; +} + +const styles = (theme: Theme) => + createStyles({ + tableWrapper: { + height: "450px", + }, + ...actionsTray, + ...searchField, + ...tableStyles, + ...containerForHeader(theme.spacing(4)), + }); + +const TenantEvents = ({ + classes, + match, + loadingTenant, + setErrorSnackMessage, +}: ITenantEventsProps) => { + const [event, setEvent] = useState([]); + const [loading, setLoading] = useState(true); + const tenantName = match.params["tenantName"]; + const tenantNamespace = match.params["tenantNamespace"]; + + useEffect(() => { + if (loadingTenant) { + setLoading(true); + } + }, [loadingTenant]); + + useEffect(() => { + if (loading) { + api + .invoke( + "GET", + `/api/v1/namespaces/${tenantNamespace}/tenants/${tenantName}/events` + ) + .then((res: IEvent[]) => { + for (let i = 0; i < res.length; i++) { + let currentTime = (Date.now() / 1000) | 0; + + res[i].seen = niceDays((currentTime - res[i].last_seen).toString()); + } + setEvent(res); + setLoading(false); + }) + .catch((err: ErrorResponseHandler) => { + setErrorSnackMessage(err); + setLoading(false); + }); + } + }, [loading, tenantNamespace, tenantName, setErrorSnackMessage]); + + return ( + +

Events

+ + + +
+ ); +}; +const mapState = (state: AppState) => ({ + loadingTenant: state.tenants.tenantDetails.loadingTenant, +}); +const connector = connect(mapState, { + setErrorSnackMessage, +}); + +export default withStyles(styles)(connector(TenantEvents));