github.com/minio/console@v1.4.1/web-app/src/screens/Console/Dashboard/Prometheus/Widgets/PieChartWidget.tsx (about) 1 // This file is part of MinIO Console Server 2 // Copyright (c) 2021 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, { useEffect, useState } from "react"; 18 import get from "lodash/get"; 19 import styled from "styled-components"; 20 import { Box, Loader } from "mds"; 21 import { Cell, Pie, PieChart, ResponsiveContainer } from "recharts"; 22 import { useSelector } from "react-redux"; 23 import api from "../../../../../common/api"; 24 import { IPieChartConfiguration } from "./types"; 25 import { widgetCommon } from "../../../Common/FormComponents/common/styleLibrary"; 26 import { IDashboardPanel } from "../types"; 27 import { splitSizeMetric, widgetDetailsToPanel } from "../utils"; 28 import { ErrorResponseHandler } from "../../../../../common/types"; 29 import { setErrorSnackMessage } from "../../../../../systemSlice"; 30 import { AppState, useAppDispatch } from "../../../../../store"; 31 32 interface IPieChartWidget { 33 title: string; 34 panelItem: IDashboardPanel; 35 timeStart: any; 36 timeEnd: any; 37 apiPrefix: string; 38 } 39 40 const PieChartMain = styled.div(({ theme }) => ({ 41 ...widgetCommon(theme), 42 "& .loadingAlign": { 43 width: "100%", 44 paddingTop: "15px", 45 textAlign: "center", 46 margin: "auto", 47 }, 48 "& .pieChartLabel": { 49 fontSize: 60, 50 color: get(theme, "signalColors.main", "#07193E"), 51 fontWeight: "bold", 52 width: "100%", 53 "& .unitText": { 54 color: get(theme, "mutedText", "#87888d"), 55 fontSize: 12, 56 }, 57 }, 58 "& .chartContainer": { 59 width: "100%", 60 height: 140, 61 }, 62 })); 63 64 const PieChartWidget = ({ 65 title, 66 panelItem, 67 timeStart, 68 timeEnd, 69 apiPrefix, 70 }: IPieChartWidget) => { 71 const dispatch = useAppDispatch(); 72 const [loading, setLoading] = useState<boolean>(false); 73 const [dataInner, setDataInner] = useState<object[]>([]); 74 const [dataOuter, setDataOuter] = useState<object[]>([]); 75 const [result, setResult] = useState<IDashboardPanel | null>(null); 76 const widgetVersion = useSelector( 77 (state: AppState) => state.dashboard.widgetLoadVersion, 78 ); 79 80 useEffect(() => { 81 setLoading(true); 82 }, [widgetVersion]); 83 84 useEffect(() => { 85 if (loading) { 86 let stepCalc = 0; 87 if (timeStart !== null && timeEnd !== null) { 88 const secondsInPeriod = 89 timeEnd.toUnixInteger() - timeStart.toUnixInteger(); 90 const periods = Math.floor(secondsInPeriod / 60); 91 92 stepCalc = periods < 1 ? 15 : periods; 93 } 94 95 api 96 .invoke( 97 "GET", 98 `/api/v1/${apiPrefix}/info/widgets/${ 99 panelItem.id 100 }/?step=${stepCalc}&${ 101 timeStart !== null ? `&start=${timeStart.toUnixInteger()}` : "" 102 }${timeStart !== null && timeEnd !== null ? "&" : ""}${ 103 timeEnd !== null ? `end=${timeEnd.toUnixInteger()}` : "" 104 }`, 105 ) 106 .then((res: any) => { 107 const widgetsWithValue = widgetDetailsToPanel(res, panelItem); 108 setDataInner(widgetsWithValue.data); 109 setDataOuter(widgetsWithValue.dataOuter as object[]); 110 setResult(widgetsWithValue); 111 setLoading(false); 112 }) 113 .catch((err: ErrorResponseHandler) => { 114 dispatch(setErrorSnackMessage(err)); 115 setLoading(false); 116 }); 117 } 118 }, [loading, panelItem, timeEnd, timeStart, dispatch, apiPrefix]); 119 120 const pieChartConfiguration = result 121 ? (result.widgetConfiguration as IPieChartConfiguration) 122 : []; 123 const middleLabel = result?.innerLabel; 124 125 const innerColors = get(pieChartConfiguration, "innerChart.colorList", []); 126 const outerColors = get(pieChartConfiguration, "outerChart.colorList", []); 127 128 return ( 129 <PieChartMain> 130 <Box className={"singleValueContainer"}> 131 <Box className={"titleContainer"}>{title}</Box> 132 {loading && ( 133 <Box className={"loadingAlign"}> 134 <Loader /> 135 </Box> 136 )} 137 {!loading && ( 138 <Box className={"contentContainer"}> 139 <span className={"pieChartLabel"}> 140 {middleLabel && splitSizeMetric(middleLabel)} 141 </span> 142 <Box className={"chartContainer"}> 143 <ResponsiveContainer width="99%"> 144 <PieChart margin={{ top: 5, bottom: 5 }}> 145 {dataOuter && ( 146 <Pie 147 data={dataOuter as object[]} 148 cx={"50%"} 149 cy={"50%"} 150 dataKey="value" 151 innerRadius={get( 152 pieChartConfiguration, 153 "outerChart.innerRadius", 154 0, 155 )} 156 outerRadius={get( 157 pieChartConfiguration, 158 "outerChart.outerRadius", 159 "80%", 160 )} 161 startAngle={get( 162 pieChartConfiguration, 163 "outerChart.startAngle", 164 0, 165 )} 166 endAngle={get( 167 pieChartConfiguration, 168 "outerChart.endAngle", 169 360, 170 )} 171 fill="#201763" 172 > 173 {dataOuter.map((entry, index) => ( 174 <Cell 175 key={`cellOuter-${index}`} 176 fill={ 177 typeof outerColors[index] === "undefined" 178 ? "#393939" 179 : outerColors[index] 180 } 181 /> 182 ))} 183 </Pie> 184 )} 185 {dataInner && ( 186 <Pie 187 data={dataInner as object[]} 188 dataKey="value" 189 cx={"50%"} 190 cy={"50%"} 191 innerRadius={get( 192 pieChartConfiguration, 193 "innerChart.innerRadius", 194 0, 195 )} 196 outerRadius={get( 197 pieChartConfiguration, 198 "innerChart.outerRadius", 199 "80%", 200 )} 201 startAngle={get( 202 pieChartConfiguration, 203 "innerChart.startAngle", 204 0, 205 )} 206 endAngle={get( 207 pieChartConfiguration, 208 "innerChart.endAngle", 209 360, 210 )} 211 fill="#201763" 212 > 213 {dataInner.map((entry, index) => { 214 return ( 215 <Cell 216 key={`cell-${index}`} 217 fill={ 218 typeof innerColors[index] === "undefined" 219 ? "#393939" 220 : innerColors[index] 221 } 222 /> 223 ); 224 })} 225 </Pie> 226 )} 227 </PieChart> 228 </ResponsiveContainer> 229 </Box> 230 </Box> 231 )} 232 </Box> 233 </PieChartMain> 234 ); 235 }; 236 237 export default PieChartWidget;