Skip to content

Commit ba1adce

Browse files
authored
Merge pull request #219 from Samridha0305/giveaway-tab
Add Giveaway Page with Timer & Leaderboard
2 parents e5fdebb + 5847c3f commit ba1adce

File tree

4 files changed

+204
-7
lines changed

4 files changed

+204
-7
lines changed

package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@
1515
"typecheck": "tsc"
1616
},
1717
"dependencies": {
18-
"@docusaurus/core": "3.7.0",
18+
"@docusaurus/core": "^3.7.0",
1919
"@docusaurus/plugin-content-docs": "3.7.0",
2020
"@docusaurus/plugin-google-analytics": "^3.8.1",
2121
"@docusaurus/plugin-ideal-image": "3.7.0",
2222
"@docusaurus/preset-classic": "3.7.0",
23+
"@docusaurus/theme-classic": "^3.8.1",
2324
"@docusaurus/theme-mermaid": "3.7.0",
2425
"@docusaurus/theme-search-algolia": "3.7.0",
2526
"@floating-ui/react": "^0.27.8",
@@ -31,6 +32,7 @@
3132
"@tsparticles/react": "^3.0.0",
3233
"@tsparticles/slim": "^3.8.1",
3334
"@vercel/analytics": "^1.5.0",
35+
"canvas-confetti": "^1.9.3",
3436
"class-variance-authority": "^0.7.1",
3537
"clsx": "^2.1.1",
3638
"dotenv": "^16.5.0",
@@ -41,7 +43,7 @@
4143
"framer-motion": "^12.7.5",
4244
"lucide-react": "^0.503.0",
4345
"prism-react-renderer": "^2.3.0",
44-
"react": "^18.0.0",
46+
"react": "^18.3.1",
4547
"react-dom": "^18.0.0",
4648
"react-icons": "^5.5.0",
4749
"react-slot-counter": "^3.3.1",
@@ -57,6 +59,7 @@
5759
"@docusaurus/tsconfig": "3.7.0",
5860
"@docusaurus/types": "3.7.0",
5961
"@tailwindcss/postcss": "^4.1.4",
62+
"@types/canvas-confetti": "^1.9.0",
6063
"autoprefixer": "^10.4.21",
6164
"postcss": "^8.5.3",
6265
"tailwindcss": "^4.1.4",
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import React, { useEffect, useState } from 'react';
2+
import Layout from '@theme/Layout';
3+
import Head from '@docusaurus/Head';
4+
import type confettiType from 'canvas-confetti';
5+
6+
const GiveawayPage: React.FC = () => {
7+
const [timeLeft, setTimeLeft] = useState({
8+
days: '--',
9+
hours: '--',
10+
minutes: '--',
11+
seconds: '--',
12+
});
13+
14+
const countdownTarget = new Date('2025-08-15T23:59:59').getTime(); // Update the deadline if needed
15+
16+
// Countdown Timer Effect
17+
useEffect(() => {
18+
const interval = setInterval(() => {
19+
const now = new Date().getTime();
20+
const distance = countdownTarget - now;
21+
22+
if (distance <= 0) {
23+
clearInterval(interval);
24+
setTimeLeft({
25+
days: '00',
26+
hours: '00',
27+
minutes: '00',
28+
seconds: '00',
29+
});
30+
return;
31+
}
32+
33+
setTimeLeft({
34+
days: String(Math.floor(distance / (1000 * 60 * 60 * 24))),
35+
hours: String(Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60))),
36+
minutes: String(Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60))),
37+
seconds: String(Math.floor((distance % (1000 * 60)) / 1000)),
38+
});
39+
}, 1000);
40+
41+
return () => clearInterval(interval);
42+
}, []);
43+
44+
// Confetti Effect
45+
useEffect(() => {
46+
const runConfetti = async () => {
47+
const module = await import('canvas-confetti');
48+
const confetti = module.default as typeof confettiType;
49+
50+
confetti({
51+
particleCount: 150,
52+
spread: 70,
53+
origin: { y: 0.6 },
54+
});
55+
};
56+
57+
const timer = setTimeout(runConfetti, 1000);
58+
59+
return () => clearTimeout(timer);
60+
}, []);
61+
62+
return (
63+
<Layout>
64+
<Head>
65+
<title>🎁 RecodeHive Giveaway</title>
66+
</Head>
67+
68+
<div className="min-h-screen bg-gradient-to-br from-[#0f0c29] via-[#302b63] to-[#24243e] text-white py-10 px-6">
69+
<div className="max-w-4xl mx-auto text-center">
70+
<h1 className="text-4xl sm:text-5xl font-bold mb-6">🎉 RecodeHive Giveaway</h1>
71+
<p className="text-lg mb-8">Participate now and win exclusive swag, resources, and more!</p>
72+
73+
<div className="flex justify-center gap-4 text-center mb-12">
74+
{['days', 'hours', 'minutes', 'seconds'].map((unit) => (
75+
<div key={unit} className="bg-white/10 px-6 py-4 rounded-xl shadow-md">
76+
<div className="text-3xl font-bold">{timeLeft[unit as keyof typeof timeLeft]}</div>
77+
<div className="text-sm uppercase tracking-widest">{unit}</div>
78+
</div>
79+
))}
80+
</div>
81+
82+
<div className="bg-white/10 p-6 rounded-xl shadow-xl mb-10">
83+
<h2 className="text-2xl font-semibold mb-4">🏆 Leaderboard</h2>
84+
<table className="w-full text-left border-collapse">
85+
<thead>
86+
<tr className="border-b border-white/20">
87+
<th className="pb-2">Rank</th>
88+
<th className="pb-2">Username</th>
89+
<th className="pb-2">Points</th>
90+
</tr>
91+
</thead>
92+
<tbody>
93+
<tr className="border-b border-white/10">
94+
<td>1</td>
95+
<td>OpenSourcePro</td>
96+
<td>1200</td>
97+
</tr>
98+
<tr className="border-b border-white/10">
99+
<td>2</td>
100+
<td>CodeWizard</td>
101+
<td>950</td>
102+
</tr>
103+
<tr>
104+
<td>3</td>
105+
<td>DevChampion</td>
106+
<td>875</td>
107+
</tr>
108+
</tbody>
109+
</table>
110+
</div>
111+
112+
<p className="text-sm text-white text-opacity-60 italic">
113+
Winners will be announced after the countdown ends. Stay active on the dashboard to climb up the leaderboard!
114+
</p>
115+
</div>
116+
</div>
117+
</Layout>
118+
);
119+
};
120+
121+
export default GiveawayPage;

src/pages/dashboard/index.tsx

Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ const parseCSVToJSON = (csvText: string): any[] => {
6666
const DashboardContent: React.FC = () => {
6767
const location = useLocation();
6868
const history = useHistory();
69-
const [activeTab, setActiveTab] = useState<'home' | 'discuss' | 'leaderboard'>('home');
69+
const [activeTab, setActiveTab] = useState<'home' | 'discuss' | 'leaderboard'|'giveaway'>('home');
7070
const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false);
7171
const [leaderboardData, setLeaderboardData] = useState<LeaderboardEntry[]>([]);
7272
const [isLoadingLeaderboard, setIsLoadingLeaderboard] = useState(false);
@@ -78,7 +78,10 @@ const DashboardContent: React.FC = () => {
7878
setActiveTab('discuss');
7979
} else if (location.hash === '#leaderboard') {
8080
setActiveTab('leaderboard');
81-
} else {
81+
} else if (location.hash === '#giveaway'){
82+
setActiveTab('giveaway');
83+
}
84+
else {
8285
setActiveTab('home');
8386
}
8487
}, [location]);
@@ -220,15 +223,19 @@ const DashboardContent: React.FC = () => {
220223
return achievements.slice(0, 3); // Limit to 3 achievements for UI
221224
};
222225

223-
const handleTabChange = (tab: 'home' | 'discuss' | 'leaderboard') => {
226+
const handleTabChange = (tab: 'home' | 'discuss' | 'leaderboard' | 'giveaway') => {
224227
setActiveTab(tab);
225228
if (tab === 'discuss') {
226229
history.push('#discuss');
227230
window.scrollTo(0, 0);
228231
} else if (tab === 'leaderboard') {
229232
history.push('#leaderboard');
230233
window.scrollTo(0, 0);
231-
} else {
234+
} else if (tab === 'giveaway'){
235+
history.push('/dashboard/giveaway');
236+
window.scrollTo(0 , 0);
237+
}
238+
else {
232239
history.push('#');
233240
}
234241
};
@@ -440,6 +447,16 @@ const DashboardContent: React.FC = () => {
440447
<span className="nav-icon">🏆</span>
441448
<span className="nav-text">Leaderboard</span>
442449
</li>
450+
451+
<li
452+
className={`nav-item ${activeTab === 'giveaway' ? 'active' : ''}`}
453+
onClick={() => handleTabChange
454+
('giveaway')}
455+
>
456+
<span className="nav-icon">🎁</span>
457+
<span className="nav-text">Giveaway</span>
458+
</li>
459+
443460
</ul>
444461
<div className="sidebar-footer">
445462
<button
@@ -594,7 +611,7 @@ const DashboardContent: React.FC = () => {
594611
/>
595612
</div>
596613
</div>
597-
) : (
614+
) : activeTab === 'leaderboard' ? (
598615
/* Leaderboard Tab */
599616
<div className="leaderboard-page-container">
600617
<motion.div
@@ -775,6 +792,59 @@ const DashboardContent: React.FC = () => {
775792
</motion.div>
776793
)}
777794
</div>
795+
) : activeTab === 'giveaway' && (
796+
// ✅ Giveaway Section 🎁
797+
<>
798+
<motion.section className="dashboard-hero" initial={{ opacity: 0, y: 10 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.8 }}>
799+
<div className="hero-content">
800+
<h1 className="dashboard-title">
801+
🎁 <span className="highlight">Giveaway</span>
802+
</h1>
803+
<p className="dashboard-subtitle">Participate in exclusive giveaways and win exciting prizes!</p>
804+
</div>
805+
</motion.section>
806+
807+
{/* 🎉 Giveaway Stats Grid */}
808+
<motion.section
809+
className="dashboard-stats-section"
810+
initial={{ opacity: 0, y: 10 }}
811+
whileInView={{ opacity: 1 }}
812+
transition={{ duration: 0.6 }}
813+
viewport={{ once: true }}
814+
>
815+
<div className="dashboard-stats-grid">
816+
<StatCard
817+
icon="⏳"
818+
title="Next Giveaway"
819+
value="5 Days"
820+
valueText="5 Days Left"
821+
description="Time remaining"
822+
/>
823+
<StatCard
824+
icon="🎫"
825+
title="Entries"
826+
value="1420"
827+
valueText="1,420"
828+
description="Total participants"
829+
/>
830+
<StatCard
831+
icon="📈"
832+
title="Your Rank"
833+
value="32"
834+
valueText="Rank 32"
835+
description="Based on your contribution"
836+
/>
837+
<StatCard
838+
icon="🏅"
839+
title="Total Winners"
840+
value="10"
841+
valueText="10 Winners"
842+
description="Winners per giveaway"
843+
/>
844+
</div>
845+
</motion.section>
846+
</>
847+
778848
)}
779849
</main>
780850
</div>

tsconfig.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
"compilerOptions": {
55
"jsx": "react",
66
"baseUrl": ".",
7+
"esModuleInterop": true,
8+
"moduleResolution": "node",
9+
"resolveJsonModule": true
710

811
},
912
"exclude": [".docusaurus", "build"]

0 commit comments

Comments
 (0)