diff --git a/portal-ui/src/common/SecureComponent/permissions.ts b/portal-ui/src/common/SecureComponent/permissions.ts
index 31cba791d..2a293741a 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 fdb8d6d9c..81803e8bc 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 3e321222b..63b0270e0 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 000000000..d1f302836
--- /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));