mirror of
https://github.com/hibiken/asynqmon.git
synced 2025-02-23 20:30:12 +08:00
Add app wide snackbar
This commit is contained in:
parent
ce978f4516
commit
d8c549adb2
@ -1,4 +1,5 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
|
import { connect, ConnectedProps } from "react-redux";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
|
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
|
||||||
import { makeStyles } from "@material-ui/core/styles";
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
@ -7,12 +8,19 @@ import Drawer from "@material-ui/core/Drawer";
|
|||||||
import Toolbar from "@material-ui/core/Toolbar";
|
import Toolbar from "@material-ui/core/Toolbar";
|
||||||
import List from "@material-ui/core/List";
|
import List from "@material-ui/core/List";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
|
import Snackbar from "@material-ui/core/Snackbar";
|
||||||
|
import SnackbarContent from "@material-ui/core/SnackbarContent";
|
||||||
import IconButton from "@material-ui/core/IconButton";
|
import IconButton from "@material-ui/core/IconButton";
|
||||||
|
import Slide from "@material-ui/core/Slide";
|
||||||
|
import { TransitionProps } from "@material-ui/core/transitions";
|
||||||
import MenuIcon from "@material-ui/icons/Menu";
|
import MenuIcon from "@material-ui/icons/Menu";
|
||||||
import BarChartIcon from "@material-ui/icons/BarChart";
|
import BarChartIcon from "@material-ui/icons/BarChart";
|
||||||
import LayersIcon from "@material-ui/icons/Layers";
|
import LayersIcon from "@material-ui/icons/Layers";
|
||||||
import SettingsIcon from "@material-ui/icons/Settings";
|
import SettingsIcon from "@material-ui/icons/Settings";
|
||||||
|
import CloseIcon from "@material-ui/icons/Close";
|
||||||
|
import { AppState } from "./store";
|
||||||
import { paths } from "./paths";
|
import { paths } from "./paths";
|
||||||
|
import { closeSnackbar } from "./actions/snackbarActions";
|
||||||
import ListItemLink from "./components/ListItemLink";
|
import ListItemLink from "./components/ListItemLink";
|
||||||
import SchedulersView from "./views/SchedulersView";
|
import SchedulersView from "./views/SchedulersView";
|
||||||
import DashboardView from "./views/DashboardView";
|
import DashboardView from "./views/DashboardView";
|
||||||
@ -71,6 +79,13 @@ const useStyles = makeStyles((theme) => ({
|
|||||||
width: theme.spacing(9),
|
width: theme.spacing(9),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
snackbar: {
|
||||||
|
background: theme.palette.grey["A400"],
|
||||||
|
color: "#ffffff",
|
||||||
|
},
|
||||||
|
snackbarCloseIcon: {
|
||||||
|
color: theme.palette.grey[400],
|
||||||
|
},
|
||||||
appBarSpacer: theme.mixins.toolbar,
|
appBarSpacer: theme.mixins.toolbar,
|
||||||
mainContainer: {
|
mainContainer: {
|
||||||
display: "flex",
|
display: "flex",
|
||||||
@ -96,7 +111,21 @@ const useStyles = makeStyles((theme) => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function App() {
|
function mapStateToProps(state: AppState) {
|
||||||
|
return { snackbar: state.snackbar };
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapDispatchToProps = {
|
||||||
|
closeSnackbar,
|
||||||
|
};
|
||||||
|
|
||||||
|
const connector = connect(mapStateToProps, mapDispatchToProps);
|
||||||
|
|
||||||
|
function SlideUpTransition(props: TransitionProps) {
|
||||||
|
return <Slide {...props} direction="up" />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function App(props: ConnectedProps<typeof connector>) {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const [open, setOpen] = useState(true);
|
const [open, setOpen] = useState(true);
|
||||||
const toggleDrawer = () => {
|
const toggleDrawer = () => {
|
||||||
@ -142,6 +171,31 @@ function App() {
|
|||||||
}}
|
}}
|
||||||
open={open}
|
open={open}
|
||||||
>
|
>
|
||||||
|
<Snackbar
|
||||||
|
anchorOrigin={{ vertical: "bottom", horizontal: "left" }}
|
||||||
|
open={props.snackbar.isOpen}
|
||||||
|
autoHideDuration={6000}
|
||||||
|
onClose={props.closeSnackbar}
|
||||||
|
TransitionComponent={SlideUpTransition}
|
||||||
|
>
|
||||||
|
<SnackbarContent
|
||||||
|
message={props.snackbar.message}
|
||||||
|
className={classes.snackbar}
|
||||||
|
action={
|
||||||
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
aria-label="close"
|
||||||
|
color="inherit"
|
||||||
|
onClick={props.closeSnackbar}
|
||||||
|
>
|
||||||
|
<CloseIcon
|
||||||
|
className={classes.snackbarCloseIcon}
|
||||||
|
fontSize="small"
|
||||||
|
/>
|
||||||
|
</IconButton>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Snackbar>
|
||||||
<div className={classes.appBarSpacer} />
|
<div className={classes.appBarSpacer} />
|
||||||
<div className={classes.sidebarContainer}>
|
<div className={classes.sidebarContainer}>
|
||||||
<List>
|
<List>
|
||||||
@ -191,4 +245,4 @@ function App() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App;
|
export default connector(App);
|
||||||
|
12
ui/src/actions/snackbarActions.ts
Normal file
12
ui/src/actions/snackbarActions.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
export const CLOSE_SNACKBAR = "CLOSE_SNACKBAR";
|
||||||
|
|
||||||
|
interface CloseSnakbarAction {
|
||||||
|
type: typeof CLOSE_SNACKBAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Union of all snackbar related action types
|
||||||
|
export type SnackbarActionTypes = CloseSnakbarAction;
|
||||||
|
|
||||||
|
export function closeSnackbar() {
|
||||||
|
return { type: CLOSE_SNACKBAR };
|
||||||
|
}
|
61
ui/src/reducers/snackbarReducer.ts
Normal file
61
ui/src/reducers/snackbarReducer.ts
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import {
|
||||||
|
CLOSE_SNACKBAR,
|
||||||
|
SnackbarActionTypes,
|
||||||
|
} from "../actions/snackbarActions";
|
||||||
|
import {
|
||||||
|
DELETE_DEAD_TASK_SUCCESS,
|
||||||
|
DELETE_RETRY_TASK_SUCCESS,
|
||||||
|
DELETE_SCHEDULED_TASK_SUCCESS,
|
||||||
|
TasksActionTypes,
|
||||||
|
} from "../actions/tasksActions";
|
||||||
|
|
||||||
|
interface SnackbarState {
|
||||||
|
isOpen: boolean;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: SnackbarState = {
|
||||||
|
isOpen: false,
|
||||||
|
message: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
function snackbarReducer(
|
||||||
|
state = initialState,
|
||||||
|
action: TasksActionTypes | SnackbarActionTypes
|
||||||
|
): SnackbarState {
|
||||||
|
switch (action.type) {
|
||||||
|
case CLOSE_SNACKBAR:
|
||||||
|
return {
|
||||||
|
// Note: We keep the message state unchanged for
|
||||||
|
// smoother transition animation.
|
||||||
|
...state,
|
||||||
|
isOpen: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
case DELETE_SCHEDULED_TASK_SUCCESS:
|
||||||
|
return {
|
||||||
|
isOpen: true,
|
||||||
|
// TODO: show only task id
|
||||||
|
message: `Scheduled task ${action.taskKey} deleted`,
|
||||||
|
};
|
||||||
|
|
||||||
|
case DELETE_RETRY_TASK_SUCCESS:
|
||||||
|
return {
|
||||||
|
isOpen: true,
|
||||||
|
// TODO: show only task id
|
||||||
|
message: `Retry task ${action.taskKey} deleted`,
|
||||||
|
};
|
||||||
|
|
||||||
|
case DELETE_DEAD_TASK_SUCCESS:
|
||||||
|
return {
|
||||||
|
isOpen: true,
|
||||||
|
// TODO: show only task id
|
||||||
|
message: `Dead task ${action.taskKey} deleted`,
|
||||||
|
};
|
||||||
|
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default snackbarReducer;
|
@ -3,12 +3,14 @@ import settingsReducer from "./reducers/settingsReducer";
|
|||||||
import queuesReducer from "./reducers/queuesReducer";
|
import queuesReducer from "./reducers/queuesReducer";
|
||||||
import tasksReducer from "./reducers/tasksReducer";
|
import tasksReducer from "./reducers/tasksReducer";
|
||||||
import schedulerEntriesReducer from "./reducers/schedulerEntriesReducer";
|
import schedulerEntriesReducer from "./reducers/schedulerEntriesReducer";
|
||||||
|
import snackbarReducer from "./reducers/snackbarReducer";
|
||||||
|
|
||||||
const rootReducer = combineReducers({
|
const rootReducer = combineReducers({
|
||||||
settings: settingsReducer,
|
settings: settingsReducer,
|
||||||
queues: queuesReducer,
|
queues: queuesReducer,
|
||||||
tasks: tasksReducer,
|
tasks: tasksReducer,
|
||||||
schedulerEntries: schedulerEntriesReducer,
|
schedulerEntries: schedulerEntriesReducer,
|
||||||
|
snackbar: snackbarReducer,
|
||||||
});
|
});
|
||||||
|
|
||||||
// AppState is the top-level application state maintained by redux store.
|
// AppState is the top-level application state maintained by redux store.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user