github.com/minio/console@v1.4.1/web-app/src/screens/Console/Configurations/SiteReplication/SiteReplicationStatus.tsx (about) 1 // This file is part of MinIO Console Server 2 // Copyright (c) 2022 MinIO, Inc. 3 // 4 // This program is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Affero General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // This program is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Affero General Public License for more details. 13 // 14 // You should have received a copy of the GNU Affero General Public License 15 // along with this program. If not, see <http://www.gnu.org/licenses/>. 16 17 import React, { Fragment, useEffect, useState } from "react"; 18 import { 19 BackLink, 20 Box, 21 breakPoints, 22 BucketsIcon, 23 Button, 24 Grid, 25 GroupsIcon, 26 IAMPoliciesIcon, 27 Loader, 28 PageLayout, 29 RefreshIcon, 30 UsersIcon, 31 SectionTitle, 32 } from "mds"; 33 import { useNavigate } from "react-router-dom"; 34 import { IAM_PAGES } from "../../../../common/SecureComponent/permissions"; 35 import { useAppDispatch } from "../../../../store"; 36 import { setErrorSnackMessage, setHelpName } from "../../../../systemSlice"; 37 import StatusCountCard from "../../Dashboard/BasicDashboard/StatusCountCard"; 38 import EntityReplicationLookup from "./EntityReplicationLookup"; 39 import TooltipWrapper from "../../Common/TooltipWrapper/TooltipWrapper"; 40 import PageHeaderWrapper from "../../Common/PageHeaderWrapper/PageHeaderWrapper"; 41 import HelpMenu from "../../HelpMenu"; 42 import { api } from "api"; 43 import { errorToHandler } from "api/errors"; 44 import { 45 ApiError, 46 HttpResponse, 47 SiteReplicationStatusResponse, 48 } from "api/consoleApi"; 49 50 export type StatsResponseType = { 51 maxBuckets?: number; 52 bucketStats?: Record<string, any>; 53 maxGroups?: number; 54 groupStats?: Record<string, any>; 55 maxUsers?: number; 56 userStats?: Record<string, any>; 57 maxPolicies?: number; 58 policyStats?: Record<string, any>; 59 sites?: Record<string, any>; 60 }; 61 62 const SREntityStatus = ({ 63 maxValue = 0, 64 entityStatObj = {}, 65 entityTextPlural = "", 66 icon = null, 67 }: { 68 maxValue: number; 69 entityStatObj: Record<string, any>; 70 entityTextPlural: string; 71 icon?: React.ReactNode; 72 }) => { 73 const statEntityLen = Object.keys(entityStatObj || {})?.length; 74 return ( 75 <Box 76 withBorders 77 sx={{ 78 padding: "25px", 79 [`@media (min-width: ${breakPoints.sm}px)`]: { 80 maxWidth: "100%", 81 }, 82 }} 83 > 84 <StatusCountCard 85 icon={icon} 86 onlineCount={maxValue} 87 offlineCount={statEntityLen} 88 okStatusText={"Synced"} 89 notOkStatusText={"Failed"} 90 label={entityTextPlural} 91 /> 92 </Box> 93 ); 94 }; 95 96 const SiteReplicationStatus = () => { 97 const navigate = useNavigate(); 98 99 const [stats, setStats] = useState<StatsResponseType>({}); 100 const [loading, setLoading] = useState<boolean>(false); 101 102 const { 103 maxBuckets = 0, 104 bucketStats = {}, 105 maxGroups = 0, 106 groupStats = {}, 107 maxUsers = 0, 108 userStats = {}, 109 maxPolicies = 0, 110 policyStats = {}, 111 } = stats || {}; 112 113 const getStats = () => { 114 setLoading(true); 115 api.admin 116 .getSiteReplicationStatus({ 117 buckets: true, 118 groups: true, 119 policies: true, 120 users: true, 121 }) 122 .then((res: HttpResponse<SiteReplicationStatusResponse, ApiError>) => { 123 setStats(res.data); 124 }) 125 .catch((res: HttpResponse<SiteReplicationStatusResponse, ApiError>) => { 126 setStats({}); 127 dispatch(setErrorSnackMessage(errorToHandler(res.error))); 128 }) 129 .finally(() => setLoading(false)); 130 }; 131 132 useEffect(() => { 133 getStats(); 134 // eslint-disable-next-line react-hooks/exhaustive-deps 135 }, []); 136 137 const dispatch = useAppDispatch(); 138 useEffect(() => { 139 dispatch(setHelpName("replication_status")); 140 // eslint-disable-next-line react-hooks/exhaustive-deps 141 }, []); 142 143 return ( 144 <Fragment> 145 <PageHeaderWrapper 146 label={ 147 <BackLink 148 label={"Site Replication"} 149 onClick={() => navigate(IAM_PAGES.SITE_REPLICATION)} 150 /> 151 } 152 actions={<HelpMenu />} 153 /> 154 155 <PageLayout> 156 <SectionTitle 157 actions={ 158 <Fragment> 159 <TooltipWrapper tooltip={"Refresh"}> 160 <Button 161 id={"refresh"} 162 onClick={() => { 163 getStats(); 164 }} 165 label={"Refresh"} 166 icon={<RefreshIcon />} 167 variant={"regular"} 168 collapseOnSmall={false} 169 /> 170 </TooltipWrapper> 171 </Fragment> 172 } 173 separator 174 > 175 Replication status from all Sites 176 </SectionTitle> 177 178 {!loading ? ( 179 <Box 180 sx={{ 181 display: "grid", 182 marginTop: "25px", 183 gridTemplateColumns: "1fr 1fr 1fr 1fr", 184 [`@media (max-width: ${breakPoints.md}px)`]: { 185 gridTemplateColumns: "1fr 1fr", 186 }, 187 [`@media (max-width: ${breakPoints.sm}px)`]: { 188 gridTemplateColumns: "1fr", 189 }, 190 gap: "30px", 191 }} 192 > 193 <SREntityStatus 194 entityStatObj={bucketStats} 195 entityTextPlural={"Buckets"} 196 maxValue={maxBuckets} 197 icon={<BucketsIcon />} 198 /> 199 <SREntityStatus 200 entityStatObj={userStats} 201 entityTextPlural={"Users"} 202 maxValue={maxUsers} 203 icon={<UsersIcon />} 204 /> 205 <SREntityStatus 206 entityStatObj={groupStats} 207 entityTextPlural={"Groups"} 208 maxValue={maxGroups} 209 icon={<GroupsIcon />} 210 /> 211 <SREntityStatus 212 entityStatObj={policyStats} 213 entityTextPlural={"Policies"} 214 maxValue={maxPolicies} 215 icon={<IAMPoliciesIcon />} 216 /> 217 </Box> 218 ) : ( 219 <Grid 220 item 221 xs={12} 222 sx={{ 223 display: "flex", 224 alignItems: "center", 225 justifyContent: "center", 226 marginTop: 45, 227 }} 228 > 229 <Loader style={{ width: 25, height: 25 }} /> 230 </Grid> 231 )} 232 233 <Box 234 withBorders 235 sx={{ 236 minHeight: 450, 237 [`@media (max-width: ${breakPoints.sm}px)`]: { 238 minHeight: 250, 239 }, 240 marginTop: "25px", 241 padding: "25px", 242 }} 243 > 244 <EntityReplicationLookup /> 245 </Box> 246 </PageLayout> 247 </Fragment> 248 ); 249 }; 250 251 export default SiteReplicationStatus;