Skip to content

Commit

Permalink
Address Validator dashboard QA issues (#4753)
Browse files Browse the repository at this point in the history
* fix validatorlist filter - input notification & enter on empty search box

* don't show validation message until enter key typed

* show 'All' in the filter select as default

* fix validtor card responsiveness

* fix typo 'Norminate'

* rename reward widget label - last era

* fix validator page widgets tooltips

* fix social media icons as hyperlink

* Rename `ExternalResourceLink` and type it

* add 2 decimals to apr

* fix search box not to show invalid choice until type enter

* Update packages/ui/src/memberships/constants/externalResourceLink.ts

Co-authored-by: Theophile Sandoz <theophile.sandoz@gmail.com>

* fix

* fix apr calculation

* fix

* add commission tooltip

* Update packages/ui/src/validators/hooks/useValidatorsList.tsx

Co-authored-by: Theophile Sandoz <theophile.sandoz@gmail.com>

---------

Co-authored-by: Theophile Sandoz <theophile.sandoz@gmail.com>
  • Loading branch information
eshark9312 and thesan authored Jan 31, 2024
1 parent 6c1ef8d commit 10f1ce6
Show file tree
Hide file tree
Showing 17 changed files with 115 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,16 @@ interface SearchBoxProps extends ControlProps<string> {
}
export const SearchBox = React.memo(({ value, onApply, onChange, label, displayReset }: SearchBoxProps) => {
const change = onChange && (({ target }: ChangeEvent<HTMLInputElement>) => onChange(target.value))
const isValid = () => !value || value.length === 0 || value.length > 2
const keyDown =
!isValid() || !value || !onApply ? undefined : ({ key }: React.KeyboardEvent) => key === 'Enter' && onApply()
const isValid = !value || value.length === 0 || value.length > 2
const [showInvalid, setShowInvalid] = useState(false)
useEffect(() => {
if (isValid) setShowInvalid(false)
}, [isValid])
const keyDown = ({ key }: React.KeyboardEvent) => {
if (key !== 'Enter') return
if (!isValid) return setShowInvalid(true)
onApply?.()
}
const reset =
onChange &&
onApply &&
Expand All @@ -56,8 +63,8 @@ export const SearchBox = React.memo(({ value, onApply, onChange, label, displayR
<FilterLabel>{label}</FilterLabel>
<SearchInput
inputSize={label ? 'xs' : 's'}
validation={isValid() ? undefined : 'invalid'}
message={isValid() ? '' : 'Minimum of 3 characters is required'}
validation={showInvalid ? 'invalid' : undefined}
message={showInvalid ? 'Minimum of 3 characters is required' : undefined}
>
<InputText placeholder="Search" value={value} onChange={change} onKeyDown={keyDown} />
{displayReset && value && (
Expand Down
3 changes: 3 additions & 0 deletions packages/ui/src/common/utils/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@ const mapEntries = (
transform: (x: any, key?: string | number, context?: AnyObject) => any,
context?: AnyObject
): [any, any][] => entries.map(([key, value]) => [key, transform(value, key, context)])

export const has = <K extends string | number, T extends Record<any, any>>(key: K, o: T): key is Extract<keyof T, K> =>
key in o
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
SidePaneLabel,
EmptyBody,
} from '@/common/components/SidePane'
import { has } from '@/common/utils/object'
import { ExternalResourceLink } from '@/memberships/constants'
import { useIsMyMembership } from '@/memberships/hooks/useIsMyMembership'
import { useMemberExtraInfo } from '@/memberships/hooks/useMemberExtraInfo'

Expand All @@ -37,15 +39,6 @@ export const MemberDetails = React.memo(({ member }: Props) => {
initiatingLeaving = '-',
} = useMemberExtraInfo(member)

const externalResourceLink: any = {
TELEGRAM: 'https://web.telegram.org/k/#@',
TWITTER: 'https://twitter.com/',
FACEBOOK: 'https://facebook.com/',
YOUTUBE: 'https://youtube.com/user/',
LINKEDIN: 'https://www.linkedin.com/in/',
GITHUB: 'https://github.com/',
}

if (isLoading || !memberDetails) {
return (
<EmptyBody>
Expand Down Expand Up @@ -143,9 +136,9 @@ export const MemberDetails = React.memo(({ member }: Props) => {
<SidePaneRow key={`${externalResource.source}-externalResources`}>
<SidePaneLabel text={socialTitle(externalResource.source)} />
<SidePaneText>
{externalResourceLink[externalResource.source] ? (
{has(externalResource.source, ExternalResourceLink) ? (
<Link
href={`${externalResourceLink[externalResource.source]}${externalResource.value}`}
href={`${ExternalResourceLink[externalResource.source]}${externalResource.value}`}
target="_blank"
>
{externalResource.value}
Expand Down
8 changes: 8 additions & 0 deletions packages/ui/src/memberships/constants/externalResourceLink.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const ExternalResourceLink = {
TELEGRAM: 'https://t.me/',
TWITTER: 'https://twitter.com/',
FACEBOOK: 'https://facebook.com/',
YOUTUBE: 'https://youtube.com/user/',
LINKEDIN: 'https://www.linkedin.com/in/',
GITHUB: 'https://github.com/',
} as const
1 change: 1 addition & 0 deletions packages/ui/src/memberships/constants/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './email'
export * from './externalResourceLink'
10 changes: 6 additions & 4 deletions packages/ui/src/validators/components/ValidatorInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import styled from 'styled-components'

import { CopyComponent } from '@/common/components/CopyComponent'
import { DiscordIcon, TelegramIcon, TwitterIcon } from '@/common/components/icons/socials'
import { Link } from '@/common/components/Link'
import { DefaultTooltip, Tooltip } from '@/common/components/Tooltip'
import { BorderRad, Colors, Transitions } from '@/common/constants'
import { shortenAddress } from '@/common/model/formatters'
import { Address } from '@/common/types'
import { MemberIcons } from '@/memberships/components'
import { Avatar } from '@/memberships/components/Avatar'
import { ExternalResourceLink } from '@/memberships/constants'
import { MemberWithDetails } from '@/memberships/types'

interface ValidatorInfoProps {
Expand Down Expand Up @@ -39,18 +41,18 @@ export const ValidatorInfo = React.memo(({ address, member, size = 's' }: Valida
{(twitter || telegram || discord) && (
<MemberIcons>
{twitter && (
<Tooltip tooltipText={twitter.value}>
<Link onClick={(e) => e.stopPropagation()} href={`${ExternalResourceLink.TWITTER}${twitter.value}`}>
<SocialTooltip>
<TwitterIcon />
</SocialTooltip>
</Tooltip>
</Link>
)}
{telegram && (
<Tooltip tooltipText={telegram.value}>
<Link onClick={(e) => e.stopPropagation()} href={`${ExternalResourceLink.TELEGRAM}${telegram.value}`}>
<SocialTooltip>
<TelegramIcon />
</SocialTooltip>
</Tooltip>
</Link>
)}
{discord && (
<Tooltip tooltipText={discord.value}>
Expand Down
28 changes: 20 additions & 8 deletions packages/ui/src/validators/components/ValidatorsFilter.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React, { useEffect, useState } from 'react'
import styled from 'styled-components'

import { FilterBox } from '@/common/components/forms/FilterBox'
import { InputNotification } from '@/common/components/forms'
import { Fields, FilterBox } from '@/common/components/forms/FilterBox'
import { SearchBox } from '@/common/components/forms/FilterBox/FilterSearchBox'
import { FilterSelect } from '@/common/components/selects'

Expand All @@ -24,8 +25,8 @@ export const ValidatorsFilter = ({ filter }: ValidatorFilterProps) => {
const display = () => filter.setSearch(search)

const { isVerified, isActive } = filter
const verificationValue = isVerified === true ? 'verified' : isVerified === false ? 'unverified' : undefined
const stateValue = isActive === true ? 'active' : isActive === false ? 'waiting' : undefined
const verificationValue = isVerified === true ? 'verified' : isVerified === false ? 'unverified' : null
const stateValue = isActive === true ? 'active' : isActive === false ? 'waiting' : null

const clear =
filter.search || verificationValue || stateValue
Expand All @@ -37,8 +38,8 @@ export const ValidatorsFilter = ({ filter }: ValidatorFilterProps) => {
: undefined

return (
<FilterBox onClear={clear}>
<Fields>
<ValidatorFilterBox onClear={clear}>
<ResponsiveWrapper>
<SelectFields>
<FilterSelect
title="Verification"
Expand All @@ -54,11 +55,17 @@ export const ValidatorsFilter = ({ filter }: ValidatorFilterProps) => {
/>
</SelectFields>
<SearchBox label="Search" value={search} onApply={display} onChange={setSearch} />
</Fields>
</FilterBox>
</ResponsiveWrapper>
</ValidatorFilterBox>
)
}

const ValidatorFilterBox = styled(FilterBox)`
${Fields} {
padding-bottom: 22px;
}
`

const SelectFields = styled.div`
display: flex;
justify-content: flex-start;
Expand All @@ -74,11 +81,16 @@ const SelectFields = styled.div`
}
}
`
const Fields = styled.div`
const ResponsiveWrapper = styled.div`
display: flex;
justify-content: space-between;
gap: 8px;
${InputNotification} {
top: unset;
bottom: 2px;
}
@media (max-width: 767px) {
flex-direction: column;
}
Expand Down
5 changes: 4 additions & 1 deletion packages/ui/src/validators/components/ValidatorsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export const ValidatorsList = ({ validators, eraIndex, order, pagination }: Vali
The APR is subject to the amount staked and have a diminishing return for higher token amounts. This
is calculated as follow:
<br />
<code>Yearly Reward * Commission / Stake</code>
<code>Yearly Reward * (1 - Commission) / Stake</code>
<dl>
<dt>Reward:</dt>
<dd>Average reward generated (during the last 30 days) extrapolated over a year.</dd>
Expand All @@ -88,6 +88,9 @@ export const ValidatorsList = ({ validators, eraIndex, order, pagination }: Vali
isDescending={order.isDescending}
>
Commission
<Tooltip tooltipText={<p>The validator commission on the nominators rewards</p>}>
<TooltipDefault />
</Tooltip>
</SortHeader>
</ListHeaders>
{!validators ? (
Expand Down
20 changes: 10 additions & 10 deletions packages/ui/src/validators/components/statistics/Era.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
import { DurationValue } from '@/common/components/typography/DurationValue'
import { ERA_DURATION } from '@/common/constants'
import { MILLISECONDS_PER_BLOCK } from '@/common/model/formatters'
import { whenDefined } from '@/common/utils'
import { isDefined, whenDefined } from '@/common/utils'

interface EraProps {
eraStartedOn: number | undefined
Expand Down Expand Up @@ -44,23 +44,23 @@ export const Era = ({ eraStartedOn }: EraProps) => {
tooltipText="One era consists of 6 epochs with 1 hour duration each."
tooltipTitle="Era"
tooltipLinkText="What is an era"
actionElement={<PercentageChart percentage={percentage ?? 0} small />}
actionElement={isDefined(percentage) && <PercentageChart percentage={percentage} small />}
>
<StatisticItemSpacedContent>
<StatisticLabel>Next Reward</StatisticLabel>
<div>
<DurationValue value={formatDurationDate(nextReward ?? 0)} />
</div>
<div>{isDefined(nextReward) && <DurationValue value={formatDurationDate(nextReward)} />}</div>
</StatisticItemSpacedContent>
<StatisticItemSpacedContent>
<StatisticLabel>Blocks / Points</StatisticLabel>
<div>
{blocks && (
<NumericValue>
<NumericValue>
{blocks ? (
<>
<BlockIcon /> {blocks} / {blocks * POINTS_PER_BLOCK}
</NumericValue>
</>
) : (
'- / -'
)}
</div>
</NumericValue>
</StatisticItemSpacedContent>
</StatisticItem>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const Rewards = ({ totalRewards, lastRewards }: RewardsProps) => {
<TokenValue size="l" value={totalRewards} />
</StatisticItemSpacedContent>
<StatisticItemSpacedContent>
<StatisticLabel> Last </StatisticLabel>
<StatisticLabel>Last Era</StatisticLabel>
<TokenValue size="l" value={lastRewards} />
</StatisticItemSpacedContent>
</StatisticItem>
Expand Down
8 changes: 4 additions & 4 deletions packages/ui/src/validators/components/statistics/Staking.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ export const Staking = ({ idealStaking, currentStaking, stakingPercentage }: Sta
return (
<StatisticItem
title="staking"
tooltipText="tooltip text..."
tooltipTitle="staking tooltip title"
tooltipLinkText="link..."
tooltipLinkURL="#"
tooltipText="The ideal staking amount is 50% of the total tokens."
tooltipTitle="Staking Amount"
tooltipLinkText="Handbook"
tooltipLinkURL="https://handbook.joystream.org/system/accounts-and-staking"
actionElement={Percentage}
>
<StatisticItemSpacedContent>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,21 @@ export const ValidatorsState = ({
allNominatorsCount,
}: ValidatorsStateProps) => {
return (
<StatisticItem
title="validators and Nominators"
tooltipText="tooltip text..."
tooltipTitle="Validators State"
tooltipLinkText="link..."
tooltipLinkURL="#"
>
<StatisticItem title="validators and Nominators">
<StatisticItemSpacedContent>
<StatisticLabel>Validator (Active / Waiting)</StatisticLabel>
<NumericValue>
{activeValidatorsCount} / {allValidatorsCount - activeValidatorsCount}
{`${activeValidatorsCount > 0 ? activeValidatorsCount : '-'} / ${
allValidatorsCount && activeValidatorsCount ? allValidatorsCount - activeValidatorsCount : '-'
}`}
</NumericValue>
</StatisticItemSpacedContent>
<StatisticItemSpacedContent>
<StatisticLabel>Nominator (Active / Total)</StatisticLabel>
<NumericValue>
{activeNominatorsCount} / {allNominatorsCount}
{`${activeNominatorsCount > 0 ? activeValidatorsCount : '-'} / ${
activeNominatorsCount && allNominatorsCount ? allNominatorsCount : '-'
}`}
</NumericValue>
</StatisticItemSpacedContent>
</StatisticItem>
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/validators/hooks/useValidatorsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ValidatorsContext } from '../providers/context'
import { ValidatorDetailsOrder } from '../types'

const VALIDATOR_PER_PAGE = 7
const DESCENDING_KEYS: ValidatorDetailsOrder['key'][] = ['apr', 'commission']
const DESCENDING_KEYS: ValidatorDetailsOrder['key'][] = ['apr']

export const useValidatorsList = () => {
const [search, setSearch] = useState('')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const NominatingRedirectModal = () => {
size="medium"
href="https://polkadot.js.org/apps/?rpc=wss://rpc.joystream.org:9944#/staking/actions"
>
Norminate <ArrowRightIcon white />
Nominate <ArrowRightIcon white />
</LinkPrimary>
</ModalFooter>
</Modal>
Expand Down
12 changes: 8 additions & 4 deletions packages/ui/src/validators/modals/validatorCard/Nominators.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ export const Nominators = ({ validator }: Props) => {
<NominatorList>
{validator.staking?.nominators?.map(({ address, staking }, index) => (
<ListItem key={index} borderless>
<ValidatorItemWrap>
<NominatorItemWrap>
<UnknownAccountInfo address={address} placeholderName="Nominator account" />
<TokenValue size="xs" value={staking} />
</ValidatorItemWrap>
</NominatorItemWrap>
</ListItem>
))}
</NominatorList>
Expand All @@ -51,10 +51,9 @@ const Title = styled.h4`
const NominatorList = styled(List)`
gap: 8px;
`
const ValidatorItemWrap = styled.div`
const NominatorItemWrap = styled.div`
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr;
justify-content: space-between;
justify-items: end;
align-items: center;
Expand All @@ -65,6 +64,11 @@ const ValidatorItemWrap = styled.div`
cursor: pointer;
transition: ${Transitions.all};
${TableListItemAsLinkHover}
@media (max-width: 424px) {
grid-gap: 8px;
grid-template-columns: 1fr;
}
`
const ListHeaders = styled.div`
display: grid;
Expand Down
Loading

0 comments on commit 10f1ce6

Please sign in to comment.