Skip to content
This repository has been archived by the owner on Nov 10, 2023. It is now read-only.

Commit

Permalink
feat: Dashboard grid layout (#3795)
Browse files Browse the repository at this point in the history
* wip layout

* feat: Adjust Dashboard layout

* fix: remove leftover prop

* fix: NFT route, style container spacings

* style: Adjust Overview widget style

* style: Remove row, col from featured apps widget, adjust spacings

* Add total transactions to sign to the PendingTxs widget title

* Add view all Link

* tune spacing in TxPendingListItem

* fix: Remove hardcoded featured app ids, use lodash sampleSize to get random apps, adjust grid layout

* fix: React prop errors

* style: Adjust overview skeleton container size

* style: Adjust pending txs spacing

* tweaks in grid layout

* fix: Update comment

Co-authored-by: Diogo Soares <[email protected]>
  • Loading branch information
usame-algan and Diogo Soares authored Apr 20, 2022
1 parent 2686fa6 commit b2836bd
Show file tree
Hide file tree
Showing 13 changed files with 375 additions and 246 deletions.
2 changes: 1 addition & 1 deletion src/components/AppLayout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ const ContentWrapper = styled.div`
flex-direction: column;
overflow-x: auto;
padding: 0 16px;
padding: 24px 40px;
> :nth-child(1) {
flex-grow: 1;
Expand Down
38 changes: 20 additions & 18 deletions src/components/Dashboard/CreateSafe.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { ReactElement } from 'react'
import styled from 'styled-components'
import { Button, Title, Text } from '@gnosis.pm/safe-react-components'
import { Button, Text } from '@gnosis.pm/safe-react-components'
import Link from 'src/components/layout/Link'
import Track from 'src/components/Track'
import { OPEN_SAFE_ROUTE } from 'src/routes/routes'
import { CREATE_SAFE_EVENTS } from 'src/utils/events/createLoadSafe'
import { Card, WidgetBody, WidgetContainer, WidgetTitle } from 'src/components/Dashboard/styled'

export const CardContentContainer = styled.div`
display: flex;
Expand All @@ -19,24 +20,25 @@ export const CardDescriptionContainer = styled.div`

const CreateSafeWidget = (): ReactElement => {
return (
<CardContentContainer>
<Title size="sm" strong withoutMargin>
Create Safe
</Title>
<WidgetContainer>
<WidgetTitle>Create Safe</WidgetTitle>
<WidgetBody>
<Card>
<CardDescriptionContainer>
<Text size="xl">Create a new Safe that is controlled by one or multiple owners.</Text>
<Text size="xl">You will be required to pay a network fee for creating your new Safe.</Text>
</CardDescriptionContainer>

<CardDescriptionContainer>
<Text size="xl">Create a new Safe that is controlled by one or multiple owners.</Text>
<Text size="xl">You will be required to pay a network fee for creating your new Safe.</Text>
</CardDescriptionContainer>

<Track {...CREATE_SAFE_EVENTS.CREATE_BUTTON}>
<Button size="lg" color="primary" variant="contained" component={Link} to={OPEN_SAFE_ROUTE}>
<Text size="xl" color="white">
+ Create new Safe
</Text>
</Button>
</Track>
</CardContentContainer>
<Track {...CREATE_SAFE_EVENTS.CREATE_BUTTON}>
<Button size="lg" color="primary" variant="contained" component={Link} to={OPEN_SAFE_ROUTE}>
<Text size="xl" color="white">
+ Create new Safe
</Text>
</Button>
</Track>
</Card>
</WidgetBody>
</WidgetContainer>
)
}

Expand Down
73 changes: 37 additions & 36 deletions src/components/Dashboard/FeaturedApps/FeaturedApps.tsx
Original file line number Diff line number Diff line change
@@ -1,64 +1,65 @@
import { ReactElement, useMemo } from 'react'
import { useAppList } from 'src/routes/safe/components/Apps/hooks/appList/useAppList'
import { Text } from '@gnosis.pm/safe-react-components'
import { Link } from 'react-router-dom'
import { getSafeAppUrl, SafeRouteParams } from 'src/routes/routes'
import { useSelector } from 'react-redux'
import { Box, Grid } from '@material-ui/core'

import styled from 'styled-components'
import { getSafeAppUrl, SafeRouteParams } from 'src/routes/routes'
import { currentSafe } from 'src/logic/safe/store/selectors'
import { getShortName } from 'src/config'
import { ReactElement, useMemo } from 'react'
import Row from 'src/components/layout/Row'
import Col from 'src/components/layout/Col'
import styled from 'styled-components'
import { Card, WidgetBody, WidgetContainer, WidgetTitle } from 'src/components/Dashboard/styled'

const FEATURED_APPS_TAGS = 'dashboard-widgets'
export const FEATURED_APPS_TAG = 'dashboard-widgets'

const StyledImage = styled.img`
max-width: 64px;
max-height: 64px;
width: 64px;
height: 64px;
`

const StyledLink = styled(Link)`
margin-top: 10px;
text-decoration: none;
`

const StyledRow = styled(Row)`
gap: 24px;
flex-wrap: inherit;
`

export const FeaturedApps = (): ReactElement => {
const { allApps } = useAppList()
const { address } = useSelector(currentSafe) ?? {}
const featuredApps = useMemo(() => allApps.filter((app) => app.tags?.includes(FEATURED_APPS_TAGS)), [allApps])
const featuredApps = useMemo(() => allApps.filter((app) => app.tags?.includes(FEATURED_APPS_TAG)), [allApps])

const routesSlug: SafeRouteParams = {
shortName: getShortName(),
safeAddress: address,
}

return (
<>
{featuredApps.map((app) => {
const appRoute = getSafeAppUrl(app.url, routesSlug)
return (
<StyledRow key={app.id} margin="lg">
<Col xs={2}>
<StyledImage src={app.iconUrl} alt={app.name} />
</Col>
<Col xs={10} layout="column">
<Text size="lg" strong>
{app.description}
</Text>
<StyledLink to={appRoute}>
<Text color="primary" size="lg" strong>
Use {app.name}
</Text>
</StyledLink>
</Col>
</StyledRow>
)
})}
</>
<WidgetContainer>
<WidgetTitle>Safe Apps</WidgetTitle>
<WidgetBody>
{featuredApps.map((app) => {
const appRoute = getSafeAppUrl(app.url, routesSlug)
return (
<Card key={app.id}>
<Grid container alignItems="center" spacing={3}>
<Grid item xs={3} md={2}>
<StyledImage src={app.iconUrl} alt={app.name} />
</Grid>
<Grid item xs={9} md={10}>
<Box mb={1}>
<Text size="xl">{app.description}</Text>
</Box>
<StyledLink to={appRoute}>
<Text color="primary" size="lg" strong>
Use {app.name}
</Text>
</StyledLink>
</Grid>
</Grid>
</Card>
)
})}
</WidgetBody>
</WidgetContainer>
)
}
163 changes: 107 additions & 56 deletions src/components/Dashboard/Overview/Overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,21 @@ import { ReactElement } from 'react'
import { useSelector } from 'react-redux'
import styled from 'styled-components'
import { Text, Identicon } from '@gnosis.pm/safe-react-components'
import { useHistory } from 'react-router-dom'
import { Box, Grid } from '@material-ui/core'
import { Skeleton } from '@material-ui/lab'

import { currentSafeLoaded, currentSafeWithNames } from 'src/logic/safe/store/selectors'
import PrefixedEthHashInfo from 'src/components/PrefixedEthHashInfo'
import Row from 'src/components/layout/Row'
import Col from 'src/components/layout/Col'
import { primaryLite, primaryActive, smallFontSize, md } from 'src/theme/variables'
import { primaryLite, primaryActive, smallFontSize, md, lg } from 'src/theme/variables'
import NetworkLabel from 'src/components/NetworkLabel/NetworkLabel'
import { nftLoadedSelector, nftTokensSelector } from 'src/logic/collectibles/store/selectors'
import { Skeleton } from '@material-ui/lab'
import { Card, DashboardTitle } from 'src/components/Dashboard/styled'
import { WidgetBody, WidgetContainer } from 'src/components/Dashboard/styled'
import Button from 'src/components/layout/Button'
import { generateSafeRoute, SAFE_ROUTES } from 'src/routes/routes'
import { currentChainId } from 'src/logic/config/store/selectors'
import { getChainById } from 'src/config'

const IdenticonContainer = styled.div`
position: relative;
Expand All @@ -34,79 +40,124 @@ const SafeThreshold = styled.div`
`

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

const NetworkLabelContainer = styled.div`
position: absolute;
top: 0;
right: 0;
top: ${lg};
right: ${lg};
& span {
bottom: auto;
}
`

const Container = styled.div`
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100%;
position: relative;
`

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

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

<Box mb={2}>
<Text size="xl" strong>
<Skeleton variant="text" height={28} />
</Text>
<Skeleton variant="text" height={21} />
</Box>
<NetworkLabelContainer>
<Skeleton variant="text" width="80px" />
</NetworkLabelContainer>
</Grid>
</Grid>
<Grid container>
<Grid item xs={3}>
<Text color="inputDefault" size="lg">
Tokens
</Text>
<StyledText size="xl">{ValueSkeleton}</StyledText>
</Grid>
<Grid item xs={3}>
<Text color="inputDefault" size="lg">
NFTs
</Text>
<StyledText size="xl">{ValueSkeleton}</StyledText>
</Grid>
</Grid>
</Card>
)

const Overview = (): ReactElement => {
const { address, name, owners, threshold, balances } = useSelector(currentSafeWithNames)
const chainId = useSelector(currentChainId)
const { shortName } = getChainById(chainId)
const loaded = useSelector(currentSafeLoaded)
const nftTokens = useSelector(nftTokensSelector)
const nftLoaded = useSelector(nftLoadedSelector)
const history = useHistory()

const handleOpenAssets = (): void => {
history.push(generateSafeRoute(SAFE_ROUTES.ASSETS_BALANCES, { safeAddress: address, shortName }))
}

return (
<Container>
<Row margin="md">
<Col layout="column">
<IdenticonContainer>
{loaded ? (
<>
<SafeThreshold>
{threshold}/{owners.length}
</SafeThreshold>
<Identicon address={address} size="lg" />
</>
) : (
<Skeleton variant="circle" width="40px" height="40px" />
)}
</IdenticonContainer>
<Text size="xl" strong>
{loaded ? name : <Skeleton variant="text" />}
</Text>
{loaded ? <PrefixedEthHashInfo hash={address} textSize="lg" /> : <Skeleton variant="text" />}
</Col>
<Col end="xs">
<NetworkLabelContainer>
<NetworkLabel />
</NetworkLabelContainer>
</Col>
</Row>
<Row>
<Col layout="column" xs={3}>
<Text color="inputDefault" size="md">
Tokens
</Text>
<StyledText size="xl">{loaded ? balances.length : ValueSkeleton}</StyledText>
</Col>
<Col layout="column" xs={3}>
<Text color="inputDefault" size="md">
NFTs
</Text>
{nftTokens && <StyledText size="xl">{nftLoaded ? nftTokens.length : ValueSkeleton}</StyledText>}
</Col>
</Row>
</Container>
<WidgetContainer>
<DashboardTitle>Dashboard</DashboardTitle>
<WidgetBody>
{!loaded ? (
SkeletonOverview
) : (
<Card>
<Grid container>
<Grid item xs={12}>
<IdenticonContainer>
<SafeThreshold>
{threshold}/{owners.length}
</SafeThreshold>
<Identicon address={address} size="xl" />
</IdenticonContainer>
<Box mb={2} overflow="hidden">
<Text size="xl" strong>
{name}
</Text>
<PrefixedEthHashInfo hash={address} textSize="xl" textColor="placeHolder" />
</Box>
<NetworkLabelContainer>
<NetworkLabel />
</NetworkLabelContainer>
</Grid>
</Grid>
<Grid container>
<Grid item xs={3}>
<Text color="inputDefault" size="lg">
Tokens
</Text>
<StyledText size="xl">{balances.length}</StyledText>
</Grid>
<Grid item xs={3}>
<Text color="inputDefault" size="lg">
NFTs
</Text>
{nftTokens && <StyledText size="xl">{nftLoaded ? nftTokens.length : ValueSkeleton}</StyledText>}
</Grid>
<Grid item xs={6}>
<Box display="flex" height={1} alignItems="flex-end" justifyContent="flex-end">
<Button size="medium" variant="contained" color="primary" onClick={handleOpenAssets}>
View Assets
</Button>
</Box>
</Grid>
</Grid>
</Card>
)}
</WidgetBody>
</WidgetContainer>
)
}

Expand Down
Loading

0 comments on commit b2836bd

Please sign in to comment.