Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve envelope visualization #94

Merged
merged 10 commits into from
Aug 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
199 changes: 138 additions & 61 deletions src/components/Envelope/Detail.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,32 @@
import voteImage from '/images/vocdoni-vote.png'
import { Flex, Heading, Image, Link, Text } from '@chakra-ui/react'
import { VoteInfoResponse } from '@vocdoni/sdk'
import { Flex, Image, Link, Tab, TabList, TabPanel, TabPanels, Text } from '@chakra-ui/react'
import { Envelope, VotePackageType } from '@vocdoni/chakra-components'
import { ElectionProvider, useElection } from '@vocdoni/react-providers'
import { PublishedElection, VoteInfoResponse } from '@vocdoni/sdk'
import { formatDistance } from 'date-fns'
import { Trans, useTranslation } from 'react-i18next'
import { generatePath, Link as RouterLink } from 'react-router-dom'
import { CopyButton } from '~components/Layout/CopyButton'
import ShowRawButton from '~components/Layout/ShowRawButton'
import { CopyButton, ReducedTextAndCopy } from '~components/Layout/CopyButton'
import { DetailsGrid, GridItemProps } from '~components/Layout/DetailsGrid'
import { RouteParamsTabs } from '~components/Layout/RouteParamsTabs'
import { RawContentBox } from '~components/Layout/ShowRawButton'
import { processIdGridItem } from '~components/Transactions/TxDetails/SpecificTxDetails'
import { RoutePath } from '~constants'
import voteImage from '/images/vocdoni-vote.png'

