Skip to content

Commit ce4aff0

Browse files
authored
Merge pull request #8 from Avalanche-Team1-DAO-Kenya/JB/Home
adds winners section
2 parents 41a06a9 + b1e1f5c commit ce4aff0

File tree

7 files changed

+614
-30
lines changed

7 files changed

+614
-30
lines changed

src/App.css

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,32 @@
1313
margin: 0;
1414
max-width: none;
1515
}
16+
:root {
17+
--cyber-secondary: rgba(0, 255, 255, 0.2); /* Default cyber color */
18+
--grid-size: 50px; /* Adjust grid spacing */
19+
}
20+
21+
.grid-overlay {
22+
position: fixed;
23+
top: 0;
24+
left: 0;
25+
right: 0;
26+
bottom: 0;
27+
background-image:
28+
linear-gradient(var(--cyber-secondary) 1px, transparent 1px),
29+
linear-gradient(90deg, var(--cyber-secondary) 1px, transparent 1px);
30+
background-size: var(--grid-size) var(--grid-size);
31+
opacity: 0.3; /* Increased opacity for testing */
32+
pointer-events: none;
33+
z-index: 0; /* Make sure it's on top of the background */
34+
animation: grid-pulse 4s infinite;
35+
}
36+
37+
@keyframes grid-pulse {
38+
0%, 100% { opacity: 0.3; }
39+
50% { opacity: 0.5; } /* Increased opacity during pulse */
40+
}
41+
1642

1743
* {
1844
margin: 0;

src/App.jsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import Home from './pages/home'
44
import Navbar from './Components/Navbar'
55
import Footer from './Components/Footer'
66
import AdminDashboard from './pages/AdminDashboard'
7+
import WinnersPage from './Components/Winner'
78

89

910
import { Route, Routes } from 'react-router-dom';
@@ -12,21 +13,26 @@ function App() {
1213

1314
return (
1415
<>
15-
<div className="app-container">
16+
<div className="app-container">
1617
<Navbar />
18+
1719
<main className="main-content">
20+
{/* Move grid overlay outside of Routes */}
21+
<div className='grid-overlay'></div>
22+
1823
<Routes>
1924
<Route path="/" element={<Home />} />
2025
<Route path="/buy-tickets" element={<Ticket />} />
2126
<Route path="/admin" element={<AdminDashboard />} />
22-
23-
27+
<Route path="/winner" element={<WinnersPage />} />
2428
</Routes>
2529
</main>
30+
2631
<Footer />
2732
</div>
2833
</>
29-
)
34+
);
35+
3036
}
3137

3238
export default App

src/Components/Navbar.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ const Navbar = () => {
5151
const navLinks = [
5252
{ path: '/', label: 'Home' },
5353
{ path: '/buy-tickets', label: 'Buy Tickets' },
54-
// { path: '/winner', label: 'Winner' },
54+
{ path: '/winner', label: 'Winner' },
5555

5656

5757
{ path: '/admin', label: 'Admin' }

src/Components/Winner.jsx

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
import React, { useState, useEffect } from 'react';
2+
import { ethers } from 'ethers';
3+
import "./css/Winners.css"
4+
5+
const CONTRACT_ADDRESS = '0xeADD42c14c50397E64b4dc94a4beD91175c1E011';
6+
const ABI = [
7+
{
8+
"anonymous": false,
9+
"inputs": [
10+
{
11+
"indexed": true,
12+
"internalType": "address",
13+
"name": "winner",
14+
"type": "address"
15+
},
16+
{
17+
"indexed": true,
18+
"internalType": "address",
19+
"name": "token",
20+
"type": "address"
21+
},
22+
{
23+
"indexed": false,
24+
"internalType": "uint256",
25+
"name": "prize",
26+
"type": "uint256"
27+
}
28+
],
29+
"name": "WinnerSelected",
30+
"type": "event"
31+
},
32+
{
33+
"inputs": [],
34+
"name": "getTokens",
35+
"outputs": [
36+
{
37+
"internalType": "address[]",
38+
"name": "",
39+
"type": "address[]"
40+
}
41+
],
42+
"stateMutability": "view",
43+
"type": "function"
44+
},
45+
{
46+
"inputs": [],
47+
"name": "lotteryActive",
48+
"outputs": [
49+
{
50+
"internalType": "bool",
51+
"name": "",
52+
"type": "bool"
53+
}
54+
],
55+
"stateMutability": "view",
56+
"type": "function"
57+
}
58+
];
59+
60+
const generateBlockchainImage = (seed) => {
61+
const colors = ['#1a237e', '#ffd700', '#4a90e2', '#c2185b', '#ffa726'];
62+
const randomColor = colors[Math.abs(seed.charCodeAt(0)) % colors.length];
63+
64+
return `data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">
65+
<defs>
66+
<pattern id="grid" width="20" height="20" patternUnits="userSpaceOnUse">
67+
<path d="M 20 0 L 0 0 0 20" fill="none" stroke="${randomColor}" strokeOpacity="0.2" strokeWidth="1"/>
68+
</pattern>
69+
<radialGradient id="glow" cx="50%" cy="50%" r="50%" fx="50%" fy="50%">
70+
<stop offset="0%" style="stop-color:${randomColor};stop-opacity:0.5"/>
71+
<stop offset="100%" style="stop-color:${randomColor};stop-opacity:0"/>
72+
</radialGradient>
73+
</defs>
74+
<rect width="200" height="200" fill="url(#grid)"/>
75+
<circle cx="100" cy="100" r="80" fill="url(#glow)"/>
76+
<polygon points="100,20 180,100 100,180 20,100" fill="${randomColor}" fillOpacity="0.3" stroke="${randomColor}" strokeWidth="2"/>
77+
</svg>`;
78+
};
79+
80+
const WinnersPage = () => {
81+
const [winners, setWinners] = useState([]);
82+
const [latestWinner, setLatestWinner] = useState(null);
83+
const [showNotification, setShowNotification] = useState(false);
84+
const [isConnected, setIsConnected] = useState(false);
85+
const [loading, setLoading] = useState(false);
86+
const [error, setError] = useState('');
87+
const [contract, setContract] = useState(null);
88+
const [account, setAccount] = useState('');
89+
90+
const connectWallet = async () => {
91+
try {
92+
if (typeof window.ethereum === 'undefined') {
93+
throw new Error('MetaMask is not installed');
94+
}
95+
96+
setLoading(true);
97+
const accounts = await window.ethereum.request({
98+
method: 'eth_requestAccounts'
99+
});
100+
101+
const provider = new ethers.BrowserProvider(window.ethereum);
102+
const signer = await provider.getSigner();
103+
const newContract = new ethers.Contract(CONTRACT_ADDRESS, ABI, signer);
104+
105+
setContract(newContract);
106+
setAccount(accounts[0]);
107+
setIsConnected(true);
108+
setLoading(false);
109+
110+
} catch (err) {
111+
setError(err.message || 'Failed to connect wallet');
112+
setLoading(false);
113+
console.error('Connection error:', err);
114+
}
115+
};
116+
117+
useEffect(() => {
118+
if (!contract) return;
119+
120+
const handleWinnerSelected = (winner, token, prize, event) => {
121+
const newWinner = {
122+
address: winner,
123+
token: token,
124+
prize: ethers.formatUnits(prize, 18),
125+
date: new Date().toLocaleDateString(),
126+
image: generateBlockchainImage(winner),
127+
transactionHash: event.transactionHash
128+
};
129+
130+
setWinners(prev => [newWinner, ...prev]);
131+
setLatestWinner(newWinner);
132+
setShowNotification(true);
133+
setTimeout(() => setShowNotification(false), 5000);
134+
};
135+
136+
contract.on('WinnerSelected', handleWinnerSelected);
137+
138+
return () => {
139+
contract.off('WinnerSelected', handleWinnerSelected);
140+
};
141+
}, [contract]);
142+
143+
useEffect(() => {
144+
if (window.ethereum) {
145+
window.ethereum.on('accountsChanged', (accounts) => {
146+
if (accounts.length > 0) {
147+
setAccount(accounts[0]);
148+
} else {
149+
setIsConnected(false);
150+
setAccount('');
151+
}
152+
});
153+
}
154+
155+
return () => {
156+
if (window.ethereum) {
157+
window.ethereum.removeListener('accountsChanged', () => {});
158+
}
159+
};
160+
}, []);
161+
162+
const NoWinnersDisplay = () => (
163+
<div className="no-winners-container">
164+
<div className="hologram-coin"></div>
165+
<h2 className="future-text">No Winners Yet... The Future Awaits!</h2>
166+
<p className="scroll-text">Be the first to make history in the lottery!</p>
167+
<button className="cyber-button" onClick={connectWallet} disabled={loading}>
168+
{loading ? 'Initializing...' : 'Enter Now & Win Big'}
169+
<span className="cyber-button-glitch"></span>
170+
</button>
171+
</div>
172+
);
173+
174+
return (
175+
<div className="cyber-container">
176+
<div className="grid-overlays"></div>
177+
<div className="node-animation"></div>
178+
179+
{!isConnected && (
180+
<div className="connect-section">
181+
<NoWinnersDisplay />
182+
{error && <div className="cyber-error">{error}</div>}
183+
</div>
184+
)}
185+
186+
{showNotification && latestWinner && (
187+
<div className="winner-notification">
188+
🎉 New Winner: {latestWinner.address.substring(0, 6)}...{latestWinner.address.substring(38)}
189+
won {latestWinner.prize} tokens!
190+
</div>
191+
)}
192+
193+
<h1 className="cyber-title">🏆 Cyber Lottery Winners</h1>
194+
195+
{isConnected && loading && (
196+
<div className="loading-container">
197+
<div className="cyber-loader"></div>
198+
<p>Initializing blockchain connection...</p>
199+
</div>
200+
)}
201+
202+
{isConnected && !loading && winners.length === 0 && <NoWinnersDisplay />}
203+
204+
{isConnected && winners.length > 0 && (
205+
<div className="winners-grid">
206+
{winners.map((winner, index) => (
207+
<div key={index} className="winner-card">
208+
<div className="card-hologram">
209+
<img
210+
src={winner.image}
211+
alt="Winner's blockchain pattern"
212+
className="winner-image"
213+
/>
214+
</div>
215+
<div className="winner-details">
216+
<h2 className="winner-address">
217+
{winner.address.substring(0, 6)}...{winner.address.substring(38)}
218+
</h2>
219+
<p className="winner-prize">
220+
{winner.prize}
221+
<span className="token-address">
222+
({winner.token.substring(0, 6)}...{winner.token.substring(38)})
223+
</span>
224+
</p>
225+
<p className="winner-date">{winner.date}</p>
226+
<a
227+
href={`https://etherscan.io/tx/${winner.transactionHash}`}
228+
target="_blank"
229+
rel="noopener noreferrer"
230+
className="transaction-link"
231+
>
232+
View Transaction
233+
</a>
234+
</div>
235+
</div>
236+
))}
237+
</div>
238+
)}
239+
</div>
240+
);
241+
};
242+
243+
export default WinnersPage;

0 commit comments

Comments
 (0)