diff --git a/package.json b/package.json index 5c27c976..9d6f2459 100644 --- a/package.json +++ b/package.json @@ -15,11 +15,12 @@ "typecheck": "tsc" }, "dependencies": { - "@docusaurus/core": "3.7.0", + "@docusaurus/core": "^3.7.0", "@docusaurus/plugin-content-docs": "3.7.0", "@docusaurus/plugin-google-analytics": "^3.8.1", "@docusaurus/plugin-ideal-image": "3.7.0", "@docusaurus/preset-classic": "3.7.0", + "@docusaurus/theme-classic": "^3.8.1", "@docusaurus/theme-mermaid": "3.7.0", "@docusaurus/theme-search-algolia": "3.7.0", "@floating-ui/react": "^0.27.8", @@ -31,6 +32,7 @@ "@tsparticles/react": "^3.0.0", "@tsparticles/slim": "^3.8.1", "@vercel/analytics": "^1.5.0", + "canvas-confetti": "^1.9.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "dotenv": "^16.5.0", @@ -41,7 +43,7 @@ "framer-motion": "^12.7.5", "lucide-react": "^0.503.0", "prism-react-renderer": "^2.3.0", - "react": "^18.0.0", + "react": "^18.3.1", "react-dom": "^18.0.0", "react-icons": "^5.5.0", "react-slot-counter": "^3.3.1", @@ -57,6 +59,7 @@ "@docusaurus/tsconfig": "3.7.0", "@docusaurus/types": "3.7.0", "@tailwindcss/postcss": "^4.1.4", + "@types/canvas-confetti": "^1.9.0", "autoprefixer": "^10.4.21", "postcss": "^8.5.3", "tailwindcss": "^4.1.4", diff --git a/src/pages/dashboard/giveaway/index.tsx b/src/pages/dashboard/giveaway/index.tsx new file mode 100644 index 00000000..62f86adb --- /dev/null +++ b/src/pages/dashboard/giveaway/index.tsx @@ -0,0 +1,121 @@ +import React, { useEffect, useState } from 'react'; +import Layout from '@theme/Layout'; +import Head from '@docusaurus/Head'; +import type confettiType from 'canvas-confetti'; + +const GiveawayPage: React.FC = () => { + const [timeLeft, setTimeLeft] = useState({ + days: '--', + hours: '--', + minutes: '--', + seconds: '--', + }); + + const countdownTarget = new Date('2025-08-15T23:59:59').getTime(); // Update the deadline if needed + + // Countdown Timer Effect + useEffect(() => { + const interval = setInterval(() => { + const now = new Date().getTime(); + const distance = countdownTarget - now; + + if (distance <= 0) { + clearInterval(interval); + setTimeLeft({ + days: '00', + hours: '00', + minutes: '00', + seconds: '00', + }); + return; + } + + setTimeLeft({ + days: String(Math.floor(distance / (1000 * 60 * 60 * 24))), + hours: String(Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60))), + minutes: String(Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60))), + seconds: String(Math.floor((distance % (1000 * 60)) / 1000)), + }); + }, 1000); + + return () => clearInterval(interval); + }, []); + + // Confetti Effect + useEffect(() => { + const runConfetti = async () => { + const module = await import('canvas-confetti'); + const confetti = module.default as typeof confettiType; + + confetti({ + particleCount: 150, + spread: 70, + origin: { y: 0.6 }, + }); + }; + + const timer = setTimeout(runConfetti, 1000); + + return () => clearTimeout(timer); + }, []); + + return ( + + + 🎁 RecodeHive Giveaway + + +
+
+

🎉 RecodeHive Giveaway

+

Participate now and win exclusive swag, resources, and more!

+ +
+ {['days', 'hours', 'minutes', 'seconds'].map((unit) => ( +
+
{timeLeft[unit as keyof typeof timeLeft]}
+
{unit}
+
+ ))} +
+ +
+

🏆 Leaderboard

+ + + + + + + + + + + + + + + + + + + + + + + + + +
RankUsernamePoints
1OpenSourcePro1200
2CodeWizard950
3DevChampion875
+
+ +

+ Winners will be announced after the countdown ends. Stay active on the dashboard to climb up the leaderboard! +

+
+
+
+ ); +}; + +export default GiveawayPage; diff --git a/src/pages/dashboard/index.tsx b/src/pages/dashboard/index.tsx index 6ce7d7ba..1cebde16 100644 --- a/src/pages/dashboard/index.tsx +++ b/src/pages/dashboard/index.tsx @@ -66,7 +66,7 @@ const parseCSVToJSON = (csvText: string): any[] => { const DashboardContent: React.FC = () => { const location = useLocation(); const history = useHistory(); - const [activeTab, setActiveTab] = useState<'home' | 'discuss' | 'leaderboard'>('home'); + const [activeTab, setActiveTab] = useState<'home' | 'discuss' | 'leaderboard'|'giveaway'>('home'); const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false); const [leaderboardData, setLeaderboardData] = useState([]); const [isLoadingLeaderboard, setIsLoadingLeaderboard] = useState(false); @@ -78,7 +78,10 @@ const DashboardContent: React.FC = () => { setActiveTab('discuss'); } else if (location.hash === '#leaderboard') { setActiveTab('leaderboard'); - } else { + } else if (location.hash === '#giveaway'){ + setActiveTab('giveaway'); + } + else { setActiveTab('home'); } }, [location]); @@ -220,7 +223,7 @@ const DashboardContent: React.FC = () => { return achievements.slice(0, 3); // Limit to 3 achievements for UI }; - const handleTabChange = (tab: 'home' | 'discuss' | 'leaderboard') => { + const handleTabChange = (tab: 'home' | 'discuss' | 'leaderboard' | 'giveaway') => { setActiveTab(tab); if (tab === 'discuss') { history.push('#discuss'); @@ -228,7 +231,11 @@ const DashboardContent: React.FC = () => { } else if (tab === 'leaderboard') { history.push('#leaderboard'); window.scrollTo(0, 0); - } else { + } else if (tab === 'giveaway'){ + history.push('/dashboard/giveaway'); + window.scrollTo(0 , 0); + } + else { history.push('#'); } }; @@ -440,6 +447,16 @@ const DashboardContent: React.FC = () => { 🏆 Leaderboard + +
  • handleTabChange + ('giveaway')} + > + 🎁 + Giveaway +
  • +
    - ) : ( + ) : activeTab === 'leaderboard' ? ( /* Leaderboard Tab */
    { )}
    + ) : activeTab === 'giveaway' && ( + // ✅ Giveaway Section 🎁 + <> + +
    +

    + 🎁 Giveaway +

    +

    Participate in exclusive giveaways and win exciting prizes!

    +
    +
    + + {/* 🎉 Giveaway Stats Grid */} + +
    + + + + +
    +
    + + )} diff --git a/tsconfig.json b/tsconfig.json index c7640365..cb476e31 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,6 +4,9 @@ "compilerOptions": { "jsx": "react", "baseUrl": ".", + "esModuleInterop": true, + "moduleResolution": "node", + "resolveJsonModule": true }, "exclude": [".docusaurus", "build"]