const EnvelopeDetail = (envelope: VoteInfoResponse) => {
/**
* Show envelope content
* @param route this is needed to use the RoutedParamsTabs
* @param envelope envelope data
* @constructor
*/
const EnvelopeDetail = ({
route,
...envelope
}: { route: RoutePath.Envelope | RoutePath.Verify } & VoteInfoResponse) => {
const { t } = useTranslation()
const emitted = formatDistance(new Date(envelope.date), new Date(), { addSuffix: true })
const encKeys = envelope.encryptionKeys?.join(',')

return (
<Flex direction={'column'} mt={'40px'} gap={6} wordBreak='break-all'>
<Heading isTruncated wordBreak='break-word'>
<Trans i18nKey={'envelopes.envelope_detail'}>Envelope Detail</Trans>
</Heading>
<Flex direction={'column'} alignItems={'center'} gap={6}>
<Image w={'100px'} src={voteImage} alt={t('envelopes.vote_registered')} />
<Text fontWeight={'bold'} fontSize={'xl'}>
Expand All @@ -38,58 +47,126 @@ const EnvelopeDetail = (envelope: VoteInfoResponse) => {
</CopyButton>
</Flex>
</Flex>
<Flex direction={'column'} gap={3}>
<Text>
<Trans i18nKey={'envelopes.emitted'} values={{ emitted: emitted }}>
Emitted {{ emitted: emitted }}
</Trans>
</Text>
{encKeys && encKeys?.length > 0 && (
<Text>
<Trans i18nKey={'envelopes.encryption_keys'} values={{ encKeys: encKeys }}>
Encryption keys: {{ encKeys: encKeys }}
</Trans>
</Text>
)}
{envelope.overwriteCount > 0 && (
<Text>
<Trans i18nKey='envelopes.overwrite_count' values={{ overwriteCount: envelope.overwriteCount }}>
Overwrite count: {{ overwriteCount: envelope.overwriteCount }}
</Trans>
</Text>
)}
<Text>
<Trans i18nKey={'envelopes.envelope_weight'} values={{ weight: envelope.weight }}>
Envelope weight: {{ weight: envelope.weight }}
</Trans>
</Text>
<Text>
<Trans
i18nKey={'envelopes.committed_in_block'}
components={{
a: (
<Link
as={RouterLink}
to={generatePath(RoutePath.Block, { height: envelope.blockHeight.toString(), tab: null, page: null })}
/>
),
}}
values={{ block: envelope.blockHeight }}
/>
</Text>
<Text>
<Trans
i18nKey={'envelopes.belongs_to_process'}
components={{
a: <Link as={RouterLink} to={generatePath(RoutePath.Process, { pid: envelope.electionID, tab: null })} />,
}}
values={{ pid: envelope.electionID }}
/>
</Text>
</Flex>
<ShowRawButton obj={envelope} />
<RouteParamsTabs path={route} isLazy>
<TabList display='flex' flexWrap='wrap'>
<Tab>
<Trans i18nKey={'envelopes.content'}>Envelope Content</Trans>
</Tab>
<Tab>
<Trans i18nKey={'process.tab_details'}>Details</Trans>
</Tab>
<Tab>
<Trans i18nKey={'raw'}>Raw</Trans>
</Tab>
</TabList>
<TabPanels>
<TabPanel>
<ElectionProvider id={envelope.electionID}>
<VotePackage votePackage={envelope.package} />
</ElectionProvider>
</TabPanel>
<TabPanel>
<EnvelopeDetailsGrid {...envelope} />
</TabPanel>
<TabPanel>
<RawContentBox obj={envelope} />
</TabPanel>
</TabPanels>
</RouteParamsTabs>
</Flex>
)
}

export const VotePackage = ({ votePackage }: { votePackage: VotePackageType }) => {
const { election } = useElection()
if (!election || !(election instanceof PublishedElection)) return null

return (
<Flex direction={'column'}>
<Envelope votePackage={votePackage} />
<Text fontSize={'sm'} as='i'>
<Trans
i18nKey={'envelopes.from_election_title'}
components={{
a: <Link as={RouterLink} to={generatePath(RoutePath.Process, { pid: election.id, tab: null })} />,
}}
values={{ title: election.title.default }}
/>
</Text>
</Flex>
)
}

const EnvelopeDetailsGrid = (envelope: VoteInfoResponse) => {
const { t } = useTranslation()

const emitted = formatDistance(new Date(envelope.date), new Date(), { addSuffix: true })
const encKeys = envelope.encryptionKeys?.join(',')

const details: GridItemProps[] = [
{
label: t('envelopes.emitted', { defaultValue: 'Emitted' }),
children: <Text>{emitted}</Text>,
},
...(envelope.overwriteCount > 0
? [
{
label: t('envelopes.overwrite_count', { defaultValue: 'Overwrite count' }),
children: envelope.overwriteCount,
},
]
: []),
{
label: 'Overwrite count',
children: <Text>{envelope.overwriteCount}</Text>,
},
...(encKeys && encKeys?.length > 0
? [
{
label: t('envelopes.encryption_keys', { defaultValue: 'Encryption keys' }),
children: encKeys,
},
]
: []),
{
label: t('envelopes.envelope_weight', { defaultValue: 'Envelope weight' }),
children: <Text>{envelope.weight}</Text>,
},
{
label: t('envelopes.committed_in_block', { defaultValue: 'Committed in block' }),
children: (
<Link
as={RouterLink}
to={generatePath(RoutePath.Block, { height: envelope.blockHeight.toString(), tab: null, page: null })}
>
{envelope.blockHeight}
</Link>
),
},
{
label: t('envelopes.on_transaction', { defaultValue: 'On Transaction' }),
children: (
<ReducedTextAndCopy
breakPoint={{ base: true, lg: false }}
pl={0}
color={'textAccent1'}
toCopy={envelope.txHash}
fontWeight={'normal'}
h={0}
fontSize={'md'}
to={generatePath(RoutePath.Transaction, {
block: envelope.blockHeight.toString(),
index: envelope.transactionIndex.toString(),
tab: null,
})}
>
{envelope.txHash}
</ReducedTextAndCopy>
),
},
{ ...processIdGridItem(envelope.electionID, t) },
]
return <DetailsGrid details={details} />
}

export default EnvelopeDetail
2 changes: 1 addition & 1 deletion src/components/Layout/TopBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export const TopBar = () => {
const rightLinks: HeaderLink[] = [
{
name: t('links.verify_vote', { defaultValue: 'Verify vote' }),
url: generatePath(RoutePath.Verify, { verifier: null }),
url: generatePath(RoutePath.Verify, { verifier: null, tab: null }),
},
]

Expand Down
10 changes: 5 additions & 5 deletions src/components/Process/Detail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ const EnvelopeCard = ({ envelope, count }: { envelope: VoteSummary; count: numbe
<CardHeader>
<Flex justify={'space-between'}>
<Text fontWeight={'bold'}>
<Trans i18nKey={'envelope.envelope_number'} num={count}>
<Trans i18nKey={'envelopes.envelope_number'} num={count}>
Envelope nº {{ num: count }}
</Trans>
</Text>
Expand All @@ -295,7 +295,7 @@ const EnvelopeCard = ({ envelope, count }: { envelope: VoteSummary; count: numbe
as={RouterLink}
to={generatePath(RoutePath.Block, { height: envelope.blockHeight.toString(), tab: null, page: null })}
>
<Trans i18nKey={'envelope.block'} height={envelope.blockHeight}>
<Trans i18nKey={'envelopes.block'} height={envelope.blockHeight}>
Block {{ height: envelope.blockHeight }}
</Trans>
</Link>
Expand All @@ -307,12 +307,12 @@ const EnvelopeCard = ({ envelope, count }: { envelope: VoteSummary; count: numbe
tab: null,
})}
>
<Trans i18nKey={'envelope.tx_number'} transactionIndex={envelope.transactionIndex}>
<Trans i18nKey={'envelopes.tx_number'} transactionIndex={envelope.transactionIndex}>
Transaction: {{ transactionIndex: envelope.transactionIndex }}
</Trans>
</Link>
<Link as={RouterLink} to={generatePath(RoutePath.Envelope, { verifier: envelope.voteID })}>
<Trans i18nKey={'envelope.details'}>Details</Trans>
<Link as={RouterLink} to={generatePath(RoutePath.Envelope, { verifier: envelope.voteID, tab: null })}>
<Trans i18nKey={'envelopes.details'}>Details</Trans>
</Link>
</Flex>
</CardBody>
Expand Down
17 changes: 15 additions & 2 deletions src/components/Transactions/TxDetails/SpecificTxDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Box, Code, Flex, Icon, Text } from '@chakra-ui/react'
import { OrganizationProvider } from '@vocdoni/react-providers'
import { ElectionProvider, OrganizationProvider } from '@vocdoni/react-providers'
import {
AdminTx,
ensure0x,
Expand All @@ -19,8 +19,9 @@ import { DetailsGrid, GridItemProps } from '~components/Layout/DetailsGrid'
import { SmallOrganizationCard } from '~components/Organizations/Card'
import { RoutePath } from '~constants'
import { b64ToHex } from '~utils/objects'
import { VotePackage } from '~components/Envelope/Detail'

const processIdGridItem = (processId: string, t: TFunction): GridItemProps => {
export const processIdGridItem = (processId: string, t: TFunction): GridItemProps => {
return {
label: t('transactions.belongs_to_process', { defaultValue: 'Belongs to process' }),
children: (
Expand Down Expand Up @@ -84,6 +85,18 @@ const VoteTxDetails = ({ rawTx, votePackage, processId }: { rawTx: any } & VoteE
},
]
: []),
...(votePackage
? [
{
label: t('transactions.vote_sense', { defaultValue: 'Vote sense' }),
children: (
<ElectionProvider id={process}>
<VotePackage votePackage={JSON.parse(votePackage)} />
</ElectionProvider>
),
},
]
: []),
]
return <DetailsGrid details={details} />
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/Verify/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const SearchVote = ({ compact }: { compact?: boolean }) => {
<Box>
<Button
onClick={() => {
navigate(generatePath(RoutePath.Verify, { verifier }))
navigate(generatePath(RoutePath.Verify, { verifier, tab: null }))
}}
>
<Trans i18nKey={'verify.verify'}>Verify</Trans>
Expand Down
4 changes: 2 additions & 2 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export enum RoutePath {
Base = '/',
Block = '/block/:height/:tab?/:page?',
BlocksList = '/blocks/:page?',
Envelope = '/envelope/:verifier',
Envelope = '/envelope/:verifier/:tab?',
Organization = '/organization/:pid/:tab?/:page?',
OrganizationsList = '/organizations/:page?/:query?',
Process = '/process/:pid/:tab?',
Expand All @@ -32,7 +32,7 @@ export enum RoutePath {
TransactionByHashOrHeight = '/transactions/id/:hashOrHeight',
Validator = '/validator/:address/:tab?',
Validators = '/validators',
Verify = '/verify/:verifier?',
Verify = '/verify/:verifier?/:tab?',
}

// old explorer route paths (used by RouteRedirector)
Expand Down
25 changes: 12 additions & 13 deletions src/i18n/locales/ca.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,21 @@
"election": {
"no_votes_yet": "Encara no hi ha vots!"
},
"envelope": {
"block": "Bloc {{height}}",
"details": "Detalls",
"envelope_number": "Sobre nº {{num}}",
"tx_number": "Transacció: {{transactionIndex}}"
},
"envelopes": {
"belongs_to_process": "Pertany al procés <a>{{pid}}</a>",
"committed_in_block": "Registrat en el bloc <a>#{{block}}</a>",
"emitted": "Emès {{emitted}}",
"encryption_keys": "Claus de xifrat: {{encKeys}}",
"envelope_detail": "Detall del sobre",
"envelope_weight": "Pes del sobre: {{weight}}",
"block": "Block",
"committed_in_block": "Registrat en el bloc",
"content": "Envelope Content",
"details": "Details",
"emitted": "Emès",
"encryption_keys": "Claus de xifrat",
"envelope_number": "Envelope nº {{num}}",
"envelope_weight": "Pes del sobre",
"from_election_title": "From election: <a>{{ title }}</a>",
"not_found": "Sobre no trobat",
"overwrite_count": "Nombre de sobrescritures: {{overwriteCount}}",
"on_transaction": "On Transaction",
"overwrite_count": "Nombre de sobrescritures",
"registered_correctly": "El vot s'ha registrat correctament",
"tx_number": "Transaction: {{transactionIndex}}",
"verifier_code": "Codi de verificació",
"vote_registered": "Vot registrat"
},
Expand Down
23 changes: 11 additions & 12 deletions src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,21 @@
"election": {
"no_votes_yet": "No votes yet!"
},
"envelope": {
"block": "Block {{height}}",
"details": "Details",
"envelope_number": "Envelope nº {{num}}",
"tx_number": "Transaction: {{transactionIndex}}"
},
"envelopes": {
"belongs_to_process": "Belongs to process <a>{{pid}}</a>",
"committed_in_block": "Commited in block <a>#{{block}}</a>",
"block": "Block",
"committed_in_block": "Commited in block",
"content": "Envelope Content",
"details": "Details",
"emitted": "Emitted {{emitted}}",
"encryption_keys": "Encryption keys: {{encKeys}}",
"envelope_detail": "Envelope Detail",
"envelope_weight": "Envelope weight: {{weight}}",
"encryption_keys": "Encryption keys",
"envelope_number": "Envelope nº {{num}}",
"envelope_weight": "Envelope weight",
"from_election_title": "From election: <a>{{title}}</a>",
"not_found": "Envelope not found",
"overwrite_count": "Overwrite count: {{overwriteCount}}",
"on_transaction": "On Transaction",
"overwrite_count": "Overwrite count",
"registered_correctly": "Vote has been registered correctly",
"tx_number": "Transaction: {{transactionIndex}}",
"verifier_code": "Verifier code",
"vote_registered": "Vote registered"
},
Expand Down
Loading
Loading