github.com/pelicanplatform/pelican@v1.0.5/web_ui/frontend/components/graphs/Graph.tsx (about) 1 /*************************************************************** 2 * 3 * Copyright (C) 2023, Pelican Project, Morgridge Institute for Research 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); you 6 * may not use this file except in compliance with the License. You may 7 * obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 ***************************************************************/ 18 "use client" 19 20 import {useEffect, useState} from "react"; 21 import { 22 Chart as ChartJS, 23 CategoryScale, 24 LinearScale, 25 TimeScale, 26 PointElement, 27 LineElement, 28 Title, 29 Tooltip, 30 Legend, 31 ChartOptions, 32 Colors 33 } from 'chart.js'; 34 35 36 import zoomPlugin from 'chartjs-plugin-zoom'; 37 import 'chartjs-adapter-luxon'; 38 39 import {BoxProps} from "@mui/material"; 40 41 import {Line} from "react-chartjs-2"; 42 import {Box, Skeleton, Typography} from "@mui/material"; 43 44 import {getDataFunction} from "@/components/graphs/prometheus"; 45 import {ChartData} from "chart.js"; 46 47 const defaultOptions: Partial<ChartOptions<"line">> = { 48 scales: { 49 x: { 50 type: 'time', 51 time: { 52 round: 'second', 53 } 54 } 55 } 56 } 57 58 interface GraphProps { 59 getData: getDataFunction; 60 drawer?: any; 61 options?: ChartOptions<"line"> 62 boxProps?: BoxProps; 63 } 64 65 export default function Graph({getData, options, boxProps, drawer}: GraphProps) { 66 67 let [data, _setData] = useState<ChartData<"line", any, any>>() 68 let [loading, setLoading] = useState<boolean>(true) 69 let [error, setError] = useState<string>("") 70 71 72 async function setData() { 73 try { 74 let response = await getData() 75 _setData(response) 76 setLoading(false) 77 if(response.datasets[0].data.length == 0){ 78 let date = new Date(Date.now()).toLocaleTimeString() 79 setError(`No data returned by database as of ${date}; Plot will auto-refresh`) 80 } else { 81 setError("") 82 } 83 } catch (e: any) { 84 let date = new Date(Date.now()).toLocaleString() 85 setError(date + " : " + e.message + "; Plot will auto-refresh") 86 } 87 } 88 89 useEffect(() => { 90 91 ChartJS.register( 92 CategoryScale, 93 LinearScale, 94 PointElement, 95 LineElement, 96 Title, 97 Tooltip, 98 Legend, 99 TimeScale, 100 zoomPlugin, 101 Colors 102 ); 103 104 105 // Do the initial data fetch 106 setData() 107 108 // Refetch the data every minute 109 const interval = setInterval(() => setData(), 60000); 110 return () => clearInterval(interval); 111 112 }, [getData]) 113 114 return ( 115 <Box> 116 { loading || !data ? 117 <Box borderRadius={2} overflow={"hidden"}><Skeleton variant={"rectangular"} width={"100%"} height={"300px"} /></Box> : 118 <> 119 <Box m={"auto"} {...boxProps}> 120 <Line 121 data={data} 122 options={{ 123 ...defaultOptions, 124 ...options 125 }} 126 /> 127 </Box> 128 <Box display={"flex"}> 129 { drawer ? drawer : undefined } 130 </Box> 131 </> 132 } 133 <Box display={"flex"} pt={1}> 134 <Typography m={"auto"} color={"red"} variant={"body2"}>{error}</Typography> 135 </Box> 136 </Box> 137 ) 138 139 }