From f5f2001c39582b601e870a5ec2be72faa1423598 Mon Sep 17 00:00:00 2001 From: Teto Gomez Date: Tue, 27 Apr 2021 14:08:09 -0600 Subject: [PATCH] Pause transaction history graphic (#425) * feat(FE): add pause button in trasaction history graphic * feat(FE): disable pause button style * feat(FE): add query to get block history data * feat(FE): modeled transaction block data * fix(FE): resolve conflict * fix(FE): fix delete warnings and clean code * fix(FE): migrations change * fix(FE): i18n fix --- .../workflows/push-lacchain-environment.yaml | 4 +- .../workflows/push-master-environment.yaml | 4 +- .../services/state-history-plugin.service.js | 1 + hasura/metadata/tables.yaml | 14 ++ kubernetes/configmap-dashboard.yaml | 2 +- .../src/components/TransactionsLineChart.js | 37 ++- webapp/src/gql/index.js | 1 + webapp/src/gql/transaction.gql.js | 14 ++ webapp/src/language/en.json | 9 +- webapp/src/language/es.json | 9 +- webapp/src/routes/Home.js | 220 ------------------ webapp/src/routes/Home/BlockProducerInfo.js | 126 ++++++++++ webapp/src/routes/Home/EqualIcon.js | 44 ++++ webapp/src/routes/Home/TransactionInfo.js | 177 ++++++++++++++ webapp/src/routes/Home/index.js | 39 ++++ webapp/src/routes/Home/styles.js | 76 ++++++ 16 files changed, 548 insertions(+), 229 deletions(-) create mode 100644 webapp/src/gql/transaction.gql.js delete mode 100644 webapp/src/routes/Home.js create mode 100644 webapp/src/routes/Home/BlockProducerInfo.js create mode 100644 webapp/src/routes/Home/EqualIcon.js create mode 100644 webapp/src/routes/Home/TransactionInfo.js create mode 100644 webapp/src/routes/Home/index.js create mode 100644 webapp/src/routes/Home/styles.js diff --git a/.github/workflows/push-lacchain-environment.yaml b/.github/workflows/push-lacchain-environment.yaml index 48f17c29..ff576aa7 100644 --- a/.github/workflows/push-lacchain-environment.yaml +++ b/.github/workflows/push-lacchain-environment.yaml @@ -33,7 +33,7 @@ jobs: DOCKER_PASSWORD: ${{ secrets.DOCKER_HUB_PASSWORD }} # Webapp PORT: '80' - REACT_APP_TTILE: 'LACChain Network Dashboard' + REACT_APP_TITLE: 'LACChain Network Dashboard' REACT_APP_DEFAULT_PRODUCER_LOGO: 'https://latamlink.io/images/latamlink_logo-header.svg' REACT_APP_EOS_RATE_LINK: '' REACT_APP_USE_REWARDS: 'false' @@ -68,7 +68,7 @@ jobs: INGRESS_GRAPHQL_HOST: dashboard-graphql.latamlink.io # Webapp PORT: '80' - REACT_APP_TTILE: 'LACChain Network Dashboard' + REACT_APP_TITLE: 'LACChain Network Dashboard' REACT_APP_DEFAULT_PRODUCER_LOGO: 'https://latamlink.io/images/latamlink_logo-header.svg' REACT_APP_EOS_RATE_LINK: '' REACT_APP_USE_REWARDS: 'false' diff --git a/.github/workflows/push-master-environment.yaml b/.github/workflows/push-master-environment.yaml index 8db82c05..2ce3cfcd 100644 --- a/.github/workflows/push-master-environment.yaml +++ b/.github/workflows/push-master-environment.yaml @@ -32,7 +32,7 @@ jobs: DOCKER_PASSWORD: ${{ secrets.DOCKER_HUB_PASSWORD }} # Webapp PORT: "80" - REACT_APP_TTILE: "EOS Mainnet Network Dashboard" + REACT_APP_TITLE: "EOS Mainnet Network Dashboard" REACT_APP_DEFAULT_PRODUCER_LOGO: "https://bloks.io/img/eosio.png" REACT_APP_EOS_RATE_LINK: "https://eosrate.io" REACT_APP_USE_REWARDS: "true" @@ -64,7 +64,7 @@ jobs: INGRESS_GRAPHQL_HOST: graphql-mainnet.eosio.cr # Webapp PORT: "80" - REACT_APP_TTILE: "EOS Mainnet Network Dashboard" + REACT_APP_TITLE: "EOS Mainnet Network Dashboard" REACT_APP_DEFAULT_PRODUCER_LOGO: "https://bloks.io/img/eosio.png" REACT_APP_EOS_RATE_LINK: "https://eosrate.io" REACT_APP_USE_REWARDS: "true" diff --git a/hapi/src/services/state-history-plugin.service.js b/hapi/src/services/state-history-plugin.service.js index afb4ea2c..d891481c 100644 --- a/hapi/src/services/state-history-plugin.service.js +++ b/hapi/src/services/state-history-plugin.service.js @@ -101,6 +101,7 @@ const handleBlocksResult = async data => { this_block: data.this_block, prev_block: data.prev_block } + await saveBlockHistory({ producer: block.producer, schedule_version: block.schedule_version, diff --git a/hasura/metadata/tables.yaml b/hasura/metadata/tables.yaml index 7463fa8a..1f1f97aa 100644 --- a/hasura/metadata/tables.yaml +++ b/hasura/metadata/tables.yaml @@ -1,6 +1,20 @@ - table: schema: public name: block_history + select_permissions: + - role: guest + permission: + columns: + - id + - block_id + - block_num + - transactions_length + - timestamp + - created_at + - updated_at + - producer + - schedule_version + filter: {} - table: schema: public name: block_history_by_producer_type diff --git a/kubernetes/configmap-dashboard.yaml b/kubernetes/configmap-dashboard.yaml index 07d87b42..f43b7d43 100644 --- a/kubernetes/configmap-dashboard.yaml +++ b/kubernetes/configmap-dashboard.yaml @@ -5,7 +5,7 @@ metadata: data: # webapp PORT: "${PORT}" - REACT_APP_TTILE: "${REACT_APP_TTILE}" + REACT_APP_TITLE: "${REACT_APP_TITLE}" REACT_APP_DEFAULT_PRODUCER_LOGO: "${REACT_APP_DEFAULT_PRODUCER_LOGO}" REACT_APP_EOS_RATE_LINK: "${REACT_APP_EOS_RATE_LINK}" REACT_APP_USE_REWARDS: "${REACT_APP_USE_REWARDS}" diff --git a/webapp/src/components/TransactionsLineChart.js b/webapp/src/components/TransactionsLineChart.js index af829569..abf9f8d3 100644 --- a/webapp/src/components/TransactionsLineChart.js +++ b/webapp/src/components/TransactionsLineChart.js @@ -5,6 +5,7 @@ import { makeStyles } from '@material-ui/styles' import { useTheme } from '@material-ui/core/styles' import Divider from '@material-ui/core/Divider' import { useTranslation } from 'react-i18next' +import Skeleton from '@material-ui/lab/Skeleton' import PropTypes from 'prop-types' import { LineChart, @@ -24,9 +25,38 @@ const useStyles = makeStyles((theme) => ({ }, description: { fontWeight: 'normal' + }, + graphSkeleton: { + width: '100%', + margin: theme.spacing(1), + '& .MuiSkeleton-rect': { + width: '100%', + height: 300 + }, + '& .MuiBox-root': { + display: 'flex', + justifyContent: 'center', + '& .MuiSkeleton-text': { + margin: theme.spacing(0, 1) + } + } } })) +const GraphSkeleton = () => { + const classes = useStyles() + + return ( + + + + + + + + ) +} + const CustomTooltip = ({ active, payload }) => { const classes = useStyles() const { t } = useTranslation('transactionsChartComponent') @@ -72,10 +102,12 @@ CustomTooltip.propTypes = { payload: PropTypes.array } -const TransactionsChart = ({ data }) => { +const TransactionsChart = ({ data, loading }) => { const theme = useTheme() const { t } = useTranslation('transactionsChartComponent') + if (loading) return + return ( { } TransactionsChart.propTypes = { - data: PropTypes.array + data: PropTypes.array, + loading: PropTypes.bool } export default TransactionsChart diff --git a/webapp/src/gql/index.js b/webapp/src/gql/index.js index 06c58cfc..7105cadc 100644 --- a/webapp/src/gql/index.js +++ b/webapp/src/gql/index.js @@ -1,2 +1,3 @@ export * from './producer.gql' export * from './setting.gql' +export * from './transaction.gql' diff --git a/webapp/src/gql/transaction.gql.js b/webapp/src/gql/transaction.gql.js new file mode 100644 index 00000000..6a7607de --- /dev/null +++ b/webapp/src/gql/transaction.gql.js @@ -0,0 +1,14 @@ +import gql from 'graphql-tag' + +export const TRANSACTION_QUERY = gql` + query($date: timestamptz) { + blockHistory: block_history( + where: { timestamp: { _gte: $date } } + order_by: { block_num: desc } + ) { + block_id + block_num + transactions_length + } + } +` diff --git a/webapp/src/language/en.json b/webapp/src/language/en.json index 4280a62e..772d1c90 100644 --- a/webapp/src/language/en.json +++ b/webapp/src/language/en.json @@ -88,7 +88,14 @@ "transPerSecond": "Transactions Per Second", "transPerBlock": "Transactions Per Block", "transactions": "Transactions", - "transactionsHistory": "Transactions History" + "transactionsHistory": "Transactions History", + "timeFrame": "Time frame", + "pause": "Pause", + "play": "Play", + "Last Hour": "Last Hour", + "Last Day": "Last Day", + "Last Week": "Last Week", + "Last Year": "Last Year" }, "networkInfoRoute": { "chainId": "Chain Id", diff --git a/webapp/src/language/es.json b/webapp/src/language/es.json index 4ac1c034..65f36c54 100644 --- a/webapp/src/language/es.json +++ b/webapp/src/language/es.json @@ -87,7 +87,14 @@ "bpSchedule": "Programa de Productor de Bloque", "transPerSecond": "Transacciones por Segundo", "transPerBlock": "Transacciones por Bloque", - "transactionsHistory": "Historial de transacciones" + "transactionsHistory": "Historial de transacciones", + "timeFrame": "Periodo de tiempo", + "pause": "Pausa", + "play": "Play", + "Last Hour": "Última Hora", + "Last Day": "Última Día", + "Last Week": "Última Semana", + "Last Year": "Último Año" }, "networkInfoRoute": { "chainId": "Identificador de la cadena", diff --git a/webapp/src/routes/Home.js b/webapp/src/routes/Home.js deleted file mode 100644 index aeede033..00000000 --- a/webapp/src/routes/Home.js +++ /dev/null @@ -1,220 +0,0 @@ -/* eslint camelcase: 0 */ -import React, { lazy, useEffect, useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { makeStyles } from '@material-ui/styles' -import { useQuery } from '@apollo/react-hooks' -import { useTranslation } from 'react-i18next' - -import { formatWithThousandSeparator } from '../utils' -import { NODES_QUERY } from '../gql' - -const Card = lazy(() => import('@material-ui/core/Card')) -const CardContent = lazy(() => import('@material-ui/core/CardContent')) -const Grid = lazy(() => import('@material-ui/core/Grid')) -const Box = lazy(() => import('@material-ui/core/Box')) -const Typography = lazy(() => import('@material-ui/core/Typography')) -const LinearProgress = lazy(() => import('@material-ui/core/LinearProgress')) -const ProducersChart = lazy(() => import('../components/ProducersChart')) -const TransactionsHistory = lazy(() => - import('../components/TransactionsHistory') -) -const NodesSummary = lazy(() => import('../components/NodesSummary')) -const TransactionsLineChart = lazy(() => - import('../components/TransactionsLineChart') -) - -const useStyles = makeStyles((theme) => ({ - leftColumn: { - alignContent: 'space-between', - paddingRight: 0, - '& .MuiCard-root': { - height: 125, - marginBottom: theme.spacing(2), - '& .MuiTypography-body1': { - marginBottom: theme.spacing(2) - } - }, - [theme.breakpoints.up('md')]: { - paddingRight: theme.spacing(2) - } - }, - rightColumn: { - paddingLeft: 0, - marginBottom: theme.spacing(2), - [theme.breakpoints.up('md')]: { - paddingLeft: theme.spacing(2) - } - }, - bottomRow: { - paddingTop: 0, - [theme.breakpoints.up('md')]: { - paddingTop: theme.spacing(1) - } - }, - boxIrreversible: { - display: 'flex', - alignItems: 'baseline', - paddingTop: theme.spacing(3), - '& .MuiTypography-body1': { - marginBottom: '0 !important', - letterSpacing: '0.09px', - color: 'rgba(0, 0, 0, 0.54)', - '& strong': { - color: '#212121' - } - } - } -})) - -const Home = () => { - const dispatch = useDispatch() - const classes = useStyles() - const { data: { loading, producers } = {} } = useQuery(NODES_QUERY) - const info = useSelector((state) => state.eos.info) - const tps = useSelector((state) => state.eos.tps) - const tpb = useSelector((state) => state.eos.tpb) - const scheduleInfo = useSelector((state) => state.eos.schedule) - const [schedule, setSchedule] = useState({ producers: [] }) - const [graphicData, setGraphicData] = useState([]) - const { t } = useTranslation('homeRoute') - - useEffect(() => { - const majorLength = tps.length > tpb.length ? tps.length : tpb.length - const dataModeled = [] - - if (!majorLength) return - - for (let index = 0; index < majorLength; index++) { - dataModeled.push({ - tps: tps[index] ? tps[index].transactions : 0, - tpb: tpb[index] ? tpb[index].transactions : 0, - blocks: { - tps: tps[index] ? tps[index].blocks : [0], - tpb: tpb[index] ? tpb[index].blocks : [0] - } - }) - } - - setGraphicData(dataModeled) - }, [tps, tpb]) - - useEffect(() => { - dispatch.eos.startTrackingInfo({ interval: 0.5 }) - dispatch.eos.startTrackingProducerSchedule({ interval: 60 }) - }, [dispatch]) - - useEffect(() => { - const newProducers = scheduleInfo.producers.map((item) => { - const data = - (producers || []).find((producer) => { - let result = producer.owner === item.producer_name - - if (!result) { - result = producer.bp_json?.nodes.find( - (node) => node.node_name === item.producer_name - ) - } - - return result - }) || {} - - return { - logo: data?.bp_json?.org?.branding?.logo_256, - url: data?.url, - owner: item.producer_name || data.owner, - rewards: data.total_rewards, - total_votes_percent: data.total_votes_percent * 100, - value: 20 - } - }) - setSchedule({ - ...scheduleInfo, - producers: newProducers - }) - }, [scheduleInfo, producers]) - - useEffect(() => { - return () => { - dispatch.eos.stopTrackingInfo() - dispatch.eos.stopTrackingProducerSchedule() - } - }, [dispatch]) - - return ( - - - - - - - {t('currentProducer')} - - {info.head_block_producer} - - - - - - - - {t('headBlock')} - - {formatWithThousandSeparator(info.head_block_num)} - - - - {`${t('lastBlock')}: `} - - {formatWithThousandSeparator( - info.last_irreversible_block_num - )} - - - - - - - - - - {loading && } - - - - - {t('bpSchedule')} - - - Ver. {schedule?.version} - - - - - - - - - - - - {t('transactions')} - - - - - - - - ) -} - -Home.propTypes = {} - -export default Home diff --git a/webapp/src/routes/Home/BlockProducerInfo.js b/webapp/src/routes/Home/BlockProducerInfo.js new file mode 100644 index 00000000..46f4ec7b --- /dev/null +++ b/webapp/src/routes/Home/BlockProducerInfo.js @@ -0,0 +1,126 @@ +/* eslint camelcase: 0 */ +import React, { lazy, useEffect, useState } from 'react' +import { useSelector } from 'react-redux' +import { useQuery } from '@apollo/react-hooks' +import PropTypes from 'prop-types' + +import { formatWithThousandSeparator } from '../../utils' +import { NODES_QUERY } from '../../gql' + +const Card = lazy(() => import('@material-ui/core/Card')) +const CardContent = lazy(() => import('@material-ui/core/CardContent')) +const Grid = lazy(() => import('@material-ui/core/Grid')) +const Box = lazy(() => import('@material-ui/core/Box')) +const Typography = lazy(() => import('@material-ui/core/Typography')) +const LinearProgress = lazy(() => import('@material-ui/core/LinearProgress')) +const ProducersChart = lazy(() => import('../../components/ProducersChart')) +const TransactionsHistory = lazy(() => + import('../../components/TransactionsHistory') +) +const NodesSummary = lazy(() => import('../../components/NodesSummary')) + +const BlockProducerInfo = ({ t, classes }) => { + const { data: { loading, producers } = {} } = useQuery(NODES_QUERY) + const scheduleInfo = useSelector((state) => state.eos.schedule) + const info = useSelector((state) => state.eos.info) + const [schedule, setSchedule] = useState({ producers: [] }) + + useEffect(() => { + const newProducers = scheduleInfo.producers.map((item) => { + const data = + (producers || []).find((producer) => { + let result = producer.owner === item.producer_name + + if (!result) { + result = producer.bp_json?.nodes.find( + (node) => node.node_name === item.producer_name + ) + } + + return result + }) || {} + + return { + logo: data?.bp_json?.org?.branding?.logo_256, + url: data?.url, + owner: item.producer_name || data.owner, + rewards: data.total_rewards, + total_votes_percent: data.total_votes_percent * 100, + value: 20 + } + }) + setSchedule({ + ...scheduleInfo, + producers: newProducers + }) + }, [scheduleInfo, producers]) + + return ( + + + + + + {t('currentProducer')} + + {info.head_block_producer} + + + + + + + + {t('headBlock')} + + {formatWithThousandSeparator(info.head_block_num)} + + + + {`${t('lastBlock')}: `} + + {formatWithThousandSeparator( + info.last_irreversible_block_num + )} + + + + + + + + + + {loading && } + + + + + {t('bpSchedule')} + + Ver. {schedule?.version} + + + + + + ) +} + +BlockProducerInfo.propTypes = { + t: PropTypes.any, + classes: PropTypes.object +} + +BlockProducerInfo.defaultProps = { + classes: {} +} + +export default BlockProducerInfo diff --git a/webapp/src/routes/Home/EqualIcon.js b/webapp/src/routes/Home/EqualIcon.js new file mode 100644 index 00000000..09b86e5a --- /dev/null +++ b/webapp/src/routes/Home/EqualIcon.js @@ -0,0 +1,44 @@ +import React from 'react' +import PropTypes from 'prop-types' +import Box from '@material-ui/core/Box' + +const EqualIcon = ({ width, height, color }) => { + return ( + + + + + + + ) +} + +EqualIcon.propTypes = { + width: PropTypes.number, + height: PropTypes.number, + color: PropTypes.string +} + +EqualIcon.defaultProps = { + width: 24, + height: 24, + color: 'black' +} + +export default EqualIcon diff --git a/webapp/src/routes/Home/TransactionInfo.js b/webapp/src/routes/Home/TransactionInfo.js new file mode 100644 index 00000000..adbb3b80 --- /dev/null +++ b/webapp/src/routes/Home/TransactionInfo.js @@ -0,0 +1,177 @@ +/* eslint camelcase: 0 */ +import React, { useEffect, useState } from 'react' +import { useSelector } from 'react-redux' +import { useLazyQuery } from '@apollo/react-hooks' +import { useTheme } from '@material-ui/core/styles' +import clsx from 'clsx' +import moment from 'moment' +import PropTypes from 'prop-types' +import Select from '@material-ui/core/Select' +import Card from '@material-ui/core/Card' +import MenuItem from '@material-ui/core/MenuItem' +import FormControl from '@material-ui/core/FormControl' +import InputLabel from '@material-ui/core/InputLabel' +import CardContent from '@material-ui/core/CardContent' +import Grid from '@material-ui/core/Grid' +import Box from '@material-ui/core/Box' +import Typography from '@material-ui/core/Typography' +import PlayArrowIcon from '@material-ui/icons/PlayArrow' + +import TransactionsLineChart from '../../components/TransactionsLineChart' +import EqualIcon from './EqualIcon' + +import { TRANSACTION_QUERY } from '../../gql' + +const options = [ + { value: '0', label: 'Live (5 min)' }, + { value: '1 Hour', label: 'Last Hour' }, + { value: '1 Day', label: 'Last Day' }, + { value: '1 Week', label: 'Last Week' }, + { value: '1 Year', label: 'Last Year' } +] +const baseValues = { block_num: null, transaction_length: 0 } + +const TransactionInfo = ({ t, classes }) => { + const theme = useTheme() + const tps = useSelector((state) => state.eos.tps) + const tpb = useSelector((state) => state.eos.tpb) + const [graphicData, setGraphicData] = useState([]) + const [option, setOption] = useState(options[0].value) + const [pause, setPause] = useState(false) + const [ + getTransactionHistory, + { loading: trxHistoryLoading, data: trxHistory } + ] = useLazyQuery(TRANSACTION_QUERY) + + useEffect(() => { + const majorLength = tps.length > tpb.length ? tps.length : tpb.length + const dataModeled = [] + + if (!majorLength || pause || option !== '0') return + + for (let index = 0; index < majorLength; index++) { + dataModeled.push({ + tps: tps[index] ? tps[index].transactions : 0, + tpb: tpb[index] ? tpb[index].transactions : 0, + blocks: { + tps: tps[index] ? tps[index].blocks : [0], + tpb: tpb[index] ? tpb[index].blocks : [0] + } + }) + } + + setGraphicData(dataModeled) + }, [tps, tpb]) + + useEffect(() => { + if (option !== '0') { + const values = option.split(' ') + const date = moment().subtract(values[0], values[1]).utc().toString() + getTransactionHistory({ + variables: { date } + }) + } + }, [option, getTransactionHistory]) + + useEffect(() => { + const dataModeled = [] + + if (option !== '0') { + if (!trxHistory?.block_history?.length) { + setGraphicData(dataModeled) + + return + } + + for (let i = 1; i < trxHistory.block_history.length; i = i + 2) { + const item = trxHistory.block_history[i] || baseValues + const prevItem = trxHistory.block_history[i - 1] || baseValues + const trxPer = item.transaction_length + prevItem.transaction_length + const blocks = [item.block_num, prevItem.block_num] + + dataModeled.push({ + tps: trxPer, + tpb: trxPer, + blocks: { + tps: blocks, + tpb: blocks + } + }) + } + + setGraphicData(dataModeled) + } + }, [trxHistoryLoading, trxHistory]) + + return ( + + + + + + + {t('transactions')} + + + + {t('timeFrame')} + + + + option === options[0].value && setPause(!pause) + } + className={clsx(classes.pauseButton, { + [classes.disableButton]: option !== options[0].value + })} + > + {pause ? ( + + ) : ( + + )} + {pause ? t('play') : t('pause')} + + + + + + + + + + ) +} + +TransactionInfo.propTypes = { + t: PropTypes.any, + classes: PropTypes.object +} + +TransactionInfo.defaultProps = { + classes: {} +} + +export default TransactionInfo diff --git a/webapp/src/routes/Home/index.js b/webapp/src/routes/Home/index.js new file mode 100644 index 00000000..5e548f16 --- /dev/null +++ b/webapp/src/routes/Home/index.js @@ -0,0 +1,39 @@ +import React, { lazy, useEffect } from 'react' +import { makeStyles } from '@material-ui/styles' +import { useDispatch } from 'react-redux' +import { useTranslation } from 'react-i18next' + +import styles from './styles' + +const useStyles = makeStyles(styles) + +const Box = lazy(() => import('@material-ui/core/Box')) +const BlockProducerInfo = lazy(() => import('./BlockProducerInfo')) +const TransactionInfo = lazy(() => import('./TransactionInfo')) + +const Home = () => { + const dispatch = useDispatch() + const { t } = useTranslation('homeRoute') + const classes = useStyles() + + useEffect(() => { + dispatch.eos.startTrackingInfo({ interval: 0.5 }) + dispatch.eos.startTrackingProducerSchedule({ interval: 60 }) + + return () => { + dispatch.eos.stopTrackingInfo() + dispatch.eos.stopTrackingProducerSchedule() + } + }, [dispatch]) + + return ( + + + + + ) +} + +Home.propTypes = {} + +export default Home diff --git a/webapp/src/routes/Home/styles.js b/webapp/src/routes/Home/styles.js new file mode 100644 index 00000000..58f037d0 --- /dev/null +++ b/webapp/src/routes/Home/styles.js @@ -0,0 +1,76 @@ +export default (theme) => ({ + leftColumn: { + alignContent: 'space-between', + paddingRight: 0, + '& .MuiCard-root': { + marginBottom: theme.spacing(2), + '& .MuiTypography-body1': { + marginBottom: theme.spacing(2) + } + }, + [theme.breakpoints.up('md')]: { + paddingRight: theme.spacing(2) + } + }, + rightColumn: { + paddingLeft: 0, + marginBottom: theme.spacing(2), + [theme.breakpoints.up('md')]: { + paddingLeft: theme.spacing(2) + } + }, + bottomRow: { + paddingTop: 0, + [theme.breakpoints.up('md')]: { + paddingTop: theme.spacing(1) + } + }, + boxIrreversible: { + display: 'flex', + alignItems: 'baseline', + paddingTop: theme.spacing(3), + '& .MuiTypography-body1': { + marginBottom: '0 !important', + letterSpacing: '0.09px', + color: 'rgba(0, 0, 0, 0.54)', + '& strong': { + color: '#212121' + } + } + }, + pauseButton: { + display: 'flex', + width: 75, + height: 24, + '&:hover': { + cursor: 'pointer' + } + }, + disableButton: { + color: theme.palette.action.disabled + }, + headerTransactionLine: { + display: 'flex', + justifyContent: 'space-between', + flexDirection: 'column', + alignItems: 'baseline', + padding: theme.spacing(1), + [theme.breakpoints.up('md')]: { + justifyContent: 'space-between', + alignItems: 'center', + flexDirection: 'row' + } + }, + formControl: { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + width: '100%', + '& .MuiFormControl-root': { + width: 200 + }, + [theme.breakpoints.up('md')]: { + width: 300 + } + } +})