Skip to content

Commit

Permalink
fix: Adjust overview widget on dashboard for new design (#2782)
Browse files Browse the repository at this point in the history
* fix: Adjust overview widget on dashboard for new design

* fix: Adjust dashboard e2e test

* fix: Icon colors in dark mode

* fix: Loading state and mobile view

* fix: Mobile button alignment
  • Loading branch information
usame-algan authored Nov 10, 2023
1 parent a9ce0c1 commit 5399c1a
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 91 deletions.
9 changes: 5 additions & 4 deletions cypress/e2e/pages/dashboard.pages.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ const transactionQueueStr = 'Pending transactions'
const noTransactionStr = 'This Safe has no queued transactions'
const overviewStr = 'Overview'
const viewAssetsStr = 'View assets'
const sendStr = 'Send'
const receiveStr = 'Receive'
const tokensStr = 'Tokens'
const nftStr = 'NFTs'
const viewAllStr = 'View all'
Expand All @@ -27,11 +29,10 @@ export function verifyOverviewWidgetData() {

cy.get('@overviewSection').within(() => {
// Prefix is separated across elements in EthHashInfo
cy.contains(constants.SEPOLIA_TEST_SAFE_5).should('exist')
cy.get('h2').contains('Overview')
cy.get(`a[href="${constants.BALANCE_URL}${encodeURIComponent(constants.SEPOLIA_TEST_SAFE_5)}"]`).contains(
viewAssetsStr,
)
cy.get(`a[href="${constants.BALANCE_URL}${encodeURIComponent(constants.SEPOLIA_TEST_SAFE_5)}"]`).contains('Tokens')
cy.get('button').contains(sendStr)
cy.get('button').contains(receiveStr)
})
}

Expand Down
3 changes: 3 additions & 0 deletions public/images/common/arrow-se.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions public/images/common/arrow-top-right.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
241 changes: 154 additions & 87 deletions src/components/dashboard/Overview/Overview.tsx
Original file line number Diff line number Diff line change
@@ -1,92 +1,94 @@
import QrCodeButton from '@/components/sidebar/QrCodeButton'
import { TxModalContext } from '@/components/tx-flow'
import NewTxMenu from '@/components/tx-flow/flows/NewTx'
import { useRemoteSafeApps } from '@/hooks/safe-apps/useRemoteSafeApps'
import { OVERVIEW_EVENTS, trackEvent } from '@/services/analytics'
import { useAppSelector } from '@/store'
import { selectCurrency } from '@/store/settingsSlice'
import { formatCurrency } from '@/utils/formatNumber'
import type { ReactElement } from 'react'
import { useMemo } from 'react'
import { useContext, useMemo } from 'react'
import { useRouter } from 'next/router'
import Link from 'next/link'
import styled from '@emotion/styled'
import { Box, Button, Grid, Skeleton, Typography } from '@mui/material'
import { Box, Button, Divider, Grid, Skeleton, Typography } from '@mui/material'
import { Card, WidgetBody, WidgetContainer } from '../styled'
import useSafeInfo from '@/hooks/useSafeInfo'
import { useCurrentChain } from '@/hooks/useChains'
import SafeIcon from '@/components/common/SafeIcon'
import ChainIndicator from '@/components/common/ChainIndicator'
import EthHashInfo from '@/components/common/EthHashInfo'
import { AppRoutes } from '@/config/routes'
import useSafeAddress from '@/hooks/useSafeAddress'
import useCollectibles from '@/hooks/useCollectibles'
import type { UrlObject } from 'url'
import { useVisibleBalances } from '@/hooks/useVisibleBalances'
import AddIcon from '@mui/icons-material/Add'
import ArrowIconNW from '@/public/images/common/arrow-top-right.svg'
import ArrowIconSE from '@/public/images/common/arrow-se.svg'

const IdenticonContainer = styled.div`
position: relative;
margin-bottom: var(--space-2);
`

const StyledText = styled(Typography)`
margin-top: 8px;
font-size: 24px;
font-weight: bold;
`

const NetworkLabelContainer = styled.div`
position: absolute;
top: var(--space-3);
right: var(--space-3);
& span {
bottom: auto;
}
`

const ValueSkeleton = () => <Skeleton variant="text" width={30} />
const ValueSkeleton = () => <Skeleton variant="text" width={20} />

const SkeletonOverview = (
<Card>
<Grid container>
<Grid item xs={12}>
<IdenticonContainer>
<Skeleton variant="circular" width="48px" height="48px" />
</IdenticonContainer>

<Box my={4}>
<Typography fontSize="lg">
<Skeleton variant="text" height={28} />
</Typography>
<Skeleton variant="text" height={21} />
<Box mb={2}>
<Skeleton variant="text" width={100} height={21} />
<Skeleton variant="text" width={120} height={40} />
</Box>
<NetworkLabelContainer>

<Box position="absolute" right="24px" top="24px">
<Skeleton variant="text" width="80px" />
</NetworkLabelContainer>
</Box>

<Divider sx={{ mb: '12px' }} />
</Grid>
</Grid>
<Grid container>
<Grid item xs={3}>
<Typography color="border.main" variant="body2">
Tokens
<Grid item container xs={6} gap={2}>
<Typography
color="border.main"
variant="caption"
fontWeight="bold"
display="flex"
gap={1}
textTransform="uppercase"
>
<ValueSkeleton /> Tokens
</Typography>
<StyledText fontSize="lg">
<ValueSkeleton />
</StyledText>
</Grid>
<Grid item xs={3}>
<Typography color="border.main" variant="body2">
NFTs
<Typography
color="border.main"
variant="caption"
fontWeight="bold"
display="flex"
gap={1}
textTransform="uppercase"
>
<ValueSkeleton /> NFTs
</Typography>
<StyledText fontSize="lg">
<ValueSkeleton />
</StyledText>
</Grid>
</Grid>
<Grid container mt={3} gap={1}>
<Skeleton variant="rounded" width="115px" height="40px" />
<Skeleton variant="rounded" width="115px" height="40px" />
</Grid>
</Card>
)

const Overview = (): ReactElement => {
const currency = useAppSelector(selectCurrency)
const router = useRouter()
const safeAddress = useSafeAddress()
const { safe, safeLoading, safeLoaded } = useSafeInfo()
const { safeLoading, safeLoaded } = useSafeInfo()
const { balances, loading: balancesLoading } = useVisibleBalances()
const [nfts] = useCollectibles()
const [nfts, , nftsLoading] = useCollectibles()
const chain = useCurrentChain()
const { chainId } = chain || {}
const { setTxFlow } = useContext(TxModalContext)
const [apps] = useRemoteSafeApps()

const fiatTotal = useMemo(
() => (balances.fiatTotal ? formatCurrency(balances.fiatTotal, currency) : ''),
[currency, balances.fiatTotal],
)

const rampSafeApp = apps?.find((app) => app.name === 'Ramp Network')

const assetsLink: UrlObject = {
pathname: AppRoutes.balances.index,
Expand All @@ -102,6 +104,12 @@ const Overview = (): ReactElement => {
const nftsCount = useMemo(() => (nfts ? `${nfts.next ? '>' : ''}${nfts.results.length}` : ''), [nfts])

const isInitialState = !safeLoaded && !safeLoading
const isLoading = safeLoading || balancesLoading || nftsLoading || isInitialState

const handleOnSend = () => {
setTxFlow(<NewTxMenu />, undefined, false)
trackEvent(OVERVIEW_EVENTS.NEW_TRANSACTION)
}

return (
<WidgetContainer>
Expand All @@ -110,56 +118,115 @@ const Overview = (): ReactElement => {
</Typography>

<WidgetBody>
{safeLoading || isInitialState ? (
{isLoading ? (
SkeletonOverview
) : (
<Card>
<Card style={{ display: 'flex', flexDirection: 'column' }}>
<Grid container pb={2}>
<SafeIcon address={safeAddress} threshold={safe.threshold} owners={safe.owners.length} size={48} />

<Grid item>
<Typography variant="body2" color="primary.light">
Total asset value
</Typography>
<Typography variant="h2">{fiatTotal}</Typography>
</Grid>
<Grid item xs />

<Grid item>
<ChainIndicator chainId={chainId} inline />
</Grid>
</Grid>

<Box mt={2} mb={4}>
{safeAddress ? (
<EthHashInfo showAvatar={false} address={safeAddress} shortAddress={false} showCopyButton hasExplorer />
) : (
<Skeleton />
)}
</Box>
<Divider />

<Grid container>
<Grid item xs={3}>
<Grid container pt="12px">
<Grid item container xs={6} gap={1}>
<Link href={assetsLink} passHref>
<Typography color="border.main" variant="body2">
Tokens
</Typography>
<StyledText fontSize="lg">{balancesLoading ? <ValueSkeleton /> : tokenCount}</StyledText>
<Box
sx={{
display: 'flex',
borderRight: '1px solid',
borderColor: ({ palette }) => palette.border.light,
gap: 0.5,
pr: 1,
}}
>
<Typography variant="caption" component="span">
{balancesLoading ? <ValueSkeleton /> : tokenCount}
</Typography>{' '}
<Typography
variant="caption"
component="span"
color="border.main"
textTransform="uppercase"
fontWeight="bold"
>
Tokens
</Typography>
</Box>
</Link>
</Grid>

<Grid item xs={3}>
<Link href={nftsLink} passHref>
<Typography color="border.main" variant="body2">
NFTs
</Typography>
<StyledText fontSize="lg">{nftsCount || <ValueSkeleton />}</StyledText>
<Box display="flex" pr={1} gap={0.5}>
<Typography variant="caption" component="span">
{nftsCount || <ValueSkeleton />}
</Typography>{' '}
<Typography
variant="caption"
component="span"
color="border.main"
textTransform="uppercase"
fontWeight="bold"
>
NFTs
</Typography>
</Box>
</Link>
</Grid>
<Grid item xs />

<Grid item>
<Box display="flex" height={1} alignItems="flex-end" justifyContent="flex-end">
<Link href={assetsLink} passHref legacyBehavior>
<Button size="medium" variant="contained" color="primary">
View assets
</Grid>
<Grid item mt="auto">
<Grid container mt={3} spacing={1} flexWrap="wrap">
{rampSafeApp && (
<Grid item xs={12} sm="auto">
<Link
href={{
pathname: AppRoutes.apps.open,
query: { safe: router.query.safe, appUrl: rampSafeApp.url },
}}
passHref
legacyBehavior
>
<Button
size="small"
variant="contained"
color="primary"
startIcon={<AddIcon />}
sx={{ height: 1 }}
fullWidth
>
Buy crypto
</Button>
</Link>
</Grid>
)}
<Grid item xs={6} sm="auto">
<Button
onClick={handleOnSend}
size="small"
variant="outlined"
color="primary"
startIcon={<ArrowIconNW />}
fullWidth
>
Send
</Button>
</Grid>
<Grid item xs={6} sm="auto">
<QrCodeButton>
<Button size="small" variant="outlined" color="primary" startIcon={<ArrowIconSE />} fullWidth>
Receive
</Button>
</Link>
</Box>
</QrCodeButton>
</Grid>
</Grid>
</Grid>
</Card>
Expand Down

0 comments on commit 5399c1a

Please sign in to comment.