diff --git a/packages/frontend/src/components/Election/Results/Results.tsx b/packages/frontend/src/components/Election/Results/Results.tsx index 7a831b8c..964742d0 100644 --- a/packages/frontend/src/components/Election/Results/Results.tsx +++ b/packages/frontend/src/components/Election/Results/Results.tsx @@ -22,6 +22,7 @@ import { Candidate } from "@equal-vote/star-vote-shared/domain_model/Candidate"; import VoterIntentWidget from "./components/VoterIntentWidget"; import SupportBlurb from "../SupportBlurb"; import ColumnDistributionWidget from "./components/ColumnDistributionWidget"; +import NameRecognitionWidget from "./components/NameRecognitionWidget"; function STARResultsViewer({ filterRandomFromLogs }: {filterRandomFromLogs: boolean }) { let i = 0; @@ -61,6 +62,7 @@ function STARResultsViewer({ filterRandomFromLogs }: {filterRandomFromLogs: bool + @@ -212,14 +214,12 @@ function IRVResultsViewer() { + - - - diff --git a/packages/frontend/src/components/Election/Results/components/ColumnDistributionWidget.tsx b/packages/frontend/src/components/Election/Results/components/ColumnDistributionWidget.tsx index 582be4c1..b2985f37 100644 --- a/packages/frontend/src/components/Election/Results/components/ColumnDistributionWidget.tsx +++ b/packages/frontend/src/components/Election/Results/components/ColumnDistributionWidget.tsx @@ -9,9 +9,6 @@ export default () => { const {ballotsForRace} = useAnonymizedBallots(); const {t, race} = useRace(); - // How many columns were used? - // Which columns were used? - let numColumns = []; let whichColumns = []; let totalColumns = 0; @@ -44,7 +41,7 @@ export default () => { {t(`results.column_distribution_num_title`)} - {race.voting_method == 'STAR' && <> + {(race.voting_method == 'STAR' || race.voting_method == 'STAR_PR') && <> {t('results.column_distribution_which_title')} } diff --git a/packages/frontend/src/components/Election/Results/components/NameRecognitionWidget.tsx b/packages/frontend/src/components/Election/Results/components/NameRecognitionWidget.tsx new file mode 100644 index 00000000..94a10845 --- /dev/null +++ b/packages/frontend/src/components/Election/Results/components/NameRecognitionWidget.tsx @@ -0,0 +1,31 @@ +import useAnonymizedBallots from "~/components/AnonymizedBallotsContextProvider"; +import Widget from "./Widget"; +import useRace from "~/components/RaceContextProvider"; +import { Divider, Typography } from "@mui/material"; +import ResultsBarChart from "./ResultsBarChart"; + +// candidates helps define the order +export default () => { + const {ballotsForRace} = useAnonymizedBallots(); + const {t, race} = useRace(); + + console.log(race.candidates) + let numActive = Object.fromEntries(race.candidates.map(c => [c.candidate_id, { + name: c.candidate_name, + count: 0, + }])) + + let b = ballotsForRace() + b.forEach((scores) => { + scores.forEach(score => { + if(score.score == null || score.score == undefined) return; + numActive[score.candidate_id].count++; + }) + }) + + return + {t(`results.name_recognition_sub_title`)} + -(a.count-b.count))} xKey='count' percentage={true} percentDenominator={b.length}/> + {t(`results.name_recognition_blank_warning`)} + +} \ No newline at end of file diff --git a/packages/frontend/src/components/ElectionContextProvider.tsx b/packages/frontend/src/components/ElectionContextProvider.tsx index 4cbe0dab..e6b26110 100644 --- a/packages/frontend/src/components/ElectionContextProvider.tsx +++ b/packages/frontend/src/components/ElectionContextProvider.tsx @@ -27,7 +27,7 @@ export const ElectionContext = createContext({ refreshElection: () => false, updateElection: () => false, permissions: [], - t: () => {} + t: () => undefined }) export const ElectionContextProvider = ({ id, children }) => { diff --git a/packages/frontend/src/components/ElectionForm/CreateElectionDialog.tsx b/packages/frontend/src/components/ElectionForm/CreateElectionDialog.tsx index dbfcaee1..15bb1da1 100644 --- a/packages/frontend/src/components/ElectionForm/CreateElectionDialog.tsx +++ b/packages/frontend/src/components/ElectionForm/CreateElectionDialog.tsx @@ -226,7 +226,7 @@ export default () => { } {t('election_creation.term_question')} - + {['poll', 'election'].map( (type, i) => @@ -273,7 +273,6 @@ export default () => { {t('election_creation.restricted_question')} - diff --git a/packages/frontend/src/components/styles.tsx b/packages/frontend/src/components/styles.tsx index ce033aac..378cecb1 100644 --- a/packages/frontend/src/components/styles.tsx +++ b/packages/frontend/src/components/styles.tsx @@ -5,14 +5,23 @@ import { useState } from "react"; import en from './en.yaml'; import { useSubstitutedTranslation } from "./util"; import { TermType } from "@equal-vote/star-vote-shared/domain_model/ElectionSettings"; +import useRace from "./RaceContextProvider"; +import useElection from "./ElectionContextProvider"; // this doesn't work yet, I filed a github issue // https://github.com/Modyfi/vite-plugin-yaml/issues/27 type TipName = keyof typeof en.tips; -export const Tip = (props: {name: TipName, electionTermType: TermType | undefined}) => { - const {t} = useSubstitutedTranslation(props.electionTermType ?? 'election'); +export const Tip = (props: {name: TipName}) => { + // TODO: maybe I can insert useElection and useRace within useSubstitutedTranslation? + const {t: ts} = useSubstitutedTranslation('election'); + const {t: te} = useElection(); + const {t: tr} = useRace(); + let t = (tr() !== undefined) ? tr : ( + (te() !== undefined) ? te : ts + ); + const [clicked, setClicked] = useState(false); const [hovered, setHovered] = useState(false); return setClicked(false)}> diff --git a/packages/frontend/src/components/util.tsx b/packages/frontend/src/components/util.tsx index ff6685d4..16bf8768 100644 --- a/packages/frontend/src/components/util.tsx +++ b/packages/frontend/src/components/util.tsx @@ -184,7 +184,7 @@ export const useSubstitutedTranslation = (electionTermType='election', v={}) => if(typeof txt !== 'string') return txt; return txt.split(rTip).map((str, i) => { if(i%2 == 0) return str; - return + return }) } diff --git a/packages/frontend/src/i18n/en.yaml b/packages/frontend/src/i18n/en.yaml index 8c8fd653..ea07986e 100644 --- a/packages/frontend/src/i18n/en.yaml +++ b/packages/frontend/src/i18n/en.yaml @@ -295,7 +295,7 @@ election_creation: title_question: What's the title for your {{election}}? !tip(election_title) # TERM will be replaced with election or poll restricted_title: Restricted? - restricted_question: Would you like your {{election}} to be restricted to a pre-defined voter list? + restricted_question: Would you like your {{election}} to be restricted to a pre-defined voter list? !tip(restricted) template_title: Choose Voters template_prompt: (You can change this later, if needed.) @@ -445,6 +445,13 @@ results: column_distribution_num_title: Number of {{preferences}} given !tip(columns_given) column_distribution_which_title: Which {{preferences}} were used? !tip(columns_used) + name_recognition_title: Name Recognition + name_recognition_sub_title: Percent of voters who listed a non-blank score for each candidate + name_recognition_blank_warning: > + Choosing not to score a candidate is identical to giving that candidate a zero. + However the voter's decision still gives us sense for the candidates they're + most familiar with. + candidate_selector: '{{capital_candidate}}' win_count: '{{count}} Wins' @@ -599,8 +606,8 @@ election_state: # Info bubbles tips: columns_given: - title: Number of scores or ranks given - description: Percentage over voters who gave each number of scores/ranks + title: Number of {{preferences}} given + description: Percentage over voters who gave each number of {{preferences}} columns_used: title: Which scores were used