github.com/minio/console@v1.4.1/web-app/src/screens/Console/Dashboard/BasicDashboard/BasicDashboard.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 from "react"; 18 import { 19 ArrowRightIcon, 20 Box, 21 breakPoints, 22 BucketsIcon, 23 Button, 24 DiagnosticsMenuIcon, 25 DrivesIcon, 26 FormatDrivesIcon, 27 HealIcon, 28 HelpBox, 29 PrometheusErrorIcon, 30 ServersIcon, 31 StorageIcon, 32 TotalObjectsIcon, 33 UptimeIcon, 34 } from "mds"; 35 import { calculateBytes, representationNumber } from "../../../../common/utils"; 36 import StatusCountCard from "./StatusCountCard"; 37 import groupBy from "lodash/groupBy"; 38 import ServersList from "./ServersList"; 39 import CounterCard from "./CounterCard"; 40 import ReportedUsage from "./ReportedUsage"; 41 import { Link } from "react-router-dom"; 42 import { IAM_PAGES } from "../../../../common/SecureComponent/permissions"; 43 import TimeStatItem from "../TimeStatItem"; 44 import TooltipWrapper from "../../Common/TooltipWrapper/TooltipWrapper"; 45 import { AdminInfoResponse, ServerDrives } from "api/consoleApi"; 46 47 const BoxItem = ({ children }: { children: any }) => { 48 return ( 49 <Box 50 withBorders 51 sx={{ 52 padding: 15, 53 height: "136px", 54 maxWidth: "100%", 55 [`@media (max-width: ${breakPoints.sm}px)`]: { 56 padding: 5, 57 maxWidth: "initial", 58 }, 59 }} 60 > 61 {children} 62 </Box> 63 ); 64 }; 65 66 interface IDashboardProps { 67 usage: AdminInfoResponse | undefined; 68 } 69 70 const getServersList = (usage: AdminInfoResponse | undefined) => { 71 if (usage && usage.servers) { 72 return [...usage.servers].sort(function (a, b) { 73 const nameA = a.endpoint?.toLowerCase() || ""; 74 const nameB = b.endpoint?.toLowerCase() || ""; 75 if (nameA < nameB) { 76 return -1; 77 } 78 if (nameA > nameB) { 79 return 1; 80 } 81 return 0; 82 }); 83 } 84 85 return []; 86 }; 87 88 const prettyUsage = (usage: string | undefined) => { 89 if (usage === undefined) { 90 return { total: "0", unit: "Mi" }; 91 } 92 93 return calculateBytes(usage); 94 }; 95 96 const BasicDashboard = ({ usage }: IDashboardProps) => { 97 const usageValue = usage && usage.usage ? usage.usage.toString() : "0"; 98 const usageToRepresent = prettyUsage(usageValue); 99 100 const { lastScan = "n/a", lastHeal = "n/a", upTime = "n/a" } = {}; 101 102 const serverList = getServersList(usage); 103 104 let allDrivesArray: ServerDrives[] = []; 105 106 serverList.forEach((server) => { 107 const drivesInput = server.drives?.map((drive) => { 108 return drive; 109 }); 110 if (drivesInput) { 111 allDrivesArray = [...allDrivesArray, ...drivesInput]; 112 } 113 }); 114 115 const serversGroup = groupBy(serverList, "state"); 116 const { offline: offlineServers = [], online: onlineServers = [] } = 117 serversGroup; 118 const drivesGroup = groupBy(allDrivesArray, "state"); 119 const { offline: offlineDrives = [], ok: onlineDrives = [] } = drivesGroup; 120 return ( 121 <Box> 122 <Box 123 sx={{ 124 display: "grid", 125 gridTemplateRows: "1fr", 126 gridTemplateColumns: "1fr", 127 gap: 27, 128 marginBottom: 40, 129 }} 130 > 131 <Box 132 sx={{ 133 display: "grid", 134 gridTemplateColumns: "1fr", 135 gap: "40px", 136 }} 137 > 138 <Box 139 sx={{ 140 display: "grid", 141 gridTemplateRows: "136px", 142 gridTemplateColumns: "1fr 1fr 1fr", 143 gap: 20, 144 [`@media (max-width: ${breakPoints.sm}px)`]: { 145 gridTemplateColumns: "1fr", 146 }, 147 [`@media (max-width: ${breakPoints.md}px)`]: { 148 marginBottom: 0, 149 }, 150 }} 151 > 152 <BoxItem> 153 <CounterCard 154 label={"Buckets"} 155 icon={<BucketsIcon />} 156 counterValue={usage ? representationNumber(usage.buckets) : 0} 157 actions={ 158 <Link 159 to={IAM_PAGES.BUCKETS} 160 style={{ 161 zIndex: 3, 162 textDecoration: "none", 163 top: "40px", 164 position: "relative", 165 marginRight: "75px", 166 }} 167 > 168 <TooltipWrapper tooltip={"Browse"}> 169 <Button 170 id={"browse-dashboard"} 171 onClick={() => {}} 172 label={"Browse"} 173 icon={<ArrowRightIcon />} 174 variant={"regular"} 175 style={{ 176 padding: 5, 177 height: 30, 178 fontSize: 14, 179 marginTop: 20, 180 }} 181 /> 182 </TooltipWrapper> 183 </Link> 184 } 185 /> 186 </BoxItem> 187 <BoxItem> 188 <CounterCard 189 label={"Objects"} 190 icon={<TotalObjectsIcon />} 191 counterValue={usage ? representationNumber(usage.objects) : 0} 192 /> 193 </BoxItem> 194 195 <BoxItem> 196 <StatusCountCard 197 onlineCount={onlineServers.length} 198 offlineCount={offlineServers.length} 199 label={"Servers"} 200 icon={<ServersIcon />} 201 /> 202 </BoxItem> 203 <BoxItem> 204 <StatusCountCard 205 offlineCount={ 206 usage?.backend?.offlineDrives || offlineDrives.length 207 } 208 onlineCount={ 209 usage?.backend?.onlineDrives || onlineDrives.length 210 } 211 label={"Drives"} 212 icon={<DrivesIcon />} 213 /> 214 </BoxItem> 215 216 <Box 217 withBorders 218 sx={{ 219 gridRowStart: "1", 220 gridRowEnd: "3", 221 gridColumnStart: "3", 222 padding: 15, 223 display: "grid", 224 justifyContent: "stretch", 225 }} 226 > 227 <ReportedUsage 228 usageValue={usageValue} 229 total={usageToRepresent.total} 230 unit={usageToRepresent.unit} 231 /> 232 233 <Box 234 sx={{ 235 display: "flex", 236 flexFlow: "column", 237 gap: "14px", 238 }} 239 > 240 <TimeStatItem 241 icon={<HealIcon />} 242 label={ 243 <Box> 244 <Box 245 sx={{ 246 display: "inline", 247 [`@media (max-width: ${breakPoints.sm}px)`]: { 248 display: "none", 249 }, 250 }} 251 > 252 Time since last 253 </Box>{" "} 254 Heal Activity 255 </Box> 256 } 257 value={lastHeal} 258 /> 259 <TimeStatItem 260 icon={<DiagnosticsMenuIcon />} 261 label={ 262 <Box> 263 <Box 264 sx={{ 265 display: "inline", 266 [`@media (max-width: ${breakPoints.sm}px)`]: { 267 display: "none", 268 }, 269 }} 270 > 271 Time since last 272 </Box>{" "} 273 Scan Activity 274 </Box> 275 } 276 value={lastScan} 277 /> 278 <TimeStatItem 279 icon={<UptimeIcon />} 280 label={"Uptime"} 281 value={upTime} 282 /> 283 </Box> 284 </Box> 285 </Box> 286 <Box 287 sx={{ 288 display: "grid", 289 gridTemplateColumns: "1fr 1fr 1fr", 290 gap: "14px", 291 [`@media (max-width: ${breakPoints.lg}px)`]: { 292 gridTemplateColumns: "1fr", 293 }, 294 }} 295 > 296 <TimeStatItem 297 icon={<StorageIcon />} 298 label={"Backend type"} 299 value={usage?.backend?.backendType ?? "Unknown"} 300 /> 301 <TimeStatItem 302 icon={<FormatDrivesIcon />} 303 label={"Standard storage class parity"} 304 value={usage?.backend?.standardSCParity?.toString() ?? "n/a"} 305 /> 306 <TimeStatItem 307 icon={<FormatDrivesIcon />} 308 label={"Reduced redundancy storage class parity"} 309 value={usage?.backend?.rrSCParity?.toString() ?? "n/a"} 310 /> 311 </Box> 312 313 <Box 314 sx={{ 315 display: "grid", 316 gridTemplateRows: "auto", 317 gridTemplateColumns: "1fr", 318 gap: "auto", 319 }} 320 > 321 <ServersList data={serverList} /> 322 </Box> 323 </Box> 324 {usage?.advancedMetricsStatus === "not configured" && ( 325 <Box> 326 <HelpBox 327 iconComponent={<PrometheusErrorIcon />} 328 title={"We can’t retrieve advanced metrics at this time."} 329 help={ 330 <Box> 331 <Box 332 sx={{ 333 fontSize: "14px", 334 }} 335 > 336 MinIO Dashboard will display basic metrics as we couldn’t 337 connect to Prometheus successfully. Please try again in a 338 few minutes. If the problem persists, you can review your 339 configuration and confirm that Prometheus server is up and 340 running. 341 </Box> 342 <Box 343 sx={{ 344 paddingTop: 20, 345 fontSize: 14, 346 }} 347 > 348 <a 349 href="https://min.io/docs/minio/linux/operations/monitoring/collect-minio-metrics-using-prometheus.html" 350 target="_blank" 351 rel="noopener" 352 > 353 Read more about Prometheus on our Docs site. 354 </a> 355 </Box> 356 </Box> 357 } 358 /> 359 </Box> 360 )} 361 </Box> 362 </Box> 363 ); 364 }; 365 366 export default BasicDashboard;