Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
5816b47
Refactor questions to accept multiple elections
selankon Oct 8, 2024
20264f4
Permit change election form id
selankon Oct 8, 2024
26958af
Extract getVotePackage function
selankon Oct 9, 2024
4920506
Split VoteButton logic
selankon Oct 10, 2024
47e25bf
Refactor name
selankon Oct 10, 2024
ba17720
Fix isInvalid lint error
selankon Oct 10, 2024
9c1ac44
Add MultiElection files
selankon Oct 14, 2024
473d2f3
Simplify validation function
selankon Oct 14, 2024
bd67227
Use relative imports
selankon Oct 14, 2024
321b048
Added ffjavascript resolution to fix version clash issues in tests
elboletaire Oct 14, 2024
8f4a830
Use null instead of empty fragment
selankon Oct 15, 2024
c745e91
Delete sameLengthValidator
selankon Oct 15, 2024
ef03ef0
Merge Multielection with the normal form
selankon Oct 16, 2024
084cbb9
Deprecate multielection confirmation
selankon Oct 18, 2024
4fd42c6
Use election id as form id
selankon Oct 18, 2024
d6e3168
Fix warn message
selankon Oct 18, 2024
41fb9b5
Fix unused variable
selankon Oct 18, 2024
a3a9997
Export component
selankon Oct 21, 2024
8691569
Fix multielection layout
selankon Oct 21, 2024
ec21eae
Implement MultipleElectionVoted
selankon Oct 22, 2024
23d4490
Fix lintern
selankon Oct 22, 2024
a3038b6
Refactor variable name
selankon Oct 22, 2024
2c09335
Create FormFieldValues type
selankon Oct 22, 2024
c9f7176
Expose isDisabled to manually disable the form
selankon Oct 23, 2024
32181c9
Expose onSubmit handler
selankon Oct 24, 2024
47c0eb1
Fix lint
selankon Oct 24, 2024
a9c1796
Fix version
selankon Oct 25, 2024
8fc54f9
Delete uneeded resolution
selankon Oct 25, 2024
ee98753
Fix null
selankon Oct 29, 2024
0c59e5e
Add chakra.form
selankon Nov 4, 2024
aa4958c
Use isDisabled properly
selankon Nov 4, 2024
27eb8cd
Fix renderWith logout
selankon Nov 5, 2024
b1493f3
Implement loaded state
selankon Nov 5, 2024
dd00b01
Fix not renderWith elections loaded state
selankon Nov 6, 2024
4d94f49
Delete global error logic
selankon Nov 11, 2024
562beb1
Add missing dependency
selankon Nov 15, 2024
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ vite.config.ts.timestamp-*.mjs
.vscode
.yarn
.yarnrc*
.idea

