diff --git a/packages/chakra-components/src/components/Election/Envelope.tsx b/packages/chakra-components/src/components/Election/Envelope.tsx
new file mode 100644
index 00000000..98ebda94
--- /dev/null
+++ b/packages/chakra-components/src/components/Election/Envelope.tsx
@@ -0,0 +1,116 @@
+import { useDatesLocale, useElection } from '@vocdoni/react-providers'
+import {
+ ElectionResultsTypeNames,
+ ElectionStatus,
+ IChoice,
+ IQuestion,
+ IVoteEncryptedPackage,
+ IVotePackage,
+ PublishedElection,
+} from '@vocdoni/sdk'
+import { Text } from '@chakra-ui/react'
+import { chakra, ChakraProps, useMultiStyleConfig } from '@chakra-ui/system'
+import { format } from 'date-fns'
+
+export type VotePackageType = IVotePackage | IVoteEncryptedPackage
+
+export const Envelope = ({
+ votePackage,
+ ...props
+}: {
+ votePackage: VotePackageType
+} & ChakraProps) => {
+ const styles = useMultiStyleConfig('Envelope')
+ const { election, localize } = useElection()
+ const locale = useDatesLocale()
+
+ if (
+ !election ||
+ 'encrypted' in votePackage ||
+ !(election instanceof PublishedElection) ||
+ election?.status === ElectionStatus.CANCELED
+ )
+ return null
+
+ if (election?.electionType.secretUntilTheEnd && election.status !== ElectionStatus.RESULTS) {
+ return (
+
+ {localize('results.secret_until_the_end', {
+ endDate: format(election.endDate, localize('results.date_format'), { locale }),
+ })}
+
+ )
+ }
+
+ return (
+
+ {election.questions.map((q, i) => {
+ return (
+
+ {localize('envelopes.question_title', { title: q.title.default })}
+
+
+ )
+ })}
+
+ )
+}
+
+const SelectedOptions = ({
+ question,
+ questionIndex,
+ votes,
+}: {
+ question: IQuestion
+ questionIndex: number
+ votes: number[]
+}) => {
+ const { election, localize } = useElection()
+ const styles = useMultiStyleConfig('Envelope')
+
+ if (!election || !(election instanceof PublishedElection)) return null
+
+ const selectedOptions: IChoice[] = []
+ switch (election.resultsType.name) {
+ case ElectionResultsTypeNames.MULTIPLE_CHOICE:
+ const abstainValues = election.resultsType?.properties?.abstainValues ?? []
+ let abstainCount = 0
+ votes.forEach((v) => {
+ if (abstainValues.includes(v.toString())) {
+ abstainCount++
+ return
+ }
+ selectedOptions.push(question.choices[v])
+ })
+ if (abstainCount > 0) {
+ selectedOptions.push({
+ title: {
+ default: localize('envelopes.envelope_abstain_count', { count: abstainCount }),
+ },
+ results: abstainCount.toString(),
+ value: -1,
+ } as IChoice)
+ }
+ break
+ case ElectionResultsTypeNames.APPROVAL:
+ votes.forEach((v, i) => {
+ if (v > 0) selectedOptions.push(question.choices[i])
+ })
+ break
+ case ElectionResultsTypeNames.SINGLE_CHOICE_MULTIQUESTION:
+ selectedOptions.push(question.choices[votes[questionIndex]])
+ break
+ default:
+ selectedOptions.push(question.choices[votes[0]])
+ }
+
+ return (
+ <>
+ {selectedOptions.map((c, i) => (
+
+ {c.title.default}
+
+ ))}
+ >
+ )
+}
diff --git a/packages/chakra-components/src/components/Election/index.tsx b/packages/chakra-components/src/components/Election/index.tsx
index 8e676627..dcafec5c 100644
--- a/packages/chakra-components/src/components/Election/index.tsx
+++ b/packages/chakra-components/src/components/Election/index.tsx
@@ -1,5 +1,6 @@
export * from './Actions'
export * from './Description'
+export * from './Envelope'
export * from './Election'
export * from './Header'
export * from './Questions'
diff --git a/packages/chakra-components/src/i18n/locales.ts b/packages/chakra-components/src/i18n/locales.ts
index 2015c922..addaa82b 100644
--- a/packages/chakra-components/src/i18n/locales.ts
+++ b/packages/chakra-components/src/i18n/locales.ts
@@ -28,6 +28,10 @@ export const locales = {
end_process_button: 'End process',
},
empty: 'Apparently this process has no questions 🤔',
+ envelopes: {
+ envelope_abstain_count: 'Abstained {{ count }} times',
+ question_title: 'Option/s selected in "{{ title }}":',
+ },
errors: {
wrong_data_title: 'Wrong data',
wrong_data_description: 'The specified data is not correct',
diff --git a/packages/chakra-components/src/theme/envelope.ts b/packages/chakra-components/src/theme/envelope.ts
new file mode 100644
index 00000000..f2381443
--- /dev/null
+++ b/packages/chakra-components/src/theme/envelope.ts
@@ -0,0 +1,38 @@
+import { createMultiStyleConfigHelpers } from '@chakra-ui/styled-system'
+
+export const envelopeAnatomy = [
+ // all questions wrapper
+ 'wrapper',
+ // individual question wrapper
+ 'question',
+ // question title
+ 'title',
+ // choice wrapper
+ 'choiceWrapper',
+ // choice title
+ 'choiceTitle',
+ // secret envelope (no results until the end text)
+ 'secret',
+]
+
+const { defineMultiStyleConfig, definePartsStyle } = createMultiStyleConfigHelpers(envelopeAnatomy)
+
+const baseStyle = definePartsStyle({
+ wrapper: {
+ flexDirection: 'column',
+ gap: 2,
+ },
+ title: {
+ pb: 0,
+ fontWeight: 'bold',
+ },
+ secret: {
+ color: 'red.200',
+ textAlign: 'center',
+ fontWeight: 'bold',
+ },
+})
+
+export const EnvelopeTheme = defineMultiStyleConfig({
+ baseStyle,
+})
diff --git a/packages/chakra-components/src/theme/index.ts b/packages/chakra-components/src/theme/index.ts
index 60c70bd2..8ac4bcc0 100644
--- a/packages/chakra-components/src/theme/index.ts
+++ b/packages/chakra-components/src/theme/index.ts
@@ -2,16 +2,18 @@ import { ConfirmModalTheme } from './confirm'
import { ElectionScheduleTheme as ElectionSchedule, ElectionTitleTheme as ElectionTitle } from './election'
import { HorizontalRulerTheme } from './layout'
import {
- QuestionsTheme as ElectionQuestions,
QuestionsConfirmationTheme,
+ QuestionsTheme as ElectionQuestions,
QuestionsTipTheme,
QuestionsTypeBadgeTheme,
} from './questions'
import { ResultsTheme as ElectionResults } from './results'
import { VoteWeightTheme } from './vote'
+import { EnvelopeTheme } from './envelope'
export const theme = {
components: {
+ Envelope: EnvelopeTheme,
ElectionQuestions,
ElectionResults,
ElectionSchedule,
@@ -27,6 +29,7 @@ export const theme = {
export * from './actions'
export * from './confirm'
+export * from './envelope'
export * from './election'
export * from './layout'
export * from './questions'