11import type { PollResults } from '@/lib/polls-types'
2+ import { rankPollOptions , pollChartSlices } from '@/lib/poll-results'
3+ import { pollOptionColors , POLL_OTHER_CHART_COLOR } from '@/lib/transaction-groups'
4+ import { PollResultsPie } from '@/components/charts/poll-results-pie'
25
36export function PollResultsView ( { results } : { results : PollResults } ) {
47 const total = results . total_voters
8+ const { ranked, leadingIds } = rankPollOptions ( results . options )
9+ const slices = pollChartSlices (
10+ results . options ,
11+ pollOptionColors ( results . options . length ) ,
12+ { count : results . other_responses . length , color : POLL_OTHER_CHART_COLOR } ,
13+ )
14+ const hasVotes = slices . some ( ( s ) => s . value > 0 )
15+ // Per-option slice color, so each breakdown row matches its donut slice.
16+ const colorById = new Map ( slices . map ( ( s ) => [ s . option_id , s . color ] ) )
517 return (
6- < section className = "space-y-5" >
7- < div className = "rounded-lg border bg-white p-5" >
8- < p className = "text-sm font-medium text-gray-700" >
9- { total === 0
10- ? 'No votes recorded.'
11- : `${ total } ${ total === 1 ? 'member' : 'members' } voted.` }
12- </ p >
13- </ div >
18+ < section >
19+ < div className = "grid gap-5 lg:grid-cols-[300px_minmax(0,1fr)] lg:items-start" >
20+ { /* Left: donut + full-text legend, sticky alongside the breakdown */ }
21+ < div className = "rounded-lg border bg-white p-5 lg:sticky lg:top-4" >
22+ < p className = "text-sm font-medium text-gray-700" >
23+ { total === 0
24+ ? 'No votes recorded.'
25+ : `${ total } ${ total === 1 ? 'member' : 'members' } voted.` }
26+ </ p >
27+ { hasVotes ? (
28+ < div className = "mt-5" >
29+ < PollResultsPie slices = { slices } totalVoters = { total } />
30+ </ div >
31+ ) : null }
32+ </ div >
1433
15- < ul className = "space-y-3" >
16- { results . options . map ( ( o ) => {
34+ { /* Right: ranked breakdown with voter chips, then Other responses —
35+ kept in this column so both align with the option cards. */ }
36+ < div className = "space-y-3" >
37+ < ul className = "space-y-3" >
38+ { ranked . map ( ( o ) => {
1739 const pct = total > 0 ? Math . round ( ( o . vote_count / total ) * 100 ) : 0
40+ const isLeading = leadingIds . has ( o . option_id )
41+ const color = colorById . get ( o . option_id ) ?? ''
1842 return (
19- < li key = { o . option_id } className = "rounded-lg border bg-white p-4" >
43+ < li
44+ key = { o . option_id }
45+ className = {
46+ 'rounded-lg border p-4 ' +
47+ ( isLeading ? 'border-blue-200 bg-blue-50/40' : 'bg-white' )
48+ }
49+ >
2050 < div className = "flex items-baseline justify-between gap-3" >
21- < p className = "text-sm font-medium text-gray-900" > { o . option_label } </ p >
22- < p className = "text-xs text-gray-500" >
51+ < p className = "flex flex-wrap items-center gap-2 text-sm font-medium text-gray-900" >
52+ < span
53+ className = "h-2.5 w-2.5 flex-none rounded-sm"
54+ style = { { backgroundColor : color } }
55+ aria-hidden
56+ />
57+ < span > { o . option_label } </ span >
58+ { isLeading ? (
59+ < span
60+ className = "inline-flex items-center rounded-full bg-blue-600 px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-white"
61+ title = "Leading option"
62+ >
63+ ★ Leading
64+ </ span >
65+ ) : null }
66+ </ p >
67+ < p className = "whitespace-nowrap text-xs text-gray-500" >
2368 { o . vote_count } { o . vote_count === 1 ? 'vote' : 'votes' } · { pct } %
2469 </ p >
2570 </ div >
2671 < div className = "mt-2 h-2 w-full overflow-hidden rounded-full bg-gray-100" >
2772 < div
28- className = "h-full bg-blue-500 "
29- style = { { width : `${ pct } %` } }
73+ className = "h-full rounded-full "
74+ style = { { width : `${ pct } %` , backgroundColor : color } }
3075 aria-hidden
3176 />
3277 </ div >
@@ -35,7 +80,10 @@ export function PollResultsView({ results }: { results: PollResults }) {
3580 { o . voter_names . map ( ( name , i ) => (
3681 < li
3782 key = { `${ name } -${ i } ` }
38- className = "truncate rounded-md bg-gray-100 px-2 py-1 text-xs text-gray-700"
83+ className = {
84+ 'truncate rounded-md px-2 py-1 text-xs ' +
85+ ( isLeading ? 'bg-blue-50 text-blue-800' : 'bg-gray-100 text-gray-700' )
86+ }
3987 title = { name }
4088 >
4189 { name }
@@ -46,28 +94,30 @@ export function PollResultsView({ results }: { results: PollResults }) {
4694 </ li >
4795 )
4896 } ) }
49- </ ul >
50-
51- { results . other_responses . length > 0 ? (
52- < section className = "rounded-lg border bg-white p-4" >
53- < p className = "text-sm font-medium text-gray-900" >
54- Other responses ({ results . other_responses . length } )
55- </ p >
56- < ul className = "mt-3 space-y-2" >
57- { results . other_responses . map ( ( r , i ) => (
58- < li
59- key = { i }
60- className = "rounded-md bg-gray-50 px-3 py-2 text-sm text-gray-800"
61- >
62- < p > { r . text } </ p >
63- { r . author ? (
64- < p className = "mt-1 text-xs text-gray-500" > — { r . author } </ p >
65- ) : null }
66- </ li >
67- ) ) }
6897 </ ul >
69- </ section >
70- ) : null }
98+
99+ { results . other_responses . length > 0 ? (
100+ < section className = "rounded-lg border bg-white p-4" >
101+ < p className = "text-sm font-medium text-gray-900" >
102+ Other responses ({ results . other_responses . length } )
103+ </ p >
104+ < ul className = "mt-3 space-y-2" >
105+ { results . other_responses . map ( ( r , i ) => (
106+ < li
107+ key = { i }
108+ className = "rounded-md bg-gray-50 px-3 py-2 text-sm text-gray-800"
109+ >
110+ < p > { r . text } </ p >
111+ { r . author ? (
112+ < p className = "mt-1 text-xs text-gray-500" > — { r . author } </ p >
113+ ) : null }
114+ </ li >
115+ ) ) }
116+ </ ul >
117+ </ section >
118+ ) : null }
119+ </ div >
120+ </ div >
71121 </ section >
72122 )
73123}
0 commit comments