"use client" ;
import { useState } from "react" ;
import { Vote, Check } from "lucide-react" ;
import { Button } from "@/components/ui/button" ;
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog" ;
import { Progress } from "@/components/ui/progress" ;
import { cn } from "@/lib/utils" ;
type Option = {
id : string ;
text : string ;
votes : number ;
};
const pollQuestion = "When should we launch?" ;
const initialOptions : Option [] = [
{ id: "a" , text: "This Friday" , votes: 3 },
{ id: "b" , text: "Next Monday" , votes: 5 },
{ id: "c" , text: "Next Friday" , votes: 2 },
];
export const title = "React Dialog Block Quick Poll" ;
export default function DialogQuickPoll () {
const [ open , setOpen ] = useState ( false );
const [ options , setOptions ] = useState < Option []>(initialOptions);
const [ selected , setSelected ] = useState < string | null >( null );
const [ hasVoted , setHasVoted ] = useState ( false );
const totalVotes = options. reduce (( sum , opt ) => sum + opt.votes, 0 );
const handleVote = () => {
if ( ! selected) return ;
setOptions (( prev ) =>
prev. map (( opt ) =>
opt.id === selected ? { ... opt, votes: opt.votes + 1 } : opt
)
);
setHasVoted ( true );
};
const handleClose = () => {
setOpen ( false );
setTimeout (() => {
setOptions (initialOptions);
setSelected ( null );
setHasVoted ( false );
}, 200 );
};
const getPercentage = ( votes : number ) => {
const total = totalVotes + (hasVoted ? 0 : selected ? 1 : 0 );
return total > 0 ? Math. round ((votes / total) * 100 ) : 0 ;
};
return (
< Dialog open = {open} onOpenChange = {setOpen}>
< div className = "flex min-h-[350px] items-center justify-center" >
< DialogTrigger asChild >
< Button variant = "outline" >
< Vote className = "mr-2 h-4 w-4" />
Quick Poll
</ Button >
</ DialogTrigger >
</ div >
< DialogContent style = {{ maxWidth: "384px" }}>
{hasVoted ? (
<>
< div className = "flex flex-col items-center justify-center py-4 text-center" >
< Check className = "h-8 w-8 text-primary" />
< h3 className = "mt-4 text-lg font-medium" >Vote Submitted</ h3 >
</ div >
< div className = "space-y-3" >
< p className = "text-sm font-medium" >{pollQuestion}</ p >
{options. map (( option ) => {
const percentage = getPercentage (option.votes);
const isWinning = option.votes === Math. max ( ... options. map ( o => o.votes));
return (
< div key = {option.id} className = "space-y-1" >
< div className = "flex justify-between text-sm" >
< span className = { cn (isWinning && "font-medium" )}>
{option.text}
{option.id === selected && (
< span className = "ml-1 text-muted-foreground" >· You</ span >
)}
</ span >
< span className = "text-muted-foreground" >{percentage}%</ span >
</ div >
< Progress value = {percentage} className = "h-2" />
</ div >
);
})}
< p className = "text-xs text-muted-foreground pt-1" >
{totalVotes} votes
</ p >
</ div >
< DialogFooter >
< Button onClick = {handleClose} className = "w-full" >
Done
</ Button >
</ DialogFooter >
</>
) : (
<>
< DialogHeader >
< DialogTitle >{pollQuestion}</ DialogTitle >
< DialogDescription >
{totalVotes} votes · Select an option
</ DialogDescription >
</ DialogHeader >
< div className = "py-4 space-y-2" >
{options. map (( option ) => (
< button
key = {option.id}
onClick = {() => setSelected (option.id)}
className = { cn (
"w-full text-left p-3 rounded-md border transition-colors" ,
selected === option.id
? "border-primary bg-primary/5"
: "hover:border-muted-foreground/50"
)}
>
< span className = "text-sm" >{option.text}</ span >
</ button >
))}
</ div >
< DialogFooter >
< Button variant = "outline" onClick = {handleClose}>
Cancel
</ Button >
< Button onClick = {handleVote} disabled = { ! selected}>
Vote
</ Button >
</ DialogFooter >
</>
)}
</ DialogContent >
</ Dialog >
);
}