11"use client" ;
22
3- import { useState } from "react" ; // Tambahkan useState
3+ import { useState } from "react" ;
44import { useReadContract , useSendCalls } from "wagmi" ;
55import { encodeFunctionData } from "viem" ;
66import { CONTRACT_ADDRESS , CLASS_VOTE_ABI , BUILDER_CODE_HEX } from "~/app/constants" ;
7- import { HowToVote } from "@mui/icons-material" ; // Import ikon untuk modal
7+ import { HowToVote } from "@mui/icons-material" ;
88
99export default function VoteCard ( ) {
1010 const { sendCalls, isPending } = useSendCalls ( ) ;
11-
12- // --- STATE UNTUK MODAL ---
1311 const [ isModalOpen , setIsModalOpen ] = useState ( false ) ;
1412 const [ selectedIndex , setSelectedIndex ] = useState < number | null > ( null ) ;
1513
1614 const { data : candidates , refetch } = useReadContract ( {
17- abi : CLASS_VOTE_ABI , address : CONTRACT_ADDRESS , functionName : "getCandidates" ,
15+ abi : CLASS_VOTE_ABI ,
16+ address : CONTRACT_ADDRESS ,
17+ functionName : "getCandidates" ,
1818 } ) ;
1919
2020 const { data : pollTitle } = useReadContract ( {
21- abi : CLASS_VOTE_ABI , address : CONTRACT_ADDRESS , functionName : "pollTitle" ,
21+ abi : CLASS_VOTE_ABI ,
22+ address : CONTRACT_ADDRESS ,
23+ functionName : "pollTitle" ,
2224 } ) ;
2325
24- // 1. Fungsi untuk MEMBUKA Modal
26+ // Fungsi untuk membuka modal konfirmasi
2527 const openModal = ( index : number ) => {
2628 setSelectedIndex ( index ) ;
2729 setIsModalOpen ( true ) ;
2830 } ;
2931
30- // 2. Fungsi untuk EKSEKUSI Vote (setelah klik "YA" di modal )
32+ // Fungsi eksekusi transaksi (Tanpa Alert Menengah )
3133 const executeVote = async ( ) => {
3234 if ( selectedIndex === null ) return ;
3335
@@ -47,78 +49,96 @@ export default function VoteCard() {
4749 capabilities : { paymasterService : { url : paymasterUrl } }
4850 } ) ;
4951
50- setIsModalOpen ( false ) ; // Tutup modal
51- alert ( "Suara sedang diproses secara gasless!" ) ;
52+ // Langsung tutup modal agar jendela transaksi (Wallet) bisa muncul tanpa halangan
53+ setIsModalOpen ( false ) ;
54+
55+ // Refetch data setelah beberapa detik untuk memperbarui UI
5256 setTimeout ( ( ) => refetch ( ) , 3000 ) ;
5357 } catch ( e ) {
54- alert ( "Gagal voting." ) ;
58+ // Alert hanya muncul jika benar-benar ada kesalahan teknis
59+ alert ( "Gagal mengirim suara. Silakan periksa koneksi." ) ;
5560 setIsModalOpen ( false ) ;
5661 }
5762 } ;
5863
5964 if ( ! candidates || ( candidates as any ) . length === 0 ) return null ;
6065
61- const selectedName = selectedIndex !== null ? ( candidates as any [ ] ) [ selectedIndex ] ?. name : "" ;
66+ const selectedCandidate = selectedIndex !== null ? ( candidates as any [ ] ) [ selectedIndex ] : null ;
6267
6368 return (
6469 < div className = "space-y-4" >
6570 < h2 className = "text-xl font-black text-center text-blue-600 mb-6 uppercase tracking-tight" >
6671 { pollTitle as string || "MEMUAT JUDUL..." }
6772 </ h2 >
6873
69- { /* DAFTAR KANDIDAT */ }
74+ { /* LIST KANDIDAT */ }
7075 { ( candidates as any [ ] ) . map ( ( c , i ) => (
7176 < div key = { i } className = "bg-white p-4 rounded-[28px] border flex items-center gap-4 shadow-sm" >
72- < img src = { c . photoUrl || "https://via.placeholder.com/150" } className = "w-20 h-20 rounded-2xl object-cover border" alt = { c . name } />
77+ < img
78+ src = { c . photoUrl || "https://via.placeholder.com/150" }
79+ className = "w-20 h-20 rounded-2xl object-cover border"
80+ alt = { c . name }
81+ />
7382 < div className = "flex-1" >
7483 < h3 className = "font-black text-gray-800 text-lg uppercase tracking-tight" > { c . name } </ h3 >
7584 </ div >
7685 < button
77- onClick = { ( ) => openModal ( i ) } // Panggil openModal
86+ onClick = { ( ) => openModal ( i ) }
7887 disabled = { isPending }
79- className = "px-6 py-3 bg-blue-600 text-white rounded-2xl font-black text-xs active:scale-95 transition-transform "
88+ className = "px-6 py-3 bg-blue-600 text-white rounded-2xl font-black text-xs active:scale-95 transition-all disabled:bg-blue-300 "
8089 >
81- { isPending ? "..." : " PILIH" }
90+ PILIH
8291 </ button >
8392 </ div >
8493 ) ) }
8594
86- < p className = "text-center text-[9px] text-gray-400 font-bold uppercase py-4 tracking-widest" > Biaya Gas ditanggung Sekolah (Paymaster)</ p >
95+ < p className = "text-center text-[9px] text-gray-400 font-bold uppercase py-4 tracking-widest" >
96+ Biaya Gas ditanggung Sekolah (Paymaster)
97+ </ p >
8798
88- { /* --- MODAL KONFIRMASI TENGAH LAYAR --- */ }
99+ { /* MODAL KONFIRMASI (CENTERED POP-UP) */ }
89100 { isModalOpen && (
90101 < 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 ) } />
102+ < div className = "absolute inset-0 bg-black/60 backdrop-blur-sm" onClick = { ( ) => ! isPending && setIsModalOpen ( false ) } />
93103
94- { /* Kotak Modal */ }
95104 < 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" >
96105 < div className = "text-center" >
97106 < 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" >
98107 < HowToVote className = "text-blue-600" fontSize = "large" />
99108 </ div >
100109
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" >
110+ < h3 className = "text-xl font-black text-zinc-900 dark:text-white uppercase tracking-tighter leading-tight" >
111+ Konfirmasi Pilihan
112+ </ h3 >
113+ < p className = "mt-2 text-sm text-zinc-500 font-medium leading-relaxed" >
103114 Apakah Anda yakin ingin memilih < br />
104- < span className = "font-black text-blue-600 text-lg" > "{ selectedName } "</ span > ?
115+ < span className = "font-black text-blue-600 text-lg uppercase " > "{ selectedCandidate ?. name } "</ span > ?
105116 </ p >
106117 </ div >
107118
108119 < div className = "flex flex-col gap-3 mt-8" >
109120 < button
110121 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"
122+ disabled = { isPending } // Matikan tombol selama loading
123+ className = "w-full py-4 bg-blue-600 text-white rounded-2xl font-black text-sm active:scale-95 transition-all shadow-lg shadow-blue-200 disabled:bg-blue-400"
112124 >
113- YA, SAYA YAKIN
125+ { /* TAMPILAN STATUS LOADING */ }
126+ { isPending ? (
127+ < span className = "flex items-center justify-center gap-2" >
128+ < span className = "w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" />
129+ MENGIRIM...
130+ </ span >
131+ ) : "YA, SAYA YAKIN" }
114132 </ button >
115133
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 >
134+ { ! isPending && (
135+ < button
136+ onClick = { ( ) => setIsModalOpen ( false ) }
137+ className = "w-full py-4 bg-zinc-100 dark:bg-zinc-800 text-zinc-500 rounded-2xl font-bold text-sm"
138+ >
139+ BATAL
140+ </ button >
141+ ) }
122142 </ div >
123143 </ div >
124144 </ div >
0 commit comments