github.com/wfusion/gofusion@v1.1.14/common/infra/asynq/asynqmon/ui/src/App.tsx (about) 1 import React from "react"; 2 import { connect, ConnectedProps } from "react-redux"; 3 import clsx from "clsx"; 4 import { BrowserRouter as Router, Switch, Route } from "react-router-dom"; 5 import { makeStyles, Theme, ThemeProvider } from "@material-ui/core/styles"; 6 import AppBar from "@material-ui/core/AppBar"; 7 import Drawer from "@material-ui/core/Drawer"; 8 import Toolbar from "@material-ui/core/Toolbar"; 9 import List from "@material-ui/core/List"; 10 import ListItem from "@material-ui/core/ListItem"; 11 import ListItemIcon from "@material-ui/core/ListItemIcon"; 12 import ListItemText from "@material-ui/core/ListItemText"; 13 import Snackbar from "@material-ui/core/Snackbar"; 14 import SnackbarContent from "@material-ui/core/SnackbarContent"; 15 import IconButton from "@material-ui/core/IconButton"; 16 import Slide from "@material-ui/core/Slide"; 17 import { TransitionProps } from "@material-ui/core/transitions"; 18 import MenuIcon from "@material-ui/icons/Menu"; 19 import BarChartIcon from "@material-ui/icons/BarChart"; 20 import LayersIcon from "@material-ui/icons/Layers"; 21 import SettingsIcon from "@material-ui/icons/Settings"; 22 import ScheduleIcon from "@material-ui/icons/Schedule"; 23 import FeedbackIcon from "@material-ui/icons/Feedback"; 24 import TimelineIcon from "@material-ui/icons/Timeline"; 25 import DoubleArrowIcon from "@material-ui/icons/DoubleArrow"; 26 import CloseIcon from "@material-ui/icons/Close"; 27 import { AppState } from "./store"; 28 import { paths as getPaths } from "./paths"; 29 import { isDarkTheme, useTheme } from "./theme"; 30 import { closeSnackbar } from "./actions/snackbarActions"; 31 import { toggleDrawer } from "./actions/settingsActions"; 32 import ListItemLink from "./components/ListItemLink"; 33 import SchedulersView from "./views/SchedulersView"; 34 import DashboardView from "./views/DashboardView"; 35 import TasksView from "./views/TasksView"; 36 import TaskDetailsView from "./views/TaskDetailsView"; 37 import SettingsView from "./views/SettingsView"; 38 import ServersView from "./views/ServersView"; 39 import RedisInfoView from "./views/RedisInfoView"; 40 import MetricsView from "./views/MetricsView"; 41 import PageNotFoundView from "./views/PageNotFoundView"; 42 import { ReactComponent as Logo } from "./images/logo-color.svg"; 43 import { ReactComponent as LogoDarkTheme } from "./images/logo-white.svg"; 44 45 const drawerWidth = 220; 46 47 // FIXME: For some reason, the following code does not work: 48 // makeStyles(theme => ({ /* use theme here */})); 49 // Using closure to work around this problem. 50 const useStyles = (theme: Theme) => 51 makeStyles({ 52 root: { 53 display: "flex", 54 }, 55 toolbar: { 56 paddingRight: 24, // keep right padding when drawer closed 57 }, 58 toolbarIcon: { 59 display: "flex", 60 alignItems: "center", 61 justifyContent: "flex-end", 62 padding: "0 8px", 63 ...theme.mixins.toolbar, 64 }, 65 appBar: { 66 backgroundColor: theme.palette.background.paper, 67 zIndex: theme.zIndex.drawer + 1, 68 }, 69 menuButton: { 70 marginRight: theme.spacing(1), 71 color: isDarkTheme(theme) 72 ? theme.palette.grey[100] 73 : theme.palette.grey[700], 74 }, 75 menuButtonHidden: { 76 display: "none", 77 }, 78 drawerPaper: { 79 position: "relative", 80 whiteSpace: "nowrap", 81 width: drawerWidth, 82 transition: theme.transitions.create("width", { 83 easing: theme.transitions.easing.sharp, 84 duration: theme.transitions.duration.enteringScreen, 85 }), 86 border: "none", 87 }, 88 drawerPaperClose: { 89 overflowX: "hidden", 90 transition: theme.transitions.create("width", { 91 easing: theme.transitions.easing.sharp, 92 duration: theme.transitions.duration.leavingScreen, 93 }), 94 width: theme.spacing(7), 95 [theme.breakpoints.up("sm")]: { 96 width: theme.spacing(9), 97 }, 98 }, 99 snackbar: { 100 background: theme.palette.grey["A400"], 101 color: "#ffffff", 102 }, 103 snackbarCloseIcon: { 104 color: theme.palette.grey[400], 105 }, 106 appBarSpacer: theme.mixins.toolbar, 107 mainContainer: { 108 display: "flex", 109 width: "100vw", 110 }, 111 content: { 112 flex: 1, 113 height: "100vh", 114 overflow: "hidden", 115 background: theme.palette.background.paper, 116 }, 117 contentWrapper: { 118 height: "100%", 119 display: "flex", 120 paddingTop: "64px", // app-bar height 121 overflow: "scroll", 122 }, 123 sidebarContainer: { 124 display: "flex", 125 justifyContent: "space-between", 126 height: "100%", 127 flexDirection: "column", 128 }, 129 listItem: { 130 borderTopRightRadius: "24px", 131 borderBottomRightRadius: "24px", 132 }, 133 }); 134 135 function mapStateToProps(state: AppState) { 136 return { 137 snackbar: state.snackbar, 138 themePreference: state.settings.themePreference, 139 isDrawerOpen: state.settings.isDrawerOpen, 140 }; 141 } 142 143 const mapDispatchToProps = { 144 closeSnackbar, 145 toggleDrawer, 146 }; 147 148 const connector = connect(mapStateToProps, mapDispatchToProps); 149 150 function SlideUpTransition(props: TransitionProps) { 151 return <Slide {...props} direction="up" />; 152 } 153 154 function App(props: ConnectedProps<typeof connector>) { 155 const theme = useTheme(props.themePreference); 156 const classes = useStyles(theme)(); 157 const paths = getPaths(); 158 return ( 159 <ThemeProvider theme={theme}> 160 <Router> 161 <div className={classes.root}> 162 <AppBar 163 position="absolute" 164 className={classes.appBar} 165 variant="outlined" 166 > 167 <Toolbar className={classes.toolbar}> 168 <IconButton 169 edge="start" 170 color="inherit" 171 aria-label="open drawer" 172 onClick={props.toggleDrawer} 173 className={classes.menuButton} 174 > 175 <MenuIcon /> 176 </IconButton> 177 {isDarkTheme(theme) ? ( 178 <LogoDarkTheme width={200} height={48} /> 179 ) : ( 180 <Logo width={200} height={48} /> 181 )} 182 </Toolbar> 183 </AppBar> 184 <div className={classes.mainContainer}> 185 <Drawer 186 variant="permanent" 187 classes={{ 188 paper: clsx( 189 classes.drawerPaper, 190 !props.isDrawerOpen && classes.drawerPaperClose 191 ), 192 }} 193 open={props.isDrawerOpen} 194 > 195 <Snackbar 196 anchorOrigin={{ vertical: "bottom", horizontal: "left" }} 197 open={props.snackbar.isOpen} 198 autoHideDuration={6000} 199 onClose={props.closeSnackbar} 200 TransitionComponent={SlideUpTransition} 201 > 202 <SnackbarContent 203 message={props.snackbar.message} 204 className={classes.snackbar} 205 action={ 206 <IconButton 207 size="small" 208 aria-label="close" 209 color="inherit" 210 onClick={props.closeSnackbar} 211 > 212 <CloseIcon 213 className={classes.snackbarCloseIcon} 214 fontSize="small" 215 /> 216 </IconButton> 217 } 218 /> 219 </Snackbar> 220 <div className={classes.appBarSpacer} /> 221 <div className={classes.sidebarContainer}> 222 <List> 223 <div> 224 <ListItemLink 225 to={paths.HOME} 226 primary="Queues" 227 icon={<BarChartIcon />} 228 /> 229 <ListItemLink 230 to={paths.SERVERS} 231 primary="Servers" 232 icon={<DoubleArrowIcon />} 233 /> 234 <ListItemLink 235 to={paths.SCHEDULERS} 236 primary="Schedulers" 237 icon={<ScheduleIcon />} 238 /> 239 <ListItemLink 240 to={paths.REDIS} 241 primary="Redis" 242 icon={<LayersIcon />} 243 /> 244 {window.PROMETHEUS_SERVER_ADDRESS && ( 245 <ListItemLink 246 to={paths.QUEUE_METRICS} 247 primary="Metrics" 248 icon={<TimelineIcon />} 249 /> 250 )} 251 </div> 252 </List> 253 <List> 254 <ListItemLink 255 to={paths.SETTINGS} 256 primary="Settings" 257 icon={<SettingsIcon />} 258 /> 259 <ListItem 260 button 261 component="a" 262 className={classes.listItem} 263 href="https://github.com/hibiken/asynqmon/issues" 264 target="_blank" 265 > 266 <ListItemIcon> 267 <FeedbackIcon /> 268 </ListItemIcon> 269 <ListItemText primary="Send Feedback" /> 270 </ListItem> 271 </List> 272 </div> 273 </Drawer> 274 <main className={classes.content}> 275 <div className={classes.contentWrapper}> 276 <Switch> 277 <Route exact path={paths.TASK_DETAILS}> 278 <TaskDetailsView /> 279 </Route> 280 <Route exact path={paths.QUEUE_DETAILS}> 281 <TasksView /> 282 </Route> 283 <Route exact path={paths.SCHEDULERS}> 284 <SchedulersView /> 285 </Route> 286 <Route exact path={paths.SERVERS}> 287 <ServersView /> 288 </Route> 289 <Route exact path={paths.REDIS}> 290 <RedisInfoView /> 291 </Route> 292 <Route exact path={paths.SETTINGS}> 293 <SettingsView /> 294 </Route> 295 <Route exact path={paths.HOME}> 296 <DashboardView /> 297 </Route> 298 <Route exact path={paths.QUEUE_METRICS}> 299 <MetricsView /> 300 </Route> 301 <Route path="*"> 302 <PageNotFoundView /> 303 </Route> 304 </Switch> 305 </div> 306 </main> 307 </div> 308 </div> 309 </Router> 310 </ThemeProvider> 311 ); 312 } 313 314 export default connector(App);