diff --git a/notes/theme-example.json b/notes/theme-example.json new file mode 100644 index 0000000..94477c9 --- /dev/null +++ b/notes/theme-example.json @@ -0,0 +1,341 @@ +{ + "breakpoints":{ + "keys":[ + "xs", + "sm", + "md", + "lg", + "xl" + ], + "values":{ + "xs":0, + "sm":600, + "md":960, + "lg":1280, + "xl":1920 + } + }, + "direction":"ltr", + "mixins":{ + "toolbar":{ + "minHeight":56, + "@media (min-width:0px) and (orientation: landscape)":{ + "minHeight":48 + }, + "@media (min-width:600px)":{ + "minHeight":64 + } + } + }, + "overrides":{ + "MuiBackdrop":{ + "root":{ + "backgroundColor":"#4A4A4A1A" + } + }, + "MuiMenu":{ + "paper":{ + "boxShadow":"0px 3px 11px 0px #E8EAFC, 0 3px 3px -2px #B2B2B21A, 0 1px 8px 0 #9A9A9A1A" + } + }, + "MuiSelect":{ + "icon":{ + "color":"#B9B9B9" + } + }, + "MuiListItem":{ + "root":{ + "&$selected":{ + "backgroundColor":"#F3F5FF !important", + "&:focus":{ + "backgroundColor":"#F3F5FF" + } + } + }, + "button":{ + "&:hover, &:focus":{ + "backgroundColor":"#F3F5FF" + } + } + }, + "MuiTouchRipple":{ + "child":{ + "backgroundColor":"white" + } + }, + "MuiTableRow":{ + "root":{ + "height":56 + } + }, + "MuiTableCell":{ + "root":{ + "borderBottom":"1px solid rgba(224, 224, 224, .5)", + "paddingLeft":24 + }, + "head":{ + "fontSize":"0.95rem" + }, + "body":{ + "fontSize":"0.95rem" + } + }, + "PrivateSwitchBase":{ + "root":{ + "marginLeft":10 + } + } + }, + "palette":{ + "common":{ + "black":"#000", + "white":"#fff" + }, + "type":"light", + "primary":{ + "main":"#536DFE", + "light":"#798dfe", + "dark":"#072cfe", + "contrastText":"#fff" + }, + "secondary":{ + "main":"#FF5C93", + "light":"#ff82ac", + "dark":"#ff0f60", + "contrastText":"#FFFFFF" + }, + "error":{ + "light":"#e57373", + "main":"#f44336", + "dark":"#d32f2f", + "contrastText":"#fff" + }, + "warning":{ + "main":"#FFC260", + "light":"#ffd186", + "dark":"#ffa513", + "contrastText":"rgba(0, 0, 0, 0.87)" + }, + "info":{ + "main":"#9013FE", + "light":"#a239fe", + "dark":"#6801c4", + "contrastText":"#fff" + }, + "success":{ + "main":"#3CD4A0", + "light":"#5bdbaf", + "dark":"#23a075", + "contrastText":"rgba(0, 0, 0, 0.87)" + }, + "grey":{ + "50":"#fafafa", + "100":"#f5f5f5", + "200":"#eeeeee", + "300":"#e0e0e0", + "400":"#bdbdbd", + "500":"#9e9e9e", + "600":"#757575", + "700":"#616161", + "800":"#424242", + "900":"#212121", + "A100":"#d5d5d5", + "A200":"#aaaaaa", + "A400":"#303030", + "A700":"#616161" + }, + "contrastThreshold":3, + "tonalOffset":0.2, + "text":{ + "primary":"#4A4A4A", + "secondary":"#6E6E6E", + "disabled":"rgba(0, 0, 0, 0.38)", + "hint":"#B9B9B9" + }, + "divider":"rgba(0, 0, 0, 0.12)", + "background":{ + "paper":"#fff", + "default":"#F6F7FF", + "light":"#F3F5FF" + }, + "action":{ + "active":"rgba(0, 0, 0, 0.54)", + "hover":"rgba(0, 0, 0, 0.04)", + "hoverOpacity":0.04, + "selected":"rgba(0, 0, 0, 0.08)", + "selectedOpacity":0.08, + "disabled":"rgba(0, 0, 0, 0.26)", + "disabledBackground":"rgba(0, 0, 0, 0.12)", + "disabledOpacity":0.38, + "focus":"rgba(0, 0, 0, 0.12)", + "focusOpacity":0.12, + "activatedOpacity":0.12 + } + }, + "props":{ + + }, + "shadows":[ + "none", + "0px 2px 1px -1px rgba(0,0,0,0.2),0px 1px 1px 0px rgba(0,0,0,0.14),0px 1px 3px 0px rgba(0,0,0,0.12)", + "0px 3px 1px -2px rgba(0,0,0,0.2),0px 2px 2px 0px rgba(0,0,0,0.14),0px 1px 5px 0px rgba(0,0,0,0.12)", + "0px 3px 3px -2px rgba(0,0,0,0.2),0px 3px 4px 0px rgba(0,0,0,0.14),0px 1px 8px 0px rgba(0,0,0,0.12)", + "0px 2px 4px -1px rgba(0,0,0,0.2),0px 4px 5px 0px rgba(0,0,0,0.14),0px 1px 10px 0px rgba(0,0,0,0.12)", + "0px 3px 5px -1px rgba(0,0,0,0.2),0px 5px 8px 0px rgba(0,0,0,0.14),0px 1px 14px 0px rgba(0,0,0,0.12)", + "0px 3px 5px -1px rgba(0,0,0,0.2),0px 6px 10px 0px rgba(0,0,0,0.14),0px 1px 18px 0px rgba(0,0,0,0.12)", + "0px 4px 5px -2px rgba(0,0,0,0.2),0px 7px 10px 1px rgba(0,0,0,0.14),0px 2px 16px 1px rgba(0,0,0,0.12)", + "0px 5px 5px -3px rgba(0,0,0,0.2),0px 8px 10px 1px rgba(0,0,0,0.14),0px 3px 14px 2px rgba(0,0,0,0.12)", + "0px 5px 6px -3px rgba(0,0,0,0.2),0px 9px 12px 1px rgba(0,0,0,0.14),0px 3px 16px 2px rgba(0,0,0,0.12)", + "0px 6px 6px -3px rgba(0,0,0,0.2),0px 10px 14px 1px rgba(0,0,0,0.14),0px 4px 18px 3px rgba(0,0,0,0.12)", + "0px 6px 7px -4px rgba(0,0,0,0.2),0px 11px 15px 1px rgba(0,0,0,0.14),0px 4px 20px 3px rgba(0,0,0,0.12)", + "0px 7px 8px -4px rgba(0,0,0,0.2),0px 12px 17px 2px rgba(0,0,0,0.14),0px 5px 22px 4px rgba(0,0,0,0.12)", + "0px 7px 8px -4px rgba(0,0,0,0.2),0px 13px 19px 2px rgba(0,0,0,0.14),0px 5px 24px 4px rgba(0,0,0,0.12)", + "0px 7px 9px -4px rgba(0,0,0,0.2),0px 14px 21px 2px rgba(0,0,0,0.14),0px 5px 26px 4px rgba(0,0,0,0.12)", + "0px 8px 9px -5px rgba(0,0,0,0.2),0px 15px 22px 2px rgba(0,0,0,0.14),0px 6px 28px 5px rgba(0,0,0,0.12)", + "0px 8px 10px -5px rgba(0,0,0,0.2),0px 16px 24px 2px rgba(0,0,0,0.14),0px 6px 30px 5px rgba(0,0,0,0.12)", + "0px 8px 11px -5px rgba(0,0,0,0.2),0px 17px 26px 2px rgba(0,0,0,0.14),0px 6px 32px 5px rgba(0,0,0,0.12)", + "0px 9px 11px -5px rgba(0,0,0,0.2),0px 18px 28px 2px rgba(0,0,0,0.14),0px 7px 34px 6px rgba(0,0,0,0.12)", + "0px 9px 12px -6px rgba(0,0,0,0.2),0px 19px 29px 2px rgba(0,0,0,0.14),0px 7px 36px 6px rgba(0,0,0,0.12)", + "0px 10px 13px -6px rgba(0,0,0,0.2),0px 20px 31px 3px rgba(0,0,0,0.14),0px 8px 38px 7px rgba(0,0,0,0.12)", + "0px 10px 13px -6px rgba(0,0,0,0.2),0px 21px 33px 3px rgba(0,0,0,0.14),0px 8px 40px 7px rgba(0,0,0,0.12)", + "0px 10px 14px -6px rgba(0,0,0,0.2),0px 22px 35px 3px rgba(0,0,0,0.14),0px 8px 42px 7px rgba(0,0,0,0.12)", + "0px 11px 14px -7px rgba(0,0,0,0.2),0px 23px 36px 3px rgba(0,0,0,0.14),0px 9px 44px 8px rgba(0,0,0,0.12)", + "0px 11px 15px -7px rgba(0,0,0,0.2),0px 24px 38px 3px rgba(0,0,0,0.14),0px 9px 46px 8px rgba(0,0,0,0.12)" + ], + "typography":{ + "htmlFontSize":16, + "fontFamily":"\\""Roboto\\"", \\""Helvetica\\"", \\""Arial\\"", sans-serif", + "fontSize":14, + "fontWeightLight":300, + "fontWeightRegular":400, + "fontWeightMedium":500, + "fontWeightBold":700, + "h1":{ + "fontFamily":"\\""Roboto\\"", \\""Helvetica\\"", \\""Arial\\"", sans-serif", + "fontWeight":300, + "fontSize":"3rem", + "lineHeight":1.167, + "letterSpacing":"-0.01562em" + }, + "h2":{ + "fontFamily":"\\""Roboto\\"", \\""Helvetica\\"", \\""Arial\\"", sans-serif", + "fontWeight":300, + "fontSize":"2rem", + "lineHeight":1.2, + "letterSpacing":"-0.00833em" + }, + "h3":{ + "fontFamily":"\\""Roboto\\"", \\""Helvetica\\"", \\""Arial\\"", sans-serif", + "fontWeight":400, + "fontSize":"1.64rem", + "lineHeight":1.167, + "letterSpacing":"0em" + }, + "h4":{ + "fontFamily":"\\""Roboto\\"", \\""Helvetica\\"", \\""Arial\\"", sans-serif", + "fontWeight":400, + "fontSize":"1.5rem", + "lineHeight":1.235, + "letterSpacing":"0.00735em" + }, + "h5":{ + "fontFamily":"\\""Roboto\\"", \\""Helvetica\\"", \\""Arial\\"", sans-serif", + "fontWeight":400, + "fontSize":"1.285rem", + "lineHeight":1.334, + "letterSpacing":"0em" + }, + "h6":{ + "fontFamily":"\\""Roboto\\"", \\""Helvetica\\"", \\""Arial\\"", sans-serif", + "fontWeight":500, + "fontSize":"1.142rem", + "lineHeight":1.6, + "letterSpacing":"0.0075em" + }, + "subtitle1":{ + "fontFamily":"\\""Roboto\\"", \\""Helvetica\\"", \\""Arial\\"", sans-serif", + "fontWeight":400, + "fontSize":"1rem", + "lineHeight":1.75, + "letterSpacing":"0.00938em" + }, + "subtitle2":{ + "fontFamily":"\\""Roboto\\"", \\""Helvetica\\"", \\""Arial\\"", sans-serif", + "fontWeight":500, + "fontSize":"0.875rem", + "lineHeight":1.57, + "letterSpacing":"0.00714em" + }, + "body1":{ + "fontFamily":"\\""Roboto\\"", \\""Helvetica\\"", \\""Arial\\"", sans-serif", + "fontWeight":400, + "fontSize":"1rem", + "lineHeight":1.5, + "letterSpacing":"0.00938em" + }, + "body2":{ + "fontFamily":"\\""Roboto\\"", \\""Helvetica\\"", \\""Arial\\"", sans-serif", + "fontWeight":400, + "fontSize":"0.875rem", + "lineHeight":1.43, + "letterSpacing":"0.01071em" + }, + "button":{ + "fontFamily":"\\""Roboto\\"", \\""Helvetica\\"", \\""Arial\\"", sans-serif", + "fontWeight":500, + "fontSize":"0.875rem", + "lineHeight":1.75, + "letterSpacing":"0.02857em", + "textTransform":"uppercase" + }, + "caption":{ + "fontFamily":"\\""Roboto\\"", \\""Helvetica\\"", \\""Arial\\"", sans-serif", + "fontWeight":400, + "fontSize":"0.75rem", + "lineHeight":1.66, + "letterSpacing":"0.03333em" + }, + "overline":{ + "fontFamily":"\\""Roboto\\"", \\""Helvetica\\"", \\""Arial\\"", sans-serif", + "fontWeight":400, + "fontSize":"0.75rem", + "lineHeight":2.66, + "letterSpacing":"0.08333em", + "textTransform":"uppercase" + } + }, + "shape":{ + "borderRadius":4 + }, + "transitions":{ + "easing":{ + "easeInOut":"cubic-bezier(0.4, 0, 0.2, 1)", + "easeOut":"cubic-bezier(0.0, 0, 0.2, 1)", + "easeIn":"cubic-bezier(0.4, 0, 1, 1)", + "sharp":"cubic-bezier(0.4, 0, 0.6, 1)" + }, + "duration":{ + "shortest":150, + "shorter":200, + "short":250, + "standard":300, + "complex":375, + "enteringScreen":225, + "leavingScreen":195 + } + }, + "zIndex":{ + "mobileStepper":1000, + "speedDial":1050, + "appBar":1100, + "drawer":1200, + "modal":1300, + "snackbar":1400, + "tooltip":1500 + }, + "customShadows":{ + "widget":"0px 3px 11px 0px #E8EAFC, 0 3px 3px -2px #B2B2B21A, 0 1px 8px 0 #9A9A9A1A", + "widgetDark":"0px 3px 18px 0px #4558A3B3, 0 3px 3px -2px #B2B2B21A, 0 1px 8px 0 #9A9A9A1A", + "widgetWide":"0px 12px 33px 0px #E8EAFC, 0 3px 3px -2px #B2B2B21A, 0 1px 8px 0 #9A9A9A1A" + } +} \ No newline at end of file diff --git a/src/components/App.js b/src/components/App.js index b7dc386..080dc28 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -7,6 +7,7 @@ import Layout from "./Layout/Layout"; // pages import Error from "../pages/error"; import Login from "../pages/login"; +import ServerNotAvailable from "../features/server/availability/components/ServerNotAvailable"; // context import { useUserState } from "../contexts/UserContext"; @@ -26,7 +27,10 @@ export default function App() { return ( - } /> + diff --git a/src/components/Layout/Layout.js b/src/components/Layout/Layout.js index 255348d..1290d02 100644 --- a/src/components/Layout/Layout.js +++ b/src/components/Layout/Layout.js @@ -1,5 +1,6 @@ import React from "react"; import { withRouter } from "react-router-dom"; +import ServerAvailabilityProvider from "../../features/server/providers/ServerAvailabilityProvider"; // components import Header from "../Header/Header"; @@ -13,13 +14,13 @@ function Layout(props) { var classes = useStyles(); return ( -
- <> + +
- -
+
+ ); } diff --git a/src/features/server/api/index.js b/src/features/server/api/index.js new file mode 100644 index 0000000..4c10415 --- /dev/null +++ b/src/features/server/api/index.js @@ -0,0 +1,3 @@ +import useServerApi from "./useServerApi"; + +export { useServerApi }; diff --git a/src/features/server/api/useServerApi.js b/src/features/server/api/useServerApi.js new file mode 100644 index 0000000..66e11a7 --- /dev/null +++ b/src/features/server/api/useServerApi.js @@ -0,0 +1,26 @@ +import { useCallback } from "react"; +import useHttpRequest from "../../../hooks/useHttpRequest"; +import { get } from "../../../utils/axios"; + +const cdn = process.env.REACT_APP_CDN_URL; +const endpoints = { + ping: `${cdn}/health/ping` +}; + +const useServerApi = () => { + const { exec } = useHttpRequest(); + + const checkHealth = useCallback( + (options) => { + const promise = exec(() => get(endpoints.ping), options); + return promise; + }, + [exec] + ); + + return { + checkHealth + }; +}; + +export default useServerApi; diff --git a/src/features/server/availability/components/ServerNotAvailable.js b/src/features/server/availability/components/ServerNotAvailable.js new file mode 100644 index 0000000..375bfdd --- /dev/null +++ b/src/features/server/availability/components/ServerNotAvailable.js @@ -0,0 +1,50 @@ +import React from "react"; +import { Grid, Paper, Typography, Button } from "@material-ui/core"; +import { Link } from "react-router-dom"; +import useStyles from "../styles"; +import classnames from "classnames"; +import ServerIsDown from "../../../../images/ServerIsDown.gif"; + +const ServerNotAvailable = () => { + const classes = useStyles(); + + return ( + + +
+ server is down +
+ + {"Oops. Looks like the server is currently unavailable."} + + + {"Try again in a few seconds"} + + +
+
+ ); +}; + +export default ServerNotAvailable; diff --git a/src/features/server/availability/styles.js b/src/features/server/availability/styles.js new file mode 100644 index 0000000..881f3a9 --- /dev/null +++ b/src/features/server/availability/styles.js @@ -0,0 +1,49 @@ +import { makeStyles } from "@material-ui/styles"; + +export default makeStyles((theme) => ({ + container: { + height: "100vh", + width: "100vw", + display: "flex", + flexDirection: "column", + justifyContent: "center", + alignItems: "center", + backgroundColor: theme.palette.error.dark, + position: "absolute", + top: 0, + left: 0 + }, + imageFrame: { + display: "flex", + alignItems: "center", + marginBottom: theme.spacing(2) + }, + image: { + width: 200, + marginRight: theme.spacing(2) + }, + paperRoot: { + backgroundColor: theme.palette.common.black, + display: "flex", + flexDirection: "column", + alignItems: "center", + paddingTop: theme.spacing(2), + paddingBottom: theme.spacing(2), + paddingLeft: theme.spacing(2), + paddingRight: theme.spacing(2), + maxWidth: 404 + }, + textRow: { + marginBottom: theme.spacing(4), + textAlign: "center" + }, + safetyText: { + fontWeight: 300, + color: theme.palette.text.hint + }, + button: { + backgroundColor: theme.palette.error.dark, + textTransform: "none", + fontSize: 22 + } +})); diff --git a/src/features/server/providers/ServerAvailabilityProvider.js b/src/features/server/providers/ServerAvailabilityProvider.js new file mode 100644 index 0000000..849fd30 --- /dev/null +++ b/src/features/server/providers/ServerAvailabilityProvider.js @@ -0,0 +1,26 @@ +import React, { useState, useEffect } from "react"; +import PropTypes from "prop-types"; +import { Redirect } from "react-router-dom"; +import { useServerApi } from "../api"; + +const ServerAvailabilityProvider = ({ children }) => { + const [ok, setOk] = useState(null); + const { checkHealth } = useServerApi(); + + useEffect(() => { + checkHealth({ + onCompleted: () => setOk(true), + onError: () => setOk(false) + }); + }, [checkHealth]); + + if (ok === null) return Checking server health...; + if (ok === false) return ; + return <>{children}; +}; + +ServerAvailabilityProvider.propTypes = { + children: PropTypes.node +}; + +export default ServerAvailabilityProvider; diff --git a/src/images/ServerIsDown.gif b/src/images/ServerIsDown.gif new file mode 100644 index 0000000..f79b773 Binary files /dev/null and b/src/images/ServerIsDown.gif differ