github.com/minio/console@v1.4.1/web-app/src/screens/Console/Dashboard/Prometheus/PrDashboard.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, { Fragment, useState } from "react"; 18 import { 19 Box, 20 Button, 21 Grid, 22 HelpBox, 23 PageLayout, 24 ProgressBar, 25 PrometheusErrorIcon, 26 SyncIcon, 27 TabItemProps, 28 Tabs, 29 } from "mds"; 30 import { IDashboardPanel } from "./types"; 31 import { panelsConfiguration } from "./utils"; 32 import { componentToUse } from "./widgetUtils"; 33 import { useAppDispatch, useAppSelector } from "../../../../store"; 34 import { 35 DLayoutColumnProps, 36 DLayoutRowProps, 37 resourcesPanelsLayout, 38 resourcesPanelsLayoutAdvanced, 39 RowPanelLayout, 40 summaryPanelsLayout, 41 trafficPanelsLayout, 42 } from "./Widgets/LayoutUtil"; 43 import { getUsageAsync } from "../dashboardThunks"; 44 import { reloadWidgets } from "../dashboardSlice"; 45 import { selFeatures } from "../../consoleSlice"; 46 import { AdminInfoResponse } from "api/consoleApi"; 47 import ZoomWidget from "./ZoomWidget"; 48 import DateRangeSelector from "../../Common/FormComponents/DateRangeSelector/DateRangeSelector"; 49 import MergedWidgetsRenderer from "./Widgets/MergedWidgetsRenderer"; 50 import BasicDashboard from "../BasicDashboard/BasicDashboard"; 51 52 interface IPrDashboard { 53 apiPrefix?: string; 54 usage: AdminInfoResponse | null; 55 } 56 57 const PrDashboard = ({ apiPrefix = "admin", usage }: IPrDashboard) => { 58 const dispatch = useAppDispatch(); 59 const status = useAppSelector((state) => state.dashboard.status); 60 const zoomOpen = useAppSelector((state) => state.dashboard.zoom.openZoom); 61 const zoomWidget = useAppSelector( 62 (state) => state.dashboard.zoom.widgetRender, 63 ); 64 const features = useAppSelector(selFeatures); 65 const obOnly = !!features?.includes("object-browser-only"); 66 let hideMenu = false; 67 if (features?.includes("hide-menu")) { 68 hideMenu = true; 69 } else if (obOnly) { 70 hideMenu = true; 71 } 72 73 const [timeStart, setTimeStart] = useState<any>(null); 74 const [timeEnd, setTimeEnd] = useState<any>(null); 75 const panelInformation = panelsConfiguration; 76 const [curTab, setCurTab] = useState<string>("info"); 77 78 const getPanelDetails = (id: number) => { 79 return panelInformation.find((panel) => panel.id === id); 80 }; 81 82 const triggerLoad = () => { 83 dispatch(reloadWidgets()); 84 }; 85 86 const renderCmpByConfig = ( 87 panelInfo: IDashboardPanel | undefined, 88 key: string, 89 ) => { 90 return ( 91 <Fragment key={`widget-${key}`}> 92 {panelInfo ? ( 93 <Fragment> 94 <Box> 95 {panelInfo.mergedPanels ? ( 96 <MergedWidgetsRenderer 97 info={panelInfo} 98 timeStart={timeStart} 99 timeEnd={timeEnd} 100 loading={true} 101 apiPrefix={apiPrefix} 102 /> 103 ) : ( 104 componentToUse( 105 panelInfo, 106 timeStart, 107 timeEnd, 108 true, 109 apiPrefix, 110 zoomOpen, 111 ) 112 )} 113 </Box> 114 </Fragment> 115 ) : null} 116 </Fragment> 117 ); 118 }; 119 120 const renderPanelItems = (layoutRows: DLayoutRowProps[]) => { 121 return layoutRows.reduce((prev: any[], rowItem, rIdx) => { 122 const { columns = [] } = rowItem; 123 const cellItems: any[] = columns.map( 124 (cellItem: DLayoutColumnProps, colIdx: number) => { 125 const panelInfo = getPanelDetails(cellItem.componentId); 126 return renderCmpByConfig(panelInfo, `${rIdx}-${colIdx}`); 127 }, 128 ); 129 const rowConfig = ( 130 <Box sx={rowItem.sx} key={`layout-row-${rIdx}`}> 131 {cellItems} 132 </Box> 133 ); 134 return [...prev, rowConfig]; 135 }, []); 136 }; 137 138 const renderSummaryPanels = () => { 139 return renderPanelItems(summaryPanelsLayout); 140 }; 141 142 const renderTrafficPanels = () => { 143 return renderPanelItems(trafficPanelsLayout); 144 }; 145 146 const renderResourcesPanels = () => { 147 return renderPanelItems(resourcesPanelsLayout); 148 }; 149 150 const renderAdvancedResourcesPanels = () => { 151 return renderPanelItems(resourcesPanelsLayoutAdvanced); 152 }; 153 154 const prometheusOptionsDisabled = 155 usage?.advancedMetricsStatus === "not configured"; 156 157 const searchBox = ( 158 <Box sx={{ marginBottom: 20 }}> 159 {curTab === "info" ? ( 160 <Grid container> 161 <Grid item> 162 <Box 163 sx={{ 164 fontSize: 18, 165 lineHeight: 2, 166 fontWeight: 700, 167 }} 168 > 169 Server Information 170 </Box> 171 </Grid> 172 <Grid item xs> 173 <Grid container direction="row-reverse"> 174 <Grid item> 175 <Button 176 id={"sync"} 177 type="button" 178 variant="callAction" 179 onClick={() => { 180 dispatch(getUsageAsync()); 181 }} 182 disabled={status === "loading"} 183 icon={<SyncIcon />} 184 label={"Sync"} 185 /> 186 </Grid> 187 </Grid> 188 </Grid> 189 </Grid> 190 ) : ( 191 <DateRangeSelector 192 timeStart={timeStart} 193 setTimeStart={setTimeStart} 194 timeEnd={timeEnd} 195 setTimeEnd={setTimeEnd} 196 triggerSync={triggerLoad} 197 /> 198 )} 199 </Box> 200 ); 201 202 const infoTab: TabItemProps = { 203 tabConfig: { label: "Info", id: "info", disabled: false }, 204 content: ( 205 <Fragment> 206 {(!usage || status === "loading") && <ProgressBar />} 207 {usage && status === "idle" && ( 208 <Fragment> 209 {searchBox} 210 <BasicDashboard usage={usage} /> 211 </Fragment> 212 )} 213 </Fragment> 214 ), 215 }; 216 217 const prometheusTabs: TabItemProps[] = [ 218 { 219 tabConfig: { 220 label: "Usage", 221 id: "usage", 222 disabled: prometheusOptionsDisabled, 223 }, 224 content: ( 225 <Fragment> 226 {searchBox} 227 <RowPanelLayout> 228 {usage?.advancedMetricsStatus === "unavailable" && ( 229 <HelpBox 230 iconComponent={<PrometheusErrorIcon />} 231 title={"We can’t retrieve advanced metrics at this time."} 232 help={ 233 <Box 234 sx={{ 235 fontSize: "14px", 236 }} 237 > 238 It looks like Prometheus is not available or reachable at 239 the moment. 240 </Box> 241 } 242 /> 243 )} 244 {panelInformation.length ? renderSummaryPanels() : null} 245 </RowPanelLayout> 246 </Fragment> 247 ), 248 }, 249 { 250 tabConfig: { 251 label: "Traffic", 252 id: "traffic", 253 disabled: prometheusOptionsDisabled, 254 }, 255 content: ( 256 <Fragment> 257 {searchBox} 258 <RowPanelLayout> 259 {usage?.advancedMetricsStatus === "unavailable" && ( 260 <HelpBox 261 iconComponent={<PrometheusErrorIcon />} 262 title={"We can’t retrieve advanced metrics at this time."} 263 help={ 264 <Box 265 sx={{ 266 fontSize: "14px", 267 }} 268 > 269 It looks like Prometheus is not available or reachable at 270 the moment. 271 </Box> 272 } 273 /> 274 )} 275 {panelInformation.length ? renderTrafficPanels() : null} 276 </RowPanelLayout> 277 </Fragment> 278 ), 279 }, 280 { 281 tabConfig: { 282 label: "Resources", 283 id: "resources", 284 disabled: prometheusOptionsDisabled, 285 }, 286 content: ( 287 <Fragment> 288 {searchBox} 289 <RowPanelLayout> 290 {usage?.advancedMetricsStatus === "unavailable" && ( 291 <HelpBox 292 iconComponent={<PrometheusErrorIcon />} 293 title={"We can’t retrieve advanced metrics at this time."} 294 help={ 295 <Box 296 sx={{ 297 fontSize: "14px", 298 }} 299 > 300 It looks like Prometheus is not available or reachable at 301 the moment. 302 </Box> 303 } 304 /> 305 )} 306 {panelInformation.length ? renderResourcesPanels() : null} 307 <h2 style={{ margin: 0, borderBottom: "1px solid #dedede" }}> 308 Advanced 309 </h2> 310 {panelInformation.length ? renderAdvancedResourcesPanels() : null} 311 </RowPanelLayout> 312 </Fragment> 313 ), 314 }, 315 ]; 316 317 let tabsOptions: TabItemProps[] = [infoTab, ...prometheusTabs]; 318 319 return ( 320 <PageLayout 321 sx={{ 322 padding: hideMenu ? 0 : "2rem", 323 }} 324 > 325 {zoomOpen && ( 326 <ZoomWidget 327 modalOpen={zoomOpen} 328 timeStart={timeStart} 329 timeEnd={timeEnd} 330 widgetRender={0} 331 value={zoomWidget} 332 apiPrefix={apiPrefix} 333 /> 334 )} 335 336 <Tabs 337 horizontal 338 options={tabsOptions} 339 currentTabOrPath={curTab} 340 onTabClick={(newValue) => { 341 setCurTab(newValue); 342 }} 343 /> 344 </PageLayout> 345 ); 346 }; 347 348 export default PrDashboard;