├── .env.example ├── .eslintrc ├── .github └── workflows │ ├── pr-tests.yml │ └── production-deploy.yml ├── .gitignore ├── README.md ├── craco.config.js ├── package.json ├── public ├── _redirects ├── index.html ├── manifest.json ├── robots.txt └── site.webmanifest ├── src ├── App.tsx ├── Components │ ├── Elements │ │ ├── content │ │ │ └── TableOfContents.tsx │ │ └── icons │ │ │ ├── Discord.icon.tsx │ │ │ ├── Github.icon.tsx │ │ │ └── Mail.icon.tsx │ ├── Layouts │ │ ├── AppHeader.tsx │ │ ├── AppNav.tsx │ │ └── AppWrapper.tsx │ ├── Pages │ │ ├── DashboardPage.tsx │ │ └── DataMetricsPage.tsx │ └── Routes.tsx ├── Constants │ ├── EthDashboardUrls.tsx │ └── UsdDashboardUrls.tsx ├── Contexts │ └── DashboardContext │ │ ├── DashboardContext.tsx │ │ └── Types.tsx ├── Media │ ├── Discord-Logo-Black.svg │ ├── Medium_Symbol_NoPadding.svg │ ├── Robot_Emoji_OG.png │ ├── github.svg │ └── mail.svg ├── Themes │ └── LightTheme.ts ├── Utils │ └── Helpers.tsx ├── index.css ├── index.tsx ├── react-app-env.d.ts ├── serviceWorker.ts └── setupTests.ts ├── tsconfig.json └── yarn.lock /.env.example: -------------------------------------------------------------------------------- 1 | PORT=3000 2 | HTTPS=false 3 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "parserOptions": { 4 | "ecmaVersion": 2020, 5 | "sourceType": "module", 6 | "ecmaFeatures": { 7 | "jsx": true 8 | } 9 | }, 10 | "settings": { 11 | "react": { 12 | "version": "detect" 13 | } 14 | }, 15 | "extends": [ 16 | "plugin:react/recommended", 17 | "plugin:@typescript-eslint/recommended", 18 | "prettier/@typescript-eslint", 19 | "plugin:prettier/recommended" 20 | ], 21 | "rules": {} 22 | } 23 | -------------------------------------------------------------------------------- /.github/workflows/pr-tests.yml: -------------------------------------------------------------------------------- 1 | name: PR Tests 2 | 3 | on: 4 | pull_request: 5 | branches: "main" 6 | 7 | jobs: 8 | build: 9 | if: github.event_name != 'push' 10 | runs-on: ubuntu-latest 11 | environment: 12 | name: production 13 | url: docs.flashbots.net 14 | steps: 15 | - uses: actions/checkout@v1 16 | - uses: actions/setup-node@v1 17 | with: 18 | node-version: '12.x' 19 | - name: Test Build 20 | env: 21 | TARGET_URL: ${{ secrets.TARGET_URL }} 22 | BASE_URL: ${{ secrets.BASE_URL }} 23 | run: | 24 | if [ -e yarn.lock ]; then 25 | yarn install --frozen-lockfile 26 | elif [ -e package-lock.json ]; then 27 | npm ci 28 | else 29 | npm i 30 | fi 31 | npm run build 32 | -------------------------------------------------------------------------------- /.github/workflows/production-deploy.yml: -------------------------------------------------------------------------------- 1 | name: Production Deploy 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v2 13 | - name: Configure AWS credentials 14 | uses: aws-actions/configure-aws-credentials@v1 15 | with: 16 | aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} 17 | aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 18 | aws-region: us-east-1 19 | - name: Use Node.js 20 | uses: actions/setup-node@v1 21 | with: 22 | node-version: 12.x 23 | - name: Install Packages 24 | run: npm install 25 | - name: Build page 26 | run: npm run deploy 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | .env 26 | 27 | src/locales/*/messages.js 28 | src/locales/_build 29 | 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flashbots Dashboard 2 | 3 | ## Prerequisites 4 | 5 | - [Yarn](https://yarnpkg.com/) 6 | 7 | ## Getting started 8 | 9 | First run `yarn` 10 | 11 | Next copy the `.env.example` and rename it to `.env` 12 | 13 | And run `yarn start` to get the app running locally 14 | 15 | To build for release, run `yarn build` you will find the compiled files in the `./build` folder. -------------------------------------------------------------------------------- /craco.config.js: -------------------------------------------------------------------------------- 1 | const TerserPlugin = require("terser-webpack-plugin") 2 | 3 | module.exports = { 4 | babel: { 5 | presets: [], 6 | plugins: ["macros"], 7 | loaderOptions: (babelLoaderOptions, { env, paths }) => { 8 | return babelLoaderOptions 9 | }, 10 | }, 11 | webpack: { 12 | configure: (webpackConfig) => ({ 13 | ...webpackConfig, 14 | optimization: { 15 | ...webpackConfig.optimization, 16 | minimizer: [ 17 | new TerserPlugin({ 18 | terserOptions: { 19 | parse: { 20 | ecma: 8, 21 | }, 22 | compress: { 23 | ecma: 5, 24 | warnings: false, 25 | comparisons: false, 26 | inline: 2, 27 | drop_console: true, 28 | }, 29 | mangle: { 30 | safari10: true, 31 | }, 32 | output: { 33 | ecma: 5, 34 | comments: false, 35 | ascii_only: true, 36 | }, 37 | }, 38 | parallel: 2, 39 | cache: true, 40 | sourceMap: true, 41 | extractComments: false, 42 | }), 43 | ], 44 | }, 45 | devtool: "source-map", 46 | }), 47 | }, 48 | } 49 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flashbots-dashboard", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "author": "RyRy79261 ", 6 | "repository": "https://github.com/flashbots/flashbots-dashboard.git", 7 | "license": "MIT", 8 | "dependencies": { 9 | "@babel/core": "^7.12.3", 10 | "@babel/runtime": "^7.12.1", 11 | "@chainsafe/common-components": "^1.0.29", 12 | "@chainsafe/common-theme": "^1.0.9", 13 | "@material-ui/styles": "4.10.0", 14 | "@types/history": "^4.7.8", 15 | "@types/react-router-dom": "^5.1.7", 16 | "babel-loader": "8.1.0", 17 | "babel-plugin-macros": "^2.8.0", 18 | "babel-preset-env": "^1.7.0", 19 | "babel-preset-react": "^6.24.1", 20 | "clsx": "^1.1.1", 21 | "formik": "^2.2.6", 22 | "react": "^16.14.0", 23 | "react-beforeunload": "^2.4.0", 24 | "react-dom": "^16.14.0", 25 | "react-iframe": "^1.8.0", 26 | "react-markdown": "^5.0.3", 27 | "react-scripts": "3.4.4", 28 | "react-toast-notifications": "^2.4.0", 29 | "remark-gfm": "^1.0.0" 30 | }, 31 | "devDependencies": { 32 | "@testing-library/jest-dom": "^5.11.4", 33 | "@testing-library/react": "^11.0.4", 34 | "@testing-library/user-event": "^12.1.7", 35 | "@types/jest": "^26.0.14", 36 | "@types/node": "^14.11.8", 37 | "@types/react": "^16.9.52", 38 | "@types/react-dom": "^16.9.0", 39 | "@types/yup": "^0.29.9", 40 | "chalk": "^4.1.0", 41 | "craco": "^0.0.3", 42 | "husky": "4.3.0", 43 | "prettier": "2.1.2", 44 | "pretty-quick": "3.0.2", 45 | "typescript": "^4.0.3" 46 | }, 47 | "scripts": { 48 | "start": "craco --max_old_space_size=4096 start", 49 | "build": "craco --max_old_space_size=4096 build", 50 | "test": "react-scripts test", 51 | "deploy": "yarn run build && aws s3 sync ./build s3://dashboard.flashbots.net", 52 | "analyze": "source-map-explorer 'build/static/js/*.js'" 53 | }, 54 | "eslintConfig": { 55 | "extends": "react-app" 56 | }, 57 | "browserslist": { 58 | "production": [ 59 | ">0.2%", 60 | "not dead", 61 | "not op_mini all" 62 | ], 63 | "development": [ 64 | "last 1 chrome version", 65 | "last 1 firefox version", 66 | "last 1 safari version" 67 | ] 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /public/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | 24 | Flashbots Dashboards 25 | 26 | 27 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Flashbots Dashboards", 3 | "name": "Flashbots Dashboards", 4 | "icons": [ 5 | ], 6 | "start_url": ".", 7 | "display": "standalone", 8 | "theme_color": "#000000", 9 | "background_color": "#ffffff" 10 | } 11 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "short_name": "", 4 | "icons": [ 5 | ], 6 | "theme_color": "#ffffff", 7 | "background_color": "#ffffff", 8 | "display": "standalone" 9 | } 10 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { ThemeSwitcher } from "@chainsafe/common-theme" 3 | import { 4 | Router, 5 | } from "@chainsafe/common-components" 6 | import Routes from "./Components/Routes" 7 | import AppWrapper from "./Components/Layouts/AppWrapper" 8 | import { lightTheme } from "./Themes/LightTheme" 9 | import { DashboardProvider } from "./Contexts/DashboardContext/DashboardContext" 10 | 11 | const App: React.FC<{}> = () => { 12 | return ( 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | ) 23 | } 24 | 25 | export default App 26 | -------------------------------------------------------------------------------- /src/Components/Elements/content/TableOfContents.tsx: -------------------------------------------------------------------------------- 1 | import { Link, Typography } from "@chainsafe/common-components" 2 | import { createStyles, ITheme, makeStyles } from "@chainsafe/common-theme" 3 | import clsx from "clsx" 4 | import React, { ReactNode } from "react" 5 | 6 | const useStyles = makeStyles( 7 | ({ constants, typography, palette }: ITheme) => { 8 | return createStyles({ 9 | root: { 10 | padding: constants.generalUnit * 4, 11 | borderRadius: constants.generalUnit * 2, 12 | boxShadow: "0px 4px 4px rgba(0, 0, 0, 0.25)", 13 | border: `1px solid ${palette.additional.borderGray}` 14 | }, 15 | title: { 16 | marginTop: 0, 17 | marginBottom: constants.generalUnit 18 | }, 19 | itemArea: { 20 | 21 | }, 22 | item: { 23 | display: "block", 24 | textDecoration: "none", 25 | color: palette.common.black.main, 26 | "& a": { 27 | color: palette.common.black.main, 28 | textDecoration: "none", 29 | display: "flex" 30 | }, 31 | "& svg, & img": { 32 | maxHeight: 18, 33 | maxWidth: 18, 34 | marginRight: constants.generalUnit 35 | }, 36 | "&:hover": { 37 | fontWeight: typography.fontWeight.bold 38 | } 39 | } 40 | }) 41 | }, 42 | ) 43 | 44 | export interface IContentItem { 45 | content: ReactNode | string, 46 | url: string, 47 | type: "hash" | "internal" | "external" 48 | } 49 | 50 | export interface ITableOfContents { 51 | title?: string 52 | items: IContentItem[] 53 | className?: string 54 | } 55 | 56 | const TableOfContents = ({ 57 | title = "Table of Contents", 58 | items, 59 | className 60 | }: ITableOfContents) => { 61 | const classes = useStyles() 62 | 63 | return
64 | 65 | { title } 66 | 67 |
68 | { 69 | items.map((item: IContentItem, index: number) => { 70 | switch(item.type) { 71 | case "hash": 72 | return ( 73 | 74 | 75 | { 76 | item.content 77 | } 78 | 79 | 80 | 81 | ) 82 | case "internal": 83 | return ( 84 | 85 | 86 | { 87 | item.content 88 | } 89 | 90 | 91 | ) 92 | case "external": 93 | return ( 94 | 95 | 96 | { 97 | item.content 98 | } 99 | 100 | 101 | ) 102 | default: 103 | return ( 104 | 105 | 106 | { 107 | item.content 108 | } 109 | 110 | 111 | ) 112 | } 113 | 114 | }) 115 | } 116 |
117 |
118 | } 119 | 120 | export default TableOfContents 121 | -------------------------------------------------------------------------------- /src/Components/Elements/icons/Discord.icon.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { ReactComponent as DiscordIconSvg } from "../../../Media/Discord-Logo-Black.svg" 3 | 4 | const DiscordIcon = () => { 5 | return 6 | } 7 | 8 | export default DiscordIcon 9 | -------------------------------------------------------------------------------- /src/Components/Elements/icons/Github.icon.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { ReactComponent as GithubIconSvg } from "../../../Media/github.svg" 3 | 4 | const GithubIcon = () => { 5 | return 6 | } 7 | 8 | export default GithubIcon 9 | -------------------------------------------------------------------------------- /src/Components/Elements/icons/Mail.icon.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | import { ReactComponent as MailIconSvg } from "../../../Media/mail.svg" 3 | 4 | const MailIcon = () => { 5 | return 6 | } 7 | 8 | export default MailIcon 9 | -------------------------------------------------------------------------------- /src/Components/Layouts/AppHeader.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | createStyles, 3 | ITheme, 4 | makeStyles, 5 | useMediaQuery, 6 | useTheme, 7 | } from "@chainsafe/common-theme" 8 | import React, { useState } from "react" 9 | import clsx from "clsx" 10 | import { Button, HamburgerMenu, Link, NavLink, Typography, useLocation } from "@chainsafe/common-components" 11 | import AppNav from "./AppNav" 12 | import { CURRENCY_TOGGLE_HIDDEN, ROUTE_LINKS } from "../Routes" 13 | import LogoPng from "../../Media/Robot_Emoji_OG.png" 14 | import { Currency, useDashboard } from "../../Contexts/DashboardContext/DashboardContext" 15 | import { Dashboard } from "../../Contexts/DashboardContext/Types" 16 | 17 | const useStyles = makeStyles( 18 | ({ constants, breakpoints, palette, typography, zIndex }: ITheme) => { 19 | return createStyles({ 20 | root: { 21 | position: "fixed", 22 | top: 0, 23 | left: 0, 24 | width: "100%", 25 | display: "flex", 26 | flexDirection: "row", 27 | alignItems: "center", 28 | height: `${constants.headerHeight}px`, 29 | padding: `0 ${constants.generalUnit * 3}px`, 30 | backgroundColor: palette.additional.black as string, 31 | zIndex: zIndex?.blocker, 32 | [breakpoints.down("md")]: { 33 | justifyContent: "flex-start", 34 | padding: `0 ${constants.generalUnit * 2}px`, 35 | }, 36 | [breakpoints.up("md")]: { 37 | justifyContent: "space-around", 38 | }, 39 | }, 40 | logo: { 41 | display: "block", 42 | position: "relative", 43 | height: 46, 44 | width: constants.logoSize as number, 45 | lineHeight: "46px", 46 | "& img": { 47 | width: constants.logoSize as number, 48 | height: constants.logoSize as number, 49 | display: "block", 50 | position: "absolute", 51 | top: "50%", 52 | left: "50%", 53 | transform: "translate(-50%, calc(-50% - 2px))" 54 | }, 55 | }, 56 | title: { 57 | cursor: "pointer", 58 | color: palette.additional.white as string, 59 | textDecoration: "none", 60 | display: "flex", 61 | alignItems: "center", 62 | "& span": { 63 | borderBottom: "none", 64 | fontWeight: typography.fontWeight.regular, 65 | margin: 0, 66 | fontSize: 32, 67 | padding: 0, 68 | display: "inline-block", 69 | verticalAlign: "center" 70 | }, 71 | 72 | "& sub": { 73 | fontSize: 24, 74 | }, 75 | "& a":{ 76 | color: palette.additional.white as string, 77 | textDecoration: "none", 78 | "&:first-child": { 79 | // ICON 80 | marginRight: constants.generalUnit / 2 81 | } 82 | } 83 | }, 84 | menuButton: { 85 | position: "absolute", 86 | transform: "translate(0, -50%) rotateY(180deg)", 87 | right: constants.generalUnit * 2, 88 | top: "50%", 89 | "& span": { 90 | backgroundColor: palette.additional.white as string 91 | } 92 | }, 93 | desktopNav: { 94 | display: "flex", 95 | flexDirection: "row", 96 | justifyContent: "flex-end", 97 | flex: "1 1 0", 98 | height: "100%", 99 | alignItems: "center", 100 | "& > *": { 101 | padding: `0 ${constants.generalUnit * 5}px` 102 | } 103 | }, 104 | link: { 105 | fontSize: 24, 106 | color: palette.additional.white as string, 107 | textDecoration: "none", 108 | fontWeight: typography.fontWeight.regular, 109 | display: "flex", 110 | flexDirection: "column", 111 | justifyContent: "center", 112 | height: "100%", 113 | "&.active": { 114 | fontWeight: typography.fontWeight.bold 115 | } 116 | }, 117 | currencySelector: { 118 | fontSize: 24, 119 | textDecoration: "none", 120 | textTransform: "uppercase", 121 | fontWeight: typography.fontWeight.regular, 122 | display: "flex", 123 | flexDirection: "row", 124 | justifyContent: "center", 125 | alignItems: "center", 126 | cursor: "pointer", 127 | "& svg": { 128 | width: 20, 129 | height: 20, 130 | marginRight: constants.generalUnit 131 | }, 132 | } 133 | }) 134 | }, 135 | ) 136 | 137 | const AppHeader = () => { 138 | const classes = useStyles() 139 | 140 | const [navOpen, setNavOpen] = useState(false) 141 | 142 | const { breakpoints }: ITheme = useTheme() 143 | const desktop = useMediaQuery(breakpoints.up("md")) 144 | const { endpoints } = useDashboard() 145 | const { currency, changeCurrency } = useDashboard() 146 | const { pathname } = useLocation() 147 | 148 | return ( 149 |
152 |
153 | 154 | robot emoji 155 | 156 | 157 | 158 | Flashbots Dashboard v0 159 | 160 | 161 |
162 | { 163 | desktop && (
164 | { 165 | endpoints.map((dashboard: Dashboard, index: number) => ( 166 | {dashboard.name} 167 | )) 168 | } 169 | { 170 | Object.keys(ROUTE_LINKS).filter((key: string) => typeof (ROUTE_LINKS as {[index: string]: any})[key] == "string").map((routeName: string, index: number) => ( 171 | {routeName} 172 | )) 173 | } 174 | { 175 | !CURRENCY_TOGGLE_HIDDEN.includes(pathname) && ( 176 | 186 | ) 187 | } 188 |
) 189 | } 190 | { 191 | !desktop && <> 192 |
195 | setNavOpen(!navOpen)} 197 | variant={navOpen ? "active" : "default"} 198 | /> 199 |
200 | setNavOpen(false)} active={navOpen} /> 201 | 202 | } 203 |
204 | ) 205 | } 206 | 207 | export default AppHeader 208 | -------------------------------------------------------------------------------- /src/Components/Layouts/AppNav.tsx: -------------------------------------------------------------------------------- 1 | import { CloseCirceSvg, NavLink, Typography, useLocation } from "@chainsafe/common-components" 2 | import { createStyles, ITheme, makeStyles, useOnClickOutside } from "@chainsafe/common-theme" 3 | import clsx from "clsx" 4 | import React, { useRef } from "react" 5 | import { Currency, useDashboard } from "../../Contexts/DashboardContext/DashboardContext" 6 | import { Dashboard } from "../../Contexts/DashboardContext/Types" 7 | import { CURRENCY_TOGGLE_HIDDEN, ROUTE_LINKS } from "../Routes" 8 | 9 | const useStyles = makeStyles( 10 | ({ constants, breakpoints, zIndex, animation, palette }: ITheme) => { 11 | return createStyles({ 12 | root: { 13 | position: "fixed", 14 | zIndex: zIndex?.blocker, 15 | top: 0, 16 | right: 0, 17 | height: "100%", 18 | transitionDuration: `${animation.translate}ms`, 19 | width: 0, 20 | opacity: 0, 21 | visibility: "hidden", 22 | "&.active": { 23 | opacity: 1, 24 | visibility: "visible", 25 | [breakpoints.up("xs")]: { 26 | width: "100%" 27 | }, 28 | [breakpoints.up("sm")]: { 29 | width: "80vw" 30 | }, 31 | [breakpoints.up("md")]: { 32 | width: "60vw" 33 | }, 34 | [breakpoints.up("lg")]: { 35 | width: 450 36 | }, 37 | } 38 | }, 39 | nav: { 40 | backgroundColor: palette.common.white.main, 41 | height: "100%", 42 | position: "absolute", 43 | top: 0, 44 | left: 0, 45 | display: "flex", 46 | flexDirection: "column", 47 | justifyContent: "center", 48 | alignItems: "center", 49 | [breakpoints.up("xs")]: { 50 | width: "100%" 51 | }, 52 | [breakpoints.up("sm")]: { 53 | width: "80vw" 54 | }, 55 | [breakpoints.up("md")]: { 56 | width: "60vw" 57 | }, 58 | [breakpoints.up("lg")]: { 59 | width: 450 60 | }, 61 | }, 62 | link: { 63 | padding: `2rem 0`, 64 | textDecoration: "none", 65 | textAlign: "center", 66 | display: "block", 67 | fontSize: "3rem", 68 | transitionDuration: `${animation.transform}ms`, 69 | "&:hover": { 70 | textDecoration: "underline", 71 | } 72 | }, 73 | close: { 74 | position: "absolute", 75 | top: constants.generalUnit * 3, 76 | right: constants.generalUnit * 3, 77 | cursor: "pointer", 78 | "& svg": { 79 | width: 50, 80 | height: 50 81 | } 82 | }, 83 | currencySelector: { 84 | fontSize: "1.5rem", 85 | display: "inline-flex", 86 | cursor: "pointer", 87 | flexDirection: "column", 88 | alignItems: "center", 89 | position: "absolute", 90 | bottom: constants.generalUnit * 2, 91 | "& svg": { 92 | width: constants.generalUnit * 3, 93 | height: constants.generalUnit * 3, 94 | marginBottom: constants.generalUnit * 2 95 | }, 96 | "& span span": { 97 | textTransform: "uppercase" 98 | } 99 | } 100 | }) 101 | }, 102 | ) 103 | 104 | interface IAppNav { 105 | active: boolean 106 | close: () => void 107 | } 108 | 109 | const AppNav = ({ 110 | active, 111 | close 112 | }: IAppNav) => { 113 | const classes = useStyles() 114 | const ref = useRef(null) 115 | useOnClickOutside(ref, () => close()) 116 | const { endpoints } = useDashboard() 117 | 118 | const { currency, changeCurrency } = useDashboard() 119 | const { pathname } = useLocation() 120 | 121 | return
126 | 157 |
158 | } 159 | 160 | export default AppNav 161 | -------------------------------------------------------------------------------- /src/Components/Layouts/AppWrapper.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | createStyles, 3 | ITheme, 4 | makeStyles, 5 | } from "@chainsafe/common-theme" 6 | import React from "react" 7 | import { ReactNode } from "react" 8 | import clsx from "clsx" 9 | import { CssBaseline } from "@chainsafe/common-components" 10 | import AppHeader from "./AppHeader" 11 | 12 | interface IAppWrapper { 13 | children: ReactNode | ReactNode[] 14 | } 15 | 16 | const useStyles = makeStyles( 17 | ({constants}: ITheme) => { 18 | return createStyles({ 19 | root: { 20 | }, 21 | bodyWrapper: { 22 | }, 23 | content: { 24 | marginTop: `${constants.headerHeight}px` 25 | }, 26 | }) 27 | }, 28 | ) 29 | 30 | const AppWrapper: React.FC = ({ children }: IAppWrapper) => { 31 | const classes = useStyles() 32 | 33 | return ( 34 |
35 | 36 |
40 | 41 |
45 | {children} 46 |
47 |
48 |
49 | ) 50 | } 51 | 52 | export default AppWrapper 53 | -------------------------------------------------------------------------------- /src/Components/Pages/DashboardPage.tsx: -------------------------------------------------------------------------------- 1 | import { useParams } from "@chainsafe/common-components" 2 | import { createStyles, ITheme, makeStyles } from "@chainsafe/common-theme" 3 | import React, { useEffect, useState } from "react" 4 | import Iframe from "react-iframe" 5 | import { useDashboard } from "../../Contexts/DashboardContext/DashboardContext" 6 | import { Dashboard } from "../../Contexts/DashboardContext/Types" 7 | 8 | const useStyles = makeStyles( 9 | ({ constants, breakpoints }: ITheme) => { 10 | return createStyles({ 11 | root: { 12 | minHeight: `calc(100vh - ${constants.headerHeight}px)`, 13 | padding: `${constants.generalUnit}px ${constants.generalUnit * 2}px ${constants.generalUnit * 3}px ${constants.generalUnit * 2}px`, 14 | }, 15 | dashArea: { 16 | minHeight: `calc(100vh - ${constants.headerHeight}px)`, 17 | display: "grid", 18 | gridTemplateColumns: "1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr", // 12, 19 | gridAutoRows: "auto", 20 | columnGap: `${constants.generalUnit}px`, 21 | rowGap: `${constants.generalUnit}px`, 22 | [breakpoints.up("sm")]: { 23 | gridAutoRows: "minmax(20vh, auto)", 24 | } 25 | }, 26 | widget: { 27 | 28 | }, 29 | tableArea: { 30 | marginTop: constants.generalUnit * 3 31 | }, 32 | tabs: { 33 | display: "flex", 34 | flexDirection: "row", 35 | justifyContent: "flex-start", 36 | flexWrap: "wrap", 37 | marginBottom: constants.generalUnit * 2, 38 | [breakpoints.down("sm")]: { 39 | justifyContent: "space-between", 40 | }, 41 | "& > *":{ 42 | margin: `0 ${constants.generalUnit}px` 43 | } 44 | }, 45 | table: { 46 | display: "none", 47 | visibility: "hidden", 48 | opacity: 0, 49 | minHeight: "300px", 50 | "&.active": { 51 | display: "block", 52 | visibility: "visible", 53 | opacity: 1, 54 | }, 55 | "& iframe": { 56 | minHeight: "300px", 57 | } 58 | }, 59 | fullIframe: { 60 | marginTop : `${constants.headerHeight}px`, 61 | height: `calc(100vh - ${constants.headerHeight}px)`, 62 | width: "100%", 63 | } 64 | }) 65 | }, 66 | ) 67 | 68 | const DashboardPage = () => { 69 | const classes = useStyles() 70 | 71 | const { endpoints } = useDashboard() 72 | 73 | const { dashboard } = useParams<{ dashboard: string}>() 74 | 75 | const [target, setTarget] = useState() 76 | 77 | useEffect(() => { 78 | if (endpoints && dashboard) { 79 | const temp = endpoints.find((item: Dashboard) => item.name.toLowerCase() === dashboard) 80 | if (temp && temp !== target) setTarget(temp) 81 | } 82 | }, [target, endpoints, dashboard]) 83 | 84 | console.log(target) 85 | 86 | return target ?