diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..4121b77 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,50 @@ +name: Build and Deploy + +on: + push: + branches: + - main + - staging + +jobs: + deploy_instance_1: + if: github.ref == 'refs/heads/staging' + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Deploy to Instance 1 + uses: appleboy/ssh-action@v0.1.8 + with: + host: ${{ secrets.STAGING_SERVER_HOST }} + username: ${{ secrets.STAGING_SERVER_USER }} + key: ${{ secrets.SERVER_KEY }} + script: | + cd /root/shutter-explorer + git pull origin staging + git submodule update --init --recursive + cd docker + docker compose up -d --build + + deploy_instance_2: + if: github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Deploy to Instance 2 + uses: appleboy/ssh-action@v0.1.8 + with: + host: ${{ secrets.PROD_SERVER_HOST }} + username: ${{ secrets.PROD_SERVER_USER }} + key: ${{ secrets.SERVER_KEY }} + script: | + cd /root/shutter-explorer + git pull origin main + git submodule update --init --recursive + cd docker + docker compose up -d --build \ No newline at end of file diff --git a/frontend/.env.development b/frontend/.env.development index 47f9e56..376cb1e 100644 --- a/frontend/.env.development +++ b/frontend/.env.development @@ -1,4 +1,4 @@ REACT_APP_EXPLORER_URL=https://gnosisscan.io/ # REACT_APP_BACKEND_API=http://167.172.189.30:5000 -REACT_APP_BACKEND_API=https://explorer.staging.shutter.network -REACT_APP_WS_URL=wss://explorer.staging.shutter.network/ws \ No newline at end of file +REACT_APP_BACKEND_API=https://api.explorer.staging.shutter.network +REACT_APP_WS_URL=wss://api.explorer.staging.shutter.network/ws \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 6c88182..b6c6307 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -12,8 +12,10 @@ "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", "@mui/icons-material": "^6.0.1", - "@mui/material": "^6.0.1", - "@mui/x-charts": "^7.15.0", + "@mui/material": "^6.1.2", + "@mui/system": "^6.1.2", + "@mui/x-charts": "^7.18.0", + "@mui/x-data-grid": "^7.18.0", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -3515,9 +3517,9 @@ "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==" }, "node_modules/@mui/core-downloads-tracker": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.0.2.tgz", - "integrity": "sha512-Cg68oOlAfbJgMgvbCwcX3Y3HdygCl6X1nREYTdEWcEKUQhNarrC45Cc35mP+zA7p3ZXE/7FLiaTCCgwuSoef/Q==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.1.2.tgz", + "integrity": "sha512-1oE4U38/TtzLWRYWEm/m70dUbpcvBx0QvDVg6NtpOmSNQC1Mbx0X/rNvYDdZnn8DIsAiVQ+SZ3am6doSswUQ4g==", "funding": { "type": "opencollective", "url": "https://opencollective.com/mui-org" @@ -3549,15 +3551,15 @@ } }, "node_modules/@mui/material": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.0.2.tgz", - "integrity": "sha512-KrnkJFSyhsAh8V30DNUbWyRyxMi4ZHjFg1ikQGx+mUAIffFTYIEx9Q+Kxd3vCT0FUFGOmbsuh6F6yRhpybsjkg==", - "dependencies": { - "@babel/runtime": "^7.25.0", - "@mui/core-downloads-tracker": "^6.0.2", - "@mui/system": "^6.0.2", - "@mui/types": "^7.2.16", - "@mui/utils": "^6.0.2", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.1.2.tgz", + "integrity": "sha512-5TtHeAVX9D5d2LYfB1GAUn29BcVETVsrQ76Dwb2SpAfQGW3JVy4deJCAd0RrIkI3eEUrsl0E4xuBdreszxdTTg==", + "dependencies": { + "@babel/runtime": "^7.25.6", + "@mui/core-downloads-tracker": "^6.1.2", + "@mui/system": "^6.1.2", + "@mui/types": "^7.2.17", + "@mui/utils": "^6.1.2", "@popperjs/core": "^2.11.8", "@types/react-transition-group": "^4.4.11", "clsx": "^2.1.1", @@ -3576,7 +3578,7 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@mui/material-pigment-css": "^6.0.2", + "@mui/material-pigment-css": "^6.1.2", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" @@ -3597,12 +3599,12 @@ } }, "node_modules/@mui/private-theming": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.0.2.tgz", - "integrity": "sha512-emddFcRhA0hPGVIwIbW5g0V8vtCgw2g/H/A7jTdGe7dpCWEPpp6jPIXRRKcEUWgmg91R6rBNfV+LFHxBxmZXOQ==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.1.2.tgz", + "integrity": "sha512-S8WcjZdNdi++8UhrrY8Lton5h/suRiQexvdTfdcPAlbajlvgM+kx+uJstuVIEyTb3gMkxzIZep87knZ0tqcR0g==", "dependencies": { - "@babel/runtime": "^7.25.0", - "@mui/utils": "^6.0.2", + "@babel/runtime": "^7.25.6", + "@mui/utils": "^6.1.2", "prop-types": "^15.8.1" }, "engines": { @@ -3623,12 +3625,13 @@ } }, "node_modules/@mui/styled-engine": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.0.2.tgz", - "integrity": "sha512-qd3Vlhted0SYVGotnCfVNcxff7vW2WN0fclbAexff60NeNS1qs/H/CImHEHUBiUGeNWMPRochbN6VF1arQ7/jA==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.1.2.tgz", + "integrity": "sha512-uKOfWkR23X39xj7th2nyTcCHqInTAXtUnqD3T5qRVdJcOPvu1rlgTleTwJC/FJvWZJBU6ieuTWDhbcx5SNViHQ==", "dependencies": { - "@babel/runtime": "^7.25.0", + "@babel/runtime": "^7.25.6", "@emotion/cache": "^11.13.1", + "@emotion/sheet": "^1.4.0", "csstype": "^3.1.3", "prop-types": "^15.8.1" }, @@ -3654,15 +3657,15 @@ } }, "node_modules/@mui/system": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.0.2.tgz", - "integrity": "sha512-AZv1/C4PuHgWFTA8YraIzl3FTVLdRz0RIMRwEADWZBdIhnuTHS/4+r8qE9+3CcpTHg1WsEu8btaO3AhQahSM9A==", - "dependencies": { - "@babel/runtime": "^7.25.0", - "@mui/private-theming": "^6.0.2", - "@mui/styled-engine": "^6.0.2", - "@mui/types": "^7.2.16", - "@mui/utils": "^6.0.2", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.1.2.tgz", + "integrity": "sha512-mzW7F1ZMIYS1aLON48Nrk9c65OrVEVQ+R4lUcTWs1lCSul0VGK23eo4dmY0NX5PS7Oe4xz3P5B9tQZZ7SYgxcg==", + "dependencies": { + "@babel/runtime": "^7.25.6", + "@mui/private-theming": "^6.1.2", + "@mui/styled-engine": "^6.1.2", + "@mui/types": "^7.2.17", + "@mui/utils": "^6.1.2", "clsx": "^2.1.1", "csstype": "^3.1.3", "prop-types": "^15.8.1" @@ -3693,9 +3696,9 @@ } }, "node_modules/@mui/types": { - "version": "7.2.16", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.16.tgz", - "integrity": "sha512-qI8TV3M7ShITEEc8Ih15A2vLzZGLhD+/UPNwck/hcls2gwg7dyRjNGXcQYHKLB5Q7PuTRfrTkAoPa2VV1s67Ag==", + "version": "7.2.17", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.17.tgz", + "integrity": "sha512-oyumoJgB6jDV8JFzRqjBo2daUuHpzDjoO/e3IrRhhHo/FxJlaVhET6mcNrKHUq2E+R+q3ql0qAtvQ4rfWHhAeQ==", "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -3706,13 +3709,13 @@ } }, "node_modules/@mui/utils": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.0.2.tgz", - "integrity": "sha512-TeFrYsxcmeoDSlkoPhX+LjIuuqC5Pyj+xz2kRceKCkUpwMNTEeVOfowXDPe+mboZwmpJ5ZxP4eiAgQMdeEasjg==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.1.2.tgz", + "integrity": "sha512-6+B1YZ8cCBWD1fc3RjqpclF9UA0MLUiuXhyCO+XowD/Z2ku5IlxeEhHHlgglyBWFGMu4kib4YU3CDsG5/zVjJQ==", "dependencies": { - "@babel/runtime": "^7.25.0", - "@mui/types": "^7.2.16", - "@types/prop-types": "^15.7.12", + "@babel/runtime": "^7.25.6", + "@mui/types": "^7.2.17", + "@types/prop-types": "^15.7.13", "clsx": "^2.1.1", "prop-types": "^15.8.1", "react-is": "^18.3.1" @@ -3735,14 +3738,14 @@ } }, "node_modules/@mui/x-charts": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@mui/x-charts/-/x-charts-7.16.0.tgz", - "integrity": "sha512-YnimCNnLpmSu93LEGYAy8HaMoa6xqqQNFRD8BtzGs492sk/coM9uh6OwHJekF5302fDpANu3rYSF0KEpFWZM2Q==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@mui/x-charts/-/x-charts-7.18.0.tgz", + "integrity": "sha512-XvCX6sBj2aRw7MZcOPjKTEIwhj4j+ixw256bArQScSd+Y2BuHxAyfl/L6NPPWamckAPqJwjQ678jFH2s5t1f1w==", "dependencies": { "@babel/runtime": "^7.25.6", "@mui/utils": "^5.16.6", - "@mui/x-charts-vendor": "7.16.0", - "@mui/x-internals": "7.16.0", + "@mui/x-charts-vendor": "7.18.0", + "@mui/x-internals": "7.18.0", "@react-spring/rafz": "^9.7.4", "@react-spring/web": "^9.7.4", "clsx": "^2.1.1", @@ -3769,9 +3772,9 @@ } }, "node_modules/@mui/x-charts-vendor": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@mui/x-charts-vendor/-/x-charts-vendor-7.16.0.tgz", - "integrity": "sha512-MyMCCl7eAM53rLbjqP4zbMy5hYtdeqCjAYCH2jpvBKdgugm2eaPLKOPM8bUVfen0wHA8BXleQrIrNceytFPyZA==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@mui/x-charts-vendor/-/x-charts-vendor-7.18.0.tgz", + "integrity": "sha512-YPL7SP6W7t9jBIrK8WjmCtW/YoUmgr0BA7j2QE9bKX4VJLEUU498fEKik0ZJZyzyR7ov0MD3m7S+YCw1o5sqkw==", "dependencies": { "@babel/runtime": "^7.25.6", "@types/d3-color": "^3.1.3", @@ -3819,10 +3822,75 @@ } } }, + "node_modules/@mui/x-data-grid": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.18.0.tgz", + "integrity": "sha512-41UjJbRxWk+Yk/lfvaO55Pwo5p+F5s3rOTiHLl53ikCT5GuJ5OCCvik0Bi3c6DzTuUBdrEucae2618rydc2DGw==", + "dependencies": { + "@babel/runtime": "^7.25.6", + "@mui/utils": "^5.16.6", + "@mui/x-internals": "7.18.0", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "reselect": "^5.1.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.9.0", + "@emotion/styled": "^11.8.1", + "@mui/material": "^5.15.14 || ^6.0.0", + "@mui/system": "^5.15.14 || ^6.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/x-data-grid/node_modules/@mui/utils": { + "version": "5.16.6", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.16.6.tgz", + "integrity": "sha512-tWiQqlhxAt3KENNiSRL+DIn9H5xNVK6Jjf70x3PnfQPz1MPBdh7yyIcAyVBT9xiw7hP3SomRhPR7hzBMBCjqEA==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/types": "^7.2.15", + "@types/prop-types": "^15.7.12", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^18.3.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@mui/x-internals": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.16.0.tgz", - "integrity": "sha512-ijer5XYmWlJqWaTmF6TGH1odG7EAupv8iDWYmDm2yVR9IQ+L2nQSuhiFClI+wmGx40KS2VKOlzDMPpF0t7/HCg==", + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@mui/x-internals/-/x-internals-7.18.0.tgz", + "integrity": "sha512-lzCHOWIR0cAIY1bGrWSprYerahbnH5C31ql/2OWCEjcngL2NAV1M6oKI2Vp4HheqzJ822c60UyWyapvyjSzY/A==", "dependencies": { "@babel/runtime": "^7.25.6", "@mui/utils": "^5.16.6" @@ -5017,9 +5085,9 @@ "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==" }, "node_modules/@types/prop-types": { - "version": "15.7.12", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", - "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" + "version": "15.7.13", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", + "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==" }, "node_modules/@types/q": { "version": "1.5.8", @@ -17908,6 +17976,11 @@ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" }, + "node_modules/reselect": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", + "integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==" + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", diff --git a/frontend/package.json b/frontend/package.json index e45d25c..1785c83 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -7,8 +7,10 @@ "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", "@mui/icons-material": "^6.0.1", - "@mui/material": "^6.0.1", - "@mui/x-charts": "^7.15.0", + "@mui/material": "^6.1.2", + "@mui/system": "^6.1.2", + "@mui/x-charts": "^7.18.0", + "@mui/x-data-grid": "^7.18.0", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", diff --git a/frontend/src/assets/icons/arrows_horizontal.svg b/frontend/src/assets/icons/arrows_horizontal.svg index 26a6315..27e86d5 100644 --- a/frontend/src/assets/icons/arrows_horizontal.svg +++ b/frontend/src/assets/icons/arrows_horizontal.svg @@ -1,10 +1,6 @@ - - - - - - - - - + + + + + diff --git a/frontend/src/components/BasicTable.tsx b/frontend/src/components/BasicTable.tsx index 35b8511..aa506ab 100644 --- a/frontend/src/components/BasicTable.tsx +++ b/frontend/src/components/BasicTable.tsx @@ -1,9 +1,15 @@ -import { Box, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Tooltip } from "@mui/material"; -import { StyledTableCell, StyledTableRow } from "../styles/table"; +import { Box, Table, TableBody, TableCell, TableHead, Tooltip } from "@mui/material"; +import { + StyledNavLink, + StyledTableCell, + StyledTableContainer, + StyledTableHeadRow, + StyledTableRow +} from "../styles/table"; import { ReactComponent as CopyIconBlue } from '../assets/icons/copy_blue.svg'; import { ReactComponent as CopyIconGrey } from '../assets/icons/copy_grey.svg'; import { truncateString } from "../utils/utils"; -import { NavLink } from "react-router-dom"; +import React from "react"; interface Column { id: string; @@ -18,42 +24,40 @@ interface BasicTableProps { } export default function BasicTable({ - rows, - columns, - isMobile -}: BasicTableProps) { + rows, + columns, + isMobile + }: BasicTableProps) { const handleCopy = (text: string) => { navigator.clipboard.writeText(text); }; + return ( - + - + {columns.map((column) => ( - + {column.label} ))} - + {rows.map((row, index) => ( - + {columns.map((column) => ( { column.id === "hash" ? - - { + + { event.preventDefault(); event.clipboardData.setData("text/plain", row[column.id]); - }} style={index % 2 === 0 ? { color: "#656B73", textDecoration: "none" } : { color: "#0044A4", textDecoration: "none" }}> + }}> {isMobile ? row[column.id] : truncateString(row[column.id], 55)} - + handleCopy(row[column.id])}> {index % 2 === 0 ? : } @@ -66,6 +70,6 @@ export default function BasicTable({ ))}
- + ); } diff --git a/frontend/src/components/CustomAxisToolTip.tsx b/frontend/src/components/CustomAxisToolTip.tsx new file mode 100644 index 0000000..730f8db --- /dev/null +++ b/frontend/src/components/CustomAxisToolTip.tsx @@ -0,0 +1,115 @@ +import * as React from 'react'; +import NoSsr from '@mui/material/NoSsr'; +import Popper from '@mui/material/Popper'; +import Paper from '@mui/material/Paper'; +import Typography from '@mui/material/Typography'; +import { useAxisTooltip, useMouseTracker } from '@mui/x-charts/ChartsTooltip'; +import { generateVirtualElement } from '@mui/x-charts/ChartsTooltip/utils'; +import { formatTime } from "../utils/utils"; + +export function CustomAxisTooltip() { + const tooltipData = useAxisTooltip(); + const mousePosition = useMouseTracker(); + + if (!tooltipData || !mousePosition) { + return null; + } + + const isMousePointer = mousePosition?.pointerType === 'mouse'; + const yOffset = isMousePointer ? 0 : 40 - mousePosition.height; + + return ( + + theme.zIndex.modal, + }} + open + placement={isMousePointer ? 'top-end' : 'top'} + anchorEl={generateVirtualElement(mousePosition)} + modifiers={[ + { + name: 'offset', + options: { + offset: [0, yOffset], + }, + }, + ]} + > + + + + + + + + + {tooltipData.seriesItems.map((seriesItem, index) => ( + + + + + + ))} + +
+ + {tooltipData.axisFormattedValue} + +
+
+
+ + {seriesItem.formattedLabel} + + + + {seriesItem.value !== null && typeof seriesItem.value === 'number' + ? formatTime(seriesItem.value) + : 'N/A'} + +
+
+
+
+ ); +} diff --git a/frontend/src/components/CustomLineChart.tsx b/frontend/src/components/CustomLineChart.tsx index 23207ba..163628b 100644 --- a/frontend/src/components/CustomLineChart.tsx +++ b/frontend/src/components/CustomLineChart.tsx @@ -1,10 +1,14 @@ -import { LineChart, LineChartProps } from '@mui/x-charts/LineChart'; +import * as React from 'react'; import { FC } from 'react'; -import { Typography, Box } from '@mui/material'; +import { LinePlot, MarkPlot } from '@mui/x-charts/LineChart'; import dayjs from 'dayjs'; -import { LineSeriesType } from '@mui/x-charts'; -import RevertedInfoBox from './RevertedInfoBox'; -import { formatTime } from '../utils/utils'; +import { ChartsXAxis, ChartsYAxis, LineSeriesType } from '@mui/x-charts'; +import { ResponsiveChartContainer } from '@mui/x-charts/ResponsiveChartContainer'; +import { CustomAxisTooltip } from "./CustomAxisToolTip"; +import Box from '@mui/material/Box'; +import { Typography } from "@mui/material"; +import RevertedInfoBox from "./RevertedInfoBox"; +import { formatTime } from "../utils/utils"; interface CustomLineChartProps { data: { @@ -16,51 +20,64 @@ interface CustomLineChartProps { } const CustomLineChart: FC = ({ data, title = 'Inclusion Time Chart', estimatedInclusionTime }) => { - const xAxisData = data.map(point => point.day * 1000); - const seriesData = data.map(point => point.averageInclusionTime); + const sortedData = data.sort((a, b) => a.day - b.day); + const dataset = sortedData.map(point => ({ + x: point.day * 1000, + y: point.averageInclusionTime, + })); - const formatDate = (timestamp: number) => { - let formattedDate= dayjs(timestamp).format('DD MMM') + const formatDate = (timestamp: number) => { + let formattedDate = dayjs(timestamp).format('DD MMM'); const parts = formattedDate.split(' '); - parts[1] = parts[1].toUpperCase(); + parts[1] = parts[1].toUpperCase(); return parts.join(' '); - }; + }; const xAxis = { - scaleType: 'time' as const, - data: xAxisData, - valueFormatter: formatDate + scaleType: 'band' as const, + dataKey: 'x', + valueFormatter: formatDate, }; - - const yAxis: LineSeriesType[] = [{ + + const series: LineSeriesType[] = [{ type: 'line', - data: seriesData, + dataKey: 'y', + label: 'Average Inclusion Time', curve: "linear", color: '#0044A4', - }] + }]; - const chartProps: LineChartProps = { - xAxis: [xAxis], - series: yAxis, - yAxis: [{ - valueFormatter: formatTime - }], - width: 600, - height: 400, + const yAxis = { + valueFormatter: formatTime, }; return ( - + {/* Add zIndex to RevertedInfoBox */} {title} {estimatedInclusionTime && ( - + )} - - + + + + + + + ); }; diff --git a/frontend/src/components/OverviewCard.tsx b/frontend/src/components/OverviewCard.tsx index 65d1f97..64c9aeb 100644 --- a/frontend/src/components/OverviewCard.tsx +++ b/frontend/src/components/OverviewCard.tsx @@ -6,18 +6,21 @@ interface OverviewCardProps { title: string; iconSrc?: string; children: React.ReactNode; + centerTitle?: boolean; } -const OverviewCard: FC = ({ title, iconSrc, children }) => { +const OverviewCard: FC = ({ title, iconSrc, children, centerTitle = false }) => { return ( - + {iconSrc && ( )} - {title} + + {title} + {children} diff --git a/frontend/src/components/RevertedInfoBox.tsx b/frontend/src/components/RevertedInfoBox.tsx index e7bb5b8..97c61c9 100644 --- a/frontend/src/components/RevertedInfoBox.tsx +++ b/frontend/src/components/RevertedInfoBox.tsx @@ -10,14 +10,13 @@ interface InfoBoxProps { const RevertedInfoBox: FC = ({ title, tooltip, value }) => { return ( - + {/* Add zIndex */} {/* Display value with subtitle1 style */} - {title} - + diff --git a/frontend/src/modules/TransactionGauge.tsx b/frontend/src/modules/TransactionGauge.tsx index 7ea6562..cda3a39 100644 --- a/frontend/src/modules/TransactionGauge.tsx +++ b/frontend/src/modules/TransactionGauge.tsx @@ -52,7 +52,7 @@ const TransactionGauge = () => { return ( - + {errorTransactionStats ? ( Error fetching Transaction Stats: {errorTransactionStats.message} ) : ( diff --git a/frontend/src/pages/Slot.tsx b/frontend/src/pages/Slot.tsx index 74d84ef..2d4e5cd 100644 --- a/frontend/src/pages/Slot.tsx +++ b/frontend/src/pages/Slot.tsx @@ -14,12 +14,12 @@ const Slot = () => { const { data: userTransactionsData, loading: loadingUser, error: errorUser } = useFetch('/api/transaction/latest_user_transactions?limit=10'); const sequencerTransactionColumns = [ - { id: 'timestamp', label: 'Age', minWidth: 120 }, + { id: 'timestamp', label: 'Tx Age', minWidth: 120 }, { id: 'hash', label: 'Sequencer Transaction Hash', minWidth: 170 }, ]; const userTransactionColumns = [ - { id: 'timestamp', label: 'Age', minWidth: 120 }, + { id: 'timestamp', label: 'Tx Age', minWidth: 120 }, { id: 'hash', label: 'User Transaction Hash', minWidth: 170 }, ]; @@ -113,7 +113,7 @@ const Slot = () => { Error fetching sequencer transactions: {errorSequencer.message} ) : ( <> - Sequencer Transactions + Sequencer Transactions {loadingSequencer ? ( Loading... ) : ( diff --git a/frontend/src/pages/System.tsx b/frontend/src/pages/System.tsx index 40d91dc..2126818 100644 --- a/frontend/src/pages/System.tsx +++ b/frontend/src/pages/System.tsx @@ -34,6 +34,9 @@ const System = () => { display: 'flex', flexDirection: 'column'}}> + + + {/* Transaction Gauge Section */} diff --git a/frontend/src/styles/overviewCard.ts b/frontend/src/styles/overviewCard.ts index 31e5269..f0297f9 100644 --- a/frontend/src/styles/overviewCard.ts +++ b/frontend/src/styles/overviewCard.ts @@ -11,20 +11,12 @@ export const CardContainer = styled(Paper)` height: 100%; `; -export const CardHeader = styled.div` +export const CardHeader = styled.div<{ centerTitle?: boolean }>` display: flex; flex-direction: column; - align-items: flex-start; + align-items: ${({ centerTitle }) => (centerTitle ? 'center' : 'flex-start')}; gap: 16px; margin-bottom: 16px; - - - h2 { - font-size: 24px; - font-weight: 600; - color: #051016; - letter-spacing: -1.2px; - } `; export const IconWrapper = styled.div` diff --git a/frontend/src/styles/searchBar.ts b/frontend/src/styles/searchBar.ts index 7663af7..7af5746 100644 --- a/frontend/src/styles/searchBar.ts +++ b/frontend/src/styles/searchBar.ts @@ -54,13 +54,13 @@ export const ChainName = styled.span.attrs({ export const StyledDropdownIcon = styled(DropdownIcon)` width: 16px; height: 16px; - fill: ${({ theme }) => theme.colors.gray300}; + fill: ${({ theme }) => theme.colors.gray}; `; export const StyledSearchIcon = styled(SearchIcon)` width: 16px; height: 16px; - fill: ${({ theme }) => theme.colors.gray300}; + fill: ${({ theme }) => theme.colors.gray}; `; @@ -82,7 +82,7 @@ export const StyledTextField = styled(TextField).attrs({ } .MuiInputBase-input { - color: ${({ theme }) => theme.colors.gray300}; + color: ${({ theme }) => theme.colors.gray}; } } diff --git a/frontend/src/styles/table.ts b/frontend/src/styles/table.ts index 7708af2..bdbd372 100644 --- a/frontend/src/styles/table.ts +++ b/frontend/src/styles/table.ts @@ -1,31 +1,47 @@ -import { TableCell, TableRow, tableCellClasses } from '@mui/material'; +import {TableCell, TableRow, tableCellClasses, TableContainer} from '@mui/material'; import { NavLink } from 'react-router-dom'; import styled from 'styled-components'; -export const StyledTableCell = styled(TableCell)(({ theme }) => ({ - [`&.${tableCellClasses.head}`]: { - backgroundColor: "#f5f5f5", - color: "#f5f5f5", +export const StyledTableContainer = styled(TableContainer)(({ theme }) => ({ + marginTop: theme.spacing(2), + border: `0.5px solid ${theme.colors.gray}`, + boxShadow: 'none', + borderRadius: '4px', +})); + +export const StyledTableHeadRow = styled(TableRow)(({ theme }) => ({ + '& th': { + borderBottom: 'none', + color: theme.colors.gray, + fontWeight: 600, + paddingTop: theme.spacing(1), + paddingBottom: theme.spacing(1), }, +})); + +export const StyledTableCell = styled(TableCell)(({ theme }) => ({ [`&.${tableCellClasses.body}`]: { fontSize: 14, + paddingTop: theme.spacing(1), + paddingBottom: theme.spacing(1), }, })); + export const StyledTableRow = styled(TableRow)(({ theme }) => ({ '&:nth-of-type(even)': { - backgroundColor: "#F7F9FB", + backgroundColor: theme.colors.lightBlue, }, - // hide last border '&:last-child td, &:last-child th': { border: 0, } })); -export const StyledNavLinkEven = styled(NavLink)` - color: blue; // Set the color for even rows -`; - -export const StyledNavLinkOdd = styled(NavLink)` - color: black; // Default color for odd rows -`; +export const StyledNavLink = styled(NavLink)` + color: ${({ theme }) => theme.palette.primary.main}; + font-weight: 600; + text-decoration: none; + &:hover { + text-decoration: underline; + } +`; \ No newline at end of file diff --git a/frontend/src/theme.ts b/frontend/src/theme.ts index ec51f65..baeb52b 100644 --- a/frontend/src/theme.ts +++ b/frontend/src/theme.ts @@ -54,6 +54,18 @@ const muiTheme = createTheme({ }, }, components: { + MuiLink: { + styleOverrides: { + root: { + color: '#0044A4', + fontWeight: 600, + textDecoration: 'none', + '&:hover': { + textDecoration: 'underline', + }, + }, + }, + }, MuiAlert: { styleOverrides: { root: { @@ -74,8 +86,9 @@ const customTheme = { background: '#FFFFFF', textPrimary: '#051016', textSecondary: '#656B73', - gray300: '#989CA2', + gray: '#989CA2', + lightBlue: '#F7F9FB' }, }; -export { muiTheme, customTheme }; \ No newline at end of file +export { muiTheme, customTheme };