diff --git a/front-end/package-lock.json b/front-end/package-lock.json index 3cc8731..81bcbbc 100644 --- a/front-end/package-lock.json +++ b/front-end/package-lock.json @@ -20,6 +20,7 @@ "dotenv": "^16.4.7", "framer-motion": "^11.18.1", "gsap": "^3.12.7", + "html-to-image": "^1.11.11", "jsonwebtoken": "^9.0.2", "lucide-react": "^0.475.0", "react": "^18.2.0", @@ -12022,6 +12023,12 @@ "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", "license": "MIT" }, + "node_modules/html-to-image": { + "version": "1.11.11", + "resolved": "https://registry.npmjs.org/html-to-image/-/html-to-image-1.11.11.tgz", + "integrity": "sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA==", + "license": "MIT" + }, "node_modules/html-url-attributes": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", diff --git a/front-end/package.json b/front-end/package.json index 21ed34e..2907be3 100644 --- a/front-end/package.json +++ b/front-end/package.json @@ -26,6 +26,7 @@ "dotenv": "^16.4.7", "framer-motion": "^11.18.1", "gsap": "^3.12.7", + "html-to-image": "^1.11.11", "jsonwebtoken": "^9.0.2", "lucide-react": "^0.475.0", "react": "^18.2.0", diff --git a/front-end/src/pages/CertificateGenerator.jsx b/front-end/src/pages/CertificateGenerator.jsx new file mode 100644 index 0000000..9d0ab70 --- /dev/null +++ b/front-end/src/pages/CertificateGenerator.jsx @@ -0,0 +1,110 @@ +import React, { useRef, useEffect, useState } from "react"; +import { toPng } from "html-to-image"; +import backgroundImage from "../assets/logo.png"; + +const CertificateGenerator = ({ username }) => { + const certificateRef = useRef(null); + const [profile, setProfile] = useState(null); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchGitHubProfile = async () => { + if (!username) { + setError("Username is missing."); + return; + } + + setLoading(true); + setError(null); + + try { + const response = await fetch(`https://api.github.com/users/${username}`); + if (!response.ok) throw new Error("GitHub profile not found"); + + const data = await response.json(); + setProfile(data); + } catch (err) { + setError(err.message); + setProfile(null); + } finally { + setLoading(false); + } + }; + + fetchGitHubProfile(); + }, [username]); + + const handleDownloadCertificate = async () => { + if (certificateRef.current) { + const dataUrl = await toPng(certificateRef.current); + const link = document.createElement("a"); + link.href = dataUrl; + link.download = `${username}_certificate.png`; + link.click(); + } + }; + + return ( +
+
+
+ +

+ Certificate of Appreciation +

+

This is proudly presented to

+ + {loading &&

Fetching GitHub Profile...

} + {error &&

{error}

} + + {profile && !loading && ( +
+ + {`${username}'s + +

+ {profile.name || username} +

+

@{username}

+
+ )} + +

+ For valuable contributions to react-blog Project in Social Winter of Code (SWoC) from January 1, 2025 to March 1, 2025. +

+ +
+
+

Project Mentor

+

OkenHaha

+
+
+

Date

+

{new Date().toLocaleDateString()}

+
+
+
+ + +
+ ); +}; + +export default CertificateGenerator; \ No newline at end of file diff --git a/front-end/src/pages/Contributors.jsx b/front-end/src/pages/Contributors.jsx index 363def6..357d4b5 100644 --- a/front-end/src/pages/Contributors.jsx +++ b/front-end/src/pages/Contributors.jsx @@ -1,21 +1,24 @@ -import React, { useEffect, useState } from 'react'; -import { getContributors } from '../components/contributors/contribution.js'; -import { useWindowSize } from 'react-use'; -import Confetti from 'react-confetti'; +import React, { useEffect, useState } from "react"; +import CertificateGenerator from "./CertificateGenerator"; +import { getContributors } from "../components/contributors/contribution.js"; +import { useWindowSize } from "react-use"; +import Confetti from "react-confetti"; +import { XCircle } from "lucide-react"; // Import icon for close button const Contributors = () => { const [data, setData] = useState([]); const [showConfetti, setShowConfetti] = useState(true); + const [selectedContributor, setSelectedContributor] = useState(null); const { width, height } = useWindowSize(); - const getData = async () => { - const res = await getContributors({}); - if (res) { - setData(res); - } - }; - useEffect(() => { + const getData = async () => { + const res = await getContributors({}); + if (res) { + setData(res); + } + }; + getData(); const timer = setTimeout(() => { setShowConfetti(false); @@ -23,21 +26,19 @@ const Contributors = () => { return () => clearTimeout(timer); }, []); + const handleAddCertificate = (contributor) => { + setSelectedContributor(contributor); + }; + return ( -
+
{showConfetti && ( - + )} - -
+ +
-

+

Contributors

@@ -45,40 +46,64 @@ const Contributors = () => {

+ {/* Contributor Cards */} + + {/* Certificate Generator Modal */} + {selectedContributor && ( +
+
+ {/* Close Button */} + + +

+ Certificate for {selectedContributor.login} +

+ +
+
+ )}
); }; -export default Contributors; \ No newline at end of file +export default Contributors; diff --git a/package-lock.json b/package-lock.json index 1c2115f..9a7f64b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "react-blog", + "name": "react-blog-Swoc25", "lockfileVersion": 3, "requires": true, "packages": { @@ -7,6 +7,7 @@ "dependencies": { "dotenv": "^16.4.7", "express-session": "^1.18.1", + "lucide-react": "^0.474.0", "nodemailer": "^6.9.16", "otp-generator": "^4.0.1", "react-toastify": "^11.0.2" @@ -842,6 +843,15 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/lucide-react": { + "version": "0.474.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.474.0.tgz", + "integrity": "sha512-CmghgHkh0OJNmxGKWc0qfPJCYHASPMVSyGY8fj3xgk4v84ItqDg64JNKFZn5hC6E0vHi6gxnbCgwhyVB09wQtA==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",