package.json.backup
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,5 @@
"engines": {
"npm": "please use yarn",
"yarn": ">= 1.19.1 && < 2"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,55 +3,69 @@ import { Box, Text } from '@chakra-ui/layout'
import { ModalBody, ModalCloseButton, ModalFooter, ModalHeader } from '@chakra-ui/modal'
import { chakra, omitThemingProps, useMultiStyleConfig } from '@chakra-ui/system'
import { useClient } from '@vocdoni/react-providers'
import { ElectionResultsTypeNames, PublishedElection } from '@vocdoni/sdk'
import { ElectionResultsTypeNames } from '@vocdoni/sdk'
import { FieldValues } from 'react-hook-form'
import { useConfirm } from '../../layout'
import { FormFieldValues } from './Form'
import { ElectionStateStorage } from './Questions'

export type QuestionsConfirmationProps = {
answers: FieldValues
election: PublishedElection
answers: FormFieldValues
elections: ElectionStateStorage
}

export const QuestionsConfirmation = ({ answers, election, ...rest }: QuestionsConfirmationProps) => {
export const QuestionsConfirmation = ({ answers, elections, ...rest }: QuestionsConfirmationProps) => {
const mstyles = useMultiStyleConfig('ConfirmModal')
const styles = useMultiStyleConfig('QuestionsConfirmation', rest)
const { cancel, proceed } = useConfirm()
const props = omitThemingProps(rest)
const { localize } = useClient()

return (
<>
<ModalHeader sx={mstyles.header}>{localize('confirm.title')}</ModalHeader>
<ModalCloseButton sx={mstyles.close} />
<ModalBody sx={mstyles.body}>
<Text sx={styles.description}>{localize('vote.confirm')}</Text>
<Box {...props} sx={styles.box}>
<Text sx={styles.description}>{localize('vote.confirm')}</Text>
{election.questions.map((q, k) => {
if (election.resultsType.name === ElectionResultsTypeNames.SINGLE_CHOICE_MULTIQUESTION) {
const choice = q.choices.find((v) => v.value === parseInt(answers[k.toString()], 10))
{Object.values(elections).map(({ election, isAbleToVote }) => {
if (!isAbleToVote)
return (
<chakra.div key={k} __css={styles.question}>
<chakra.div __css={styles.title}>{q.title.default}</chakra.div>
<chakra.div __css={styles.answer}>{choice?.title.default}</chakra.div>
<chakra.div __css={styles.question} key={election.id}>
<chakra.div __css={styles.title}>{election.title.default}</chakra.div>
<chakra.div __css={styles.answer}>{localize('vote.not_able_to_vote')}</chakra.div>
</chakra.div>
)
}
const choices = answers[0]
.map((a: string) =>
q.choices[Number(a)] ? q.choices[Number(a)].title.default : localize('vote.abstain')
)
.map((a: string) => (
<span>
- {a}
<br />
</span>
))

return (
<chakra.div key={k} __css={styles.question}>
<chakra.div __css={styles.title}>{q.title.default}</chakra.div>
<chakra.div __css={styles.answer}>{choices}</chakra.div>
</chakra.div>
<>
{election.questions.map((q, k) => {
if (election.resultsType.name === ElectionResultsTypeNames.SINGLE_CHOICE_MULTIQUESTION) {
const choice = q.choices.find((v) => v.value === parseInt(answers[election.id][k.toString()], 10))
return (
<chakra.div key={k} __css={styles.question}>
<chakra.div __css={styles.title}>{q.title.default}</chakra.div>
<chakra.div __css={styles.answer}>{choice?.title.default}</chakra.div>
</chakra.div>
)
}
const choices = answers[election.id][0]
.map((a: string) =>
q.choices[Number(a)] ? q.choices[Number(a)].title.default : localize('vote.abstain')
)
.map((a: string) => (
<span>
- {a}
<br />
</span>
))

return (
<chakra.div key={k} __css={styles.question}>
<chakra.div __css={styles.title}>{q.title.default}</chakra.div>
<chakra.div __css={styles.answer}>{choices}</chakra.div>
</chakra.div>
)
})}
</>
)
})}
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,26 @@ import { QuestionTip } from './Tip'
export type QuestionProps = {
index: string
question: IQuestion
isDisabled?: boolean
}

export type QuestionFieldProps = ChakraProps & QuestionProps

export const QuestionField = ({ question, index }: QuestionFieldProps) => {
export const QuestionField = ({ question, index, isDisabled }: QuestionFieldProps) => {
const styles = useMultiStyleConfig('ElectionQuestions')
const {
formState: { errors },
} = useFormContext()

const [election, qi] = index.split('.')
const questionIndex = Number(qi)
let isInvalid = false
if (errors[election] && Array.isArray(errors[election]) && errors[election][questionIndex]) {
isInvalid = !!errors[election][questionIndex]
}
return (
<chakra.div __css={styles.question}>
<FormControl isInvalid={!!errors[index]}>
<FormControl isInvalid={isInvalid}>
<chakra.div __css={styles.header}>
<chakra.label __css={styles.title}>{question.title.default}</chakra.label>
</chakra.div>
Expand All @@ -34,7 +41,7 @@ export const QuestionField = ({ question, index }: QuestionFieldProps) => {
<Markdown>{question.description.default}</Markdown>
</chakra.div>
)}
<FieldSwitcher index={index} question={question} />
<FieldSwitcher index={index} question={question} isDisabled={isDisabled} />
<QuestionTip />
</chakra.div>
</FormControl>
Expand All @@ -58,7 +65,7 @@ export const FieldSwitcher = (props: QuestionProps) => {
}
}

export const MultiChoice = ({ index, question }: QuestionProps) => {
export const MultiChoice = ({ index, question, isDisabled }: QuestionProps) => {
const styles = useMultiStyleConfig('ElectionQuestions')
const {
election,
Expand All @@ -71,7 +78,7 @@ export const MultiChoice = ({ index, question }: QuestionProps) => {

if (!(election instanceof PublishedElection)) return null

const isNotAbleToVote = election?.status !== ElectionStatus.ONGOING || !isAbleToVote || voting
const disabled = election?.status !== ElectionStatus.ONGOING || !isAbleToVote || voting || isDisabled

if (!(election && election.resultsType.name === ElectionResultsTypeNames.MULTIPLE_CHOICE)) {
return null
Expand All @@ -85,7 +92,7 @@ export const MultiChoice = ({ index, question }: QuestionProps) => {
<Stack sx={styles.stack}>
<Controller
control={control}
disabled={isNotAbleToVote}
disabled={disabled}
rules={{
validate: (v) => {
// allow a single selection if is an abstain
Expand Down Expand Up @@ -114,7 +121,7 @@ export const MultiChoice = ({ index, question }: QuestionProps) => {
key={ck}
sx={styles.checkbox}
value={choice.value.toString()}
isDisabled={isNotAbleToVote || maxSelected}
isDisabled={disabled || maxSelected}
isChecked={currentValues.includes(choice.value.toString())}
onChange={(e) => {
if (values.includes(e.target.value)) {
Expand All @@ -139,7 +146,7 @@ export const MultiChoice = ({ index, question }: QuestionProps) => {
)
}

export const ApprovalChoice = ({ index, question }: QuestionProps) => {
export const ApprovalChoice = ({ index, question, isDisabled }: QuestionProps) => {
const styles = useMultiStyleConfig('ElectionQuestions')
const {
election,
Expand All @@ -152,7 +159,7 @@ export const ApprovalChoice = ({ index, question }: QuestionProps) => {

if (!(election instanceof PublishedElection)) return null

const isNotAbleToVote = election?.status !== ElectionStatus.ONGOING || !isAbleToVote || voting
const disabled = election?.status !== ElectionStatus.ONGOING || !isAbleToVote || voting || isDisabled

if (!(election && election.resultsType.name === ElectionResultsTypeNames.APPROVAL)) {
return null
Expand All @@ -164,7 +171,7 @@ export const ApprovalChoice = ({ index, question }: QuestionProps) => {
<Stack sx={styles.stack}>
<Controller
control={control}
disabled={isNotAbleToVote}
disabled={disabled}
rules={{
validate: (v) => {
return (v && v.length > 0) || localize('validation.at_least_one')
Expand All @@ -181,7 +188,7 @@ export const ApprovalChoice = ({ index, question }: QuestionProps) => {
key={ck}
sx={styles.checkbox}
value={choice.value.toString()}
isDisabled={isNotAbleToVote}
isDisabled={disabled}
onChange={(e) => {
if (values.includes(e.target.value)) {
onChange(values.filter((v: string) => v !== e.target.value))
Expand All @@ -203,7 +210,7 @@ export const ApprovalChoice = ({ index, question }: QuestionProps) => {
)
}

export const SingleChoice = ({ index, question }: QuestionProps) => {
export const SingleChoice = ({ index, question, isDisabled }: QuestionProps) => {
const styles = useMultiStyleConfig('ElectionQuestions')
const {
election,
Expand All @@ -218,7 +225,7 @@ export const SingleChoice = ({ index, question }: QuestionProps) => {

if (!(election instanceof PublishedElection)) return null

const disabled = election?.status !== ElectionStatus.ONGOING || !isAbleToVote || voting
const disabled = election?.status !== ElectionStatus.ONGOING || !isAbleToVote || voting || isDisabled
return (
<Controller
control={control}
Expand All @@ -227,7 +234,7 @@ export const SingleChoice = ({ index, question }: QuestionProps) => {
required: localize('validation.required'),
}}
name={index}
render={({ field }) => (
render={({ field, fieldState: { error: fieldError } }) => (
<RadioGroup sx={styles.radioGroup} {...field} isDisabled={disabled}>
<Stack direction='column' sx={styles.stack}>
{question.choices.map((choice, ck) => (
Expand All @@ -236,7 +243,7 @@ export const SingleChoice = ({ index, question }: QuestionProps) => {
</Radio>
))}
</Stack>
<FormErrorMessage sx={styles.error}>{errors[index]?.message as string}</FormErrorMessage>
<FormErrorMessage sx={styles.error}>{fieldError?.message as string}</FormErrorMessage>
</RadioGroup>
)}
/>
Expand Down
Loading
Loading