11"use client" ;
22
3- import { useReadContract , useAccount , useSendCalls } from "wagmi" ;
3+ import { useState } from "react" ; // Tambahkan useState
4+ import { useReadContract , useSendCalls } from "wagmi" ;
45import { encodeFunctionData } from "viem" ;
56import { CONTRACT_ADDRESS , CLASS_VOTE_ABI , BUILDER_CODE_HEX } from "~/app/constants" ;
7+ import { HowToVote } from "@mui/icons-material" ; // Import ikon untuk modal
68
79export default function VoteCard ( ) {
810 const { sendCalls, isPending } = useSendCalls ( ) ;
11+
12+ // --- STATE UNTUK MODAL ---
13+ const [ isModalOpen , setIsModalOpen ] = useState ( false ) ;
14+ const [ selectedIndex , setSelectedIndex ] = useState < number | null > ( null ) ;
15+
916 const { data : candidates , refetch } = useReadContract ( {
1017 abi : CLASS_VOTE_ABI , address : CONTRACT_ADDRESS , functionName : "getCandidates" ,
1118 } ) ;
1219
13- const { data : pollTitle } = useReadContract ( {
14- abi : CLASS_VOTE_ABI ,
15- address : CONTRACT_ADDRESS ,
16- functionName : "pollTitle" ,
17- } ) ;
20+ const { data : pollTitle } = useReadContract ( {
21+ abi : CLASS_VOTE_ABI , address : CONTRACT_ADDRESS , functionName : "pollTitle" ,
22+ } ) ;
1823
19- const handleVote = async ( index : number ) => {
20- const name = ( candidates as any [ ] ) ?. [ index ] ?. name || "kandidat ini" ;
21- if ( ! confirm ( `Apakah Anda yakin memilih ${ name } ?` ) ) return ;
24+ // 1. Fungsi untuk MEMBUKA Modal
25+ const openModal = ( index : number ) => {
26+ setSelectedIndex ( index ) ;
27+ setIsModalOpen ( true ) ;
28+ } ;
2229
30+ // 2. Fungsi untuk EKSEKUSI Vote (setelah klik "YA" di modal)
31+ const executeVote = async ( ) => {
32+ if ( selectedIndex === null ) return ;
33+
2334 const paymasterUrl = process . env . NEXT_PUBLIC_PAYMASTER_URL ;
2435 if ( ! paymasterUrl ) return ;
2536
@@ -30,33 +41,88 @@ const { data: pollTitle } = useReadContract({
3041 data : `${ encodeFunctionData ( {
3142 abi : CLASS_VOTE_ABI ,
3243 functionName : "vote" ,
33- args : [ BigInt ( index ) ]
34- } ) } ${ BUILDER_CODE_HEX } ` as `0x${string } `, // Gabungkan Builder Code
44+ args : [ BigInt ( selectedIndex ) ]
45+ } ) } ${ BUILDER_CODE_HEX } ` as `0x${string } `,
3546 } ] ,
3647 capabilities : { paymasterService : { url : paymasterUrl } }
3748 } ) ;
38- alert ( "Suara terkirim!" ) ;
49+
50+ setIsModalOpen ( false ) ; // Tutup modal
51+ alert ( "Suara sedang diproses secara gasless!" ) ;
3952 setTimeout ( ( ) => refetch ( ) , 3000 ) ;
40- } catch ( e ) { alert ( "Gagal voting." ) ; }
53+ } catch ( e ) {
54+ alert ( "Gagal voting." ) ;
55+ setIsModalOpen ( false ) ;
56+ }
4157 } ;
4258
4359 if ( ! candidates || ( candidates as any ) . length === 0 ) return null ;
4460
61+ const selectedName = selectedIndex !== null ? ( candidates as any [ ] ) [ selectedIndex ] ?. name : "" ;
62+
4563 return (
4664 < div className = "space-y-4" >
4765 < h2 className = "text-xl font-black text-center text-blue-600 mb-6 uppercase tracking-tight" >
48- { pollTitle as string || "MEMUAT JUDUL..." }
49- </ h2 >
66+ { pollTitle as string || "MEMUAT JUDUL..." }
67+ </ h2 >
68+
69+ { /* DAFTAR KANDIDAT */ }
5070 { ( candidates as any [ ] ) . map ( ( c , i ) => (
5171 < div key = { i } className = "bg-white p-4 rounded-[28px] border flex items-center gap-4 shadow-sm" >
5272 < img src = { c . photoUrl || "https://via.placeholder.com/150" } className = "w-20 h-20 rounded-2xl object-cover border" alt = { c . name } />
53- < div className = "flex-1" > < h3 className = "font-black text-gray-800 text-lg" > { c . name } </ h3 > </ div >
54- < button onClick = { ( ) => handleVote ( i ) } disabled = { isPending } className = "px-6 py-3 bg-blue-600 text-white rounded-2xl font-black text-xs" >
73+ < div className = "flex-1" >
74+ < h3 className = "font-black text-gray-800 text-lg uppercase tracking-tight" > { c . name } </ h3 >
75+ </ div >
76+ < button
77+ onClick = { ( ) => openModal ( i ) } // Panggil openModal
78+ disabled = { isPending }
79+ className = "px-6 py-3 bg-blue-600 text-white rounded-2xl font-black text-xs active:scale-95 transition-transform"
80+ >
5581 { isPending ? "..." : "PILIH" }
5682 </ button >
5783 </ div >
5884 ) ) }
85+
5986 < p className = "text-center text-[9px] text-gray-400 font-bold uppercase py-4 tracking-widest" > Biaya Gas ditanggung Sekolah (Paymaster)</ p >
87+
88+ { /* --- MODAL KONFIRMASI TENGAH LAYAR --- */ }
89+ { isModalOpen && (
90+ < div className = "fixed inset-0 z-[100] flex items-center justify-center p-4" >
91+ { /* Latar Belakang Gelap (Overlay) */ }
92+ < div className = "absolute inset-0 bg-black/60 backdrop-blur-sm" onClick = { ( ) => setIsModalOpen ( false ) } />
93+
94+ { /* Kotak Modal */ }
95+ < div className = "relative bg-white dark:bg-zinc-900 w-full max-w-sm rounded-[32px] p-8 shadow-2xl animate-in zoom-in duration-300" >
96+ < div className = "text-center" >
97+ < div className = "w-16 h-16 bg-blue-50 dark:bg-blue-900/30 rounded-full flex items-center justify-center mx-auto mb-4" >
98+ < HowToVote className = "text-blue-600" fontSize = "large" />
99+ </ div >
100+
101+ < h3 className = "text-xl font-black text-zinc-900 dark:text-white uppercase tracking-tighter" > Konfirmasi</ h3 >
102+ < p className = "mt-2 text-sm text-zinc-500 font-medium" >
103+ Apakah Anda yakin ingin memilih < br />
104+ < span className = "font-black text-blue-600 text-lg" > "{ selectedName } "</ span > ?
105+ </ p >
106+ </ div >
107+
108+ < div className = "flex flex-col gap-3 mt-8" >
109+ < button
110+ onClick = { executeVote }
111+ className = "w-full py-4 bg-blue-600 text-white rounded-2xl font-black text-sm active:scale-95 transition-transform shadow-lg shadow-blue-200"
112+ >
113+ YA, SAYA YAKIN
114+ </ button >
115+
116+ < button
117+ onClick = { ( ) => setIsModalOpen ( false ) }
118+ className = "w-full py-4 bg-zinc-100 dark:bg-zinc-800 text-zinc-500 rounded-2xl font-bold text-sm"
119+ >
120+ BATAL
121+ </ button >
122+ </ div >
123+ </ div >
124+ </ div >
125+ ) }
60126 </ div >
61127 ) ;
62128}
0 commit comments