Skip to content

Commit

Permalink
improved DAO member card UX
Browse files Browse the repository at this point in the history
  • Loading branch information
NoahSaso committed Mar 27, 2024
1 parent 097066b commit 48e41ef
Show file tree
Hide file tree
Showing 15 changed files with 143 additions and 83 deletions.
4 changes: 4 additions & 0 deletions packages/config/tailwind/colors.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const colors = [
'color-light-transparent',
'color-white',
'color-brand',
'color-brand-secondary',
'color-active',
'color-error',
'color-valid',
Expand All @@ -19,6 +20,7 @@ const colors = [
'text-tertiary',
'text-quaternary',
'text-brand',
'text-brand-secondary',
'text-interactive-disabled',
'text-interactive-active',
'text-interactive-error',
Expand All @@ -35,6 +37,7 @@ const colors = [
'icon-secondary',
'icon-tertiary',
'icon-brand',
'icon-brand-secondary',
'icon-interactive-disabled',
'icon-interactive-active',
'icon-interactive-error',
Expand Down Expand Up @@ -92,6 +95,7 @@ const colors = [
'component-pill',
'component-badge-primary',
'component-badge-brand',
'component-badge-brand-secondary',
'component-badge-valid',
'component-badge-error',
].reduce(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,15 @@ export const MembersTab = () => {
votingPowerPercent,
}): StatefulDaoMemberCardProps => ({
address,
balanceLabel: t('title.staked'),
balance: {
label: t('title.staked'),
unit: '$' + governanceToken.symbol,
value: {
loading: false,
data: convertMicroDenomToDenomWithDecimals(
loading: false,
data: {
token: governanceToken,
amount: convertMicroDenomToDenomWithDecimals(
balance,
governanceToken.decimals
).toLocaleString(undefined, {
maximumFractionDigits: governanceToken.decimals,
}),
),
},
},
votingPowerPercent: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,5 +128,7 @@ export const makeManageMembersAction: ActionMaker<ManageMembersData> = ({
useDefaults,
useTransformToCosmos,
useDecodedCosmosMsg,
// Show at the top.
order: 1,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ export const MembersTab = () => {
data:
votingModule.data.members?.map(({ addr, weight }) => ({
address: addr,
balanceLabel: t('title.votingWeight'),
balance: {
label: t('title.votingWeight'),
value: {
loading: false,
data: weight.toLocaleString(),
loading: false,
data: {
amount: weight,
},
},
votingPowerPercent:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ import {
EntityDisplay,
} from '../../../../components'
import { useVotingModuleAdapterOptions } from '../../../react/context'
import { useGovernanceCollectionInfo } from '../hooks'
import { useCommonGovernanceTokenInfo } from '../hooks'

export const MembersTab = () => {
const { t } = useTranslation()
const { chainId, votingModuleAddress } = useVotingModuleAdapterOptions()
const { collectionInfo } = useGovernanceCollectionInfo()
const token = useCommonGovernanceTokenInfo()

const members = useCachedLoadingWithError(
DaoVotingCw721StakedSelectors.topStakersSelector({
Expand All @@ -33,12 +33,12 @@ export const MembersTab = () => {
votingPowerPercent,
}): StatefulDaoMemberCardProps => ({
address,
balanceLabel: t('title.staked'),
balance: {
label: t('title.staked'),
unit: '$' + collectionInfo.symbol,
value: {
loading: false,
data: count.toLocaleString(),
loading: false,
data: {
amount: count,
token,
},
},
votingPowerPercent: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,15 @@ export const MembersTab = () => {
votingPowerPercent,
}): StatefulDaoMemberCardProps => ({
address,
balanceLabel: t('title.staked'),
balance: {
label: t('title.staked'),
unit: '$' + governanceToken.symbol,
value: {
loading: false,
data: convertMicroDenomToDenomWithDecimals(
loading: false,
data: {
amount: convertMicroDenomToDenomWithDecimals(
balance,
governanceToken.decimals
).toLocaleString(undefined, {
maximumFractionDigits: governanceToken.decimals,
}),
),
token: governanceToken,
},
},
votingPowerPercent: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,15 @@ export const MembersTab = () => {
votingPowerPercent,
}): StatefulDaoMemberCardProps => ({
address,
balanceLabel: t('title.staked'),
balance: {
label: t('title.staked'),
unit: '$' + governanceToken.symbol,
value: {
loading: false,
data: convertMicroDenomToDenomWithDecimals(
loading: false,
data: {
amount: convertMicroDenomToDenomWithDecimals(
balance,
governanceToken.decimals
).toLocaleString(undefined, {
maximumFractionDigits: governanceToken.decimals,
}),
),
token: governanceToken,
},
},
votingPowerPercent: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ export const ProposalCreationForm = ({
(contribution, contributionIndex) => {
// Every other row.
const backgroundClassName =
// eslint-disable-next-line i18next/no-literal-string
contributionIndex % 2 !== 0 && 'bg-background-tertiary'

return (
Expand Down Expand Up @@ -291,6 +292,7 @@ export const ProposalCreationForm = ({
({ id, contributor, compensation }, contributionIndex) => {
// Every other row.
const backgroundClassName =
// eslint-disable-next-line i18next/no-literal-string
contributionIndex % 2 !== 0 && 'bg-background-tertiary'

const tokens = compensation.compensationPerAttribute
Expand Down
28 changes: 18 additions & 10 deletions packages/stateless/components/dao/DaoMemberCard.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ComponentMeta, ComponentStory } from '@storybook/react'

import { CHAIN_ID } from '@dao-dao/storybook'
import { EntityType } from '@dao-dao/types'
import { EntityType, TokenType } from '@dao-dao/types'
import { DaoMemberCardProps } from '@dao-dao/types/components/DaoMemberCard'

import { ButtonLink } from '../buttons'
Expand All @@ -22,16 +22,24 @@ const Template: ComponentStory<typeof DaoMemberCard> = (args) => (
export const makeProps = (): DaoMemberCardProps => ({
address: 'juno1abczhsdyechxcjz90y',
// Random number between 0 and 100,000 with 6 decimals.
balanceLabel: 'Staked',
balance: {
label: 'Staked',
unit: 'DAO',
value: {
loading: false,
data: (
Math.floor(Math.random() * (100000 * 1e6) + 1e6) / 1e6
).toLocaleString(undefined, {
maximumFractionDigits: 6,
}),
loading: false,
data: {
amount: Math.floor(Math.random() * (100000 * 1e6) + 1e6) / 1e6,
token: {
chainId: CHAIN_ID,
type: TokenType.Native,
denomOrAddress: 'udao',
decimals: 6,
symbol: 'DAO',
imageUrl: 'https://daodao.zone/daodao.png',
source: {
chainId: CHAIN_ID,
type: TokenType.Native,
denomOrAddress: 'udao',
},
},
},
},
// Random number between 0 and 31 with 2 decimals.
Expand Down
71 changes: 49 additions & 22 deletions packages/stateless/components/dao/DaoMemberCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,33 @@ import { useTranslation } from 'react-i18next'

import { EntityType } from '@dao-dao/types'
import { DaoMemberCardProps } from '@dao-dao/types/components/DaoMemberCard'
import { formatPercentOf100 } from '@dao-dao/utils'
import { formatPercentOf100, intelligentAddressConcat } from '@dao-dao/utils'

import { useDaoNavHelpers } from '../../hooks'
import { ButtonLink } from '../buttons'
import { CopyableAddress } from '../CopyableAddress'
import { CopyToClipboard } from '../CopyToClipboard'
import { ProfileImage } from '../profile'
import { TokenAmountDisplay } from '../token'

export const DaoMemberCard = ({
address,
balance,
balanceLabel,
votingPowerPercent,
loadingEntity,
}: DaoMemberCardProps) => {
const { t } = useTranslation()
const { getDaoPath } = useDaoNavHelpers()

const title = (
const loadingOrHasName =
loadingEntity.loading ||
(loadingEntity.updating && !loadingEntity.data.name) ||
loadingEntity.data.name

const title = loadingOrHasName ? (
<p
className={clsx(
'title-text text-text-body',
'title-text text-text-body !text-base',
(loadingEntity.loading || loadingEntity.updating) && 'animate-pulse'
)}
>
Expand All @@ -31,11 +38,15 @@ export const DaoMemberCard = ({
? '...'
: loadingEntity.data.name}
</p>
) : (
<p className="title-text text-text-tertiary !text-base truncate">
{intelligentAddressConcat(address)}
</p>
)

return (
<div className="flex flex-col justify-between rounded-md border border-border-primary">
<div className="flex flex-col items-center px-6 pt-10 pb-8">
<div className="flex flex-col items-center p-4 gap-2">
{/* Image */}
<ProfileImage
imageUrl={
Expand All @@ -50,7 +61,7 @@ export const DaoMemberCard = ({
/>

{/* Name */}
<div className="mt-4 mb-2 flex flex-row items-center gap-2">
<div className="flex flex-row gap-2 items-center">
{!loadingEntity.loading &&
loadingEntity.data.type === EntityType.Dao ? (
<ButtonLink href={getDaoPath(address)} size="none" variant="none">
Expand All @@ -59,20 +70,30 @@ export const DaoMemberCard = ({
) : (
title
)}
</div>

{/* Address */}
<CopyableAddress address={address} />
{!loadingEntity.loading &&
loadingEntity.data.type !== EntityType.Dao && (
<CopyToClipboard
iconClassName={
loadingOrHasName ? undefined : '!text-icon-tertiary'
}
iconSize="sm"
textClassName="hidden"
tooltip={t('button.copyAddressToClipboard')}
value={address}
/>
)}
</div>
</div>

<div className="flex flex-col gap-2 border-t border-border-interactive-disabled p-4">
{/* Voting power */}
<div className="flex flex-row flex-wrap items-center justify-between gap-x-2 gap-y-1">
<p className="secondary-text">{t('title.votingPower')}</p>
<p className="caption-text">{t('title.votingPower')}</p>

<p
className={clsx(
'body-text font-mono',
'body-text font-mono text-text-brand-secondary font-semibold',
votingPowerPercent.loading && 'animate-pulse'
)}
>
Expand All @@ -84,17 +105,23 @@ export const DaoMemberCard = ({

{/* Balance */}
<div className="flex flex-row flex-wrap items-center justify-between gap-x-2 gap-y-1">
<p className="secondary-text text-text-tertiary">{balance.label}</p>
<p
className={clsx(
'caption-text font-mono',
balance.value.loading && 'animate-pulse'
)}
>
{balance.value.loading
? '...'
: balance.value.data + (balance.unit ? ' ' + balance.unit : '')}
</p>
<p className="caption-text">{balanceLabel}</p>

<TokenAmountDisplay
amount={
balance.loading
? { loading: true }
: { loading: false, data: balance.data.amount }
}
className="caption-text font-mono"
decimals={
balance.loading || !balance.data.token
? 0
: balance.data.token.decimals
}
hideSymbol={!balance.loading && !balance.data.token}
symbol={balance.loading ? '...' : balance.data.token?.symbol || ''}
/>
</div>
</div>
</div>
Expand Down
11 changes: 6 additions & 5 deletions packages/stateless/components/dao/tabs/MembersTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ export const MembersTab = ({
<ErrorPage error={members.error} />
) : members.data.length ? (
<>
<GridCardContainer>
<GridCardContainer className="xl:grid-cols-4">
{members.data
.slice(
(membersPage - 1) * MEMBERS_PER_PAGE,
Expand Down Expand Up @@ -287,17 +287,18 @@ export const MembersTab = ({
[
'Member',
members.data.length
? members.data[0].balance.label +
(members.data[0].balance.unit
? ` (${members.data[0].balance.unit})`
? members.data[0].balanceLabel +
(!members.data[0].balance.loading &&
members.data[0].balance.data.token
? ` (${members.data[0].balance.data.token.symbol})`
: '')
: 'Balance',
'Voting power',
],
...members.data.map(
({ address, balance, votingPowerPercent }) => [
address,
balance.value.loading ? '...' : balance.value.data,
balance.loading ? '...' : balance.data.amount.toString(),
votingPowerPercent.loading ? '...' : votingPowerPercent.data,
]
),
Expand Down
Loading

0 comments on commit 48e41ef

Please sign in to comment.