-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
447 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,10 @@ | ||
/** @type {import('next').NextConfig} */ | ||
const nextConfig = { | ||
output: "standalone", | ||
trailingSlash: true, | ||
images: { | ||
domains: ["127.0.0.1", "localhost"], // Allow images from 127.0.0.1 (localhost) | ||
}, | ||
}; | ||
|
||
export default nextConfig; |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import Image from "next/image"; | ||
import Navbar from "@/app/components/navbar"; | ||
import About from "@/app/components/about"; | ||
import { Raleway, JetBrains_Mono } from "next/font/google"; | ||
import axios from "axios"; | ||
import { notFound } from "next/navigation"; | ||
import BlurFade from "@/app/components/ui/blur-fade"; | ||
|
||
const raleway = Raleway({ subsets: ["latin"] }); | ||
const jetbrains_mono = JetBrains_Mono({ subsets: ["latin"] }); | ||
|
||
export const dynamic = "force-dynamic"; // Ensure page is always server-rendered | ||
|
||
export default async function Home({ params }) { | ||
const { slug } = params; | ||
|
||
let certificate; | ||
|
||
try { | ||
// Fetch data from the server-side API | ||
const response = await axios.get( | ||
`http://localhost:5000/certificates/${slug}` | ||
); | ||
|
||
if (!response.data) { | ||
notFound(); // Trigger a 404 if no certificate is found | ||
} | ||
|
||
certificate = response.data; // If certificate exists, assign it | ||
} catch (error) { | ||
console.error("Error fetching certificate data:", error.message); | ||
notFound(); // Trigger 404 if there is an error | ||
} | ||
|
||
const imageURL = `http://127.0.0.1:5000${certificate.image}`; | ||
const name = certificate.name; | ||
|
||
return ( | ||
<div className="z-20 flex flex-col min-h-screen w-full max-w-7xl mx-auto py-10"> | ||
<BlurFade delay={0.25} inView> | ||
<div className="mx-6 md:mx-12 p-6 pt-4"> | ||
<p | ||
className={`font-mono ${jetbrains_mono.className} text-sm py-2 bg-gradient-to-r from-[#9046D4] to-[#FFA7FB] bg-clip-text text-transparent max-w-[120px]`} | ||
> | ||
ACM MPSTME | ||
</p> | ||
<p | ||
className={`${raleway.className} text-white flex items-center gap-3 text-lg md:text-2xl`} | ||
> | ||
PFE - Participation Certificate{" "} | ||
<span | ||
className={`${jetbrains_mono.className} bg-[#9046D4] p-1 px-3 text-xs flex font-bold justify-center items-center rounded-full`} | ||
> | ||
{certificate.subject} | ||
</span> | ||
</p> | ||
</div> | ||
</BlurFade> | ||
<main className="flex-col items-center md:items-stretch md:flex-row flex m-8 justify-center gap-8"> | ||
<BlurFade delay={0.25 * 2} inView> | ||
<Image | ||
alt={`ACM's PFE Certificate for ${name}`} | ||
src={imageURL} | ||
className="border-2 border-[#9046D4]/50 rounded-2xl shadow-[0_5px_50px_-15px] shadow-[#9046D4]/50 opacity-100" | ||
width="600" | ||
height="500" | ||
priority={true} | ||
/> | ||
</BlurFade> | ||
<BlurFade delay={0.25 * 3} inView> | ||
<About certificate={certificate} /> | ||
</BlurFade> | ||
</main> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
"use client" | ||
import { useState } from "react"; | ||
import { Raleway, JetBrains_Mono } from "next/font/google"; | ||
|
||
const raleway = Raleway({ subsets: ['latin']}); | ||
const jbm = JetBrains_Mono({subsets: ['latin']}); | ||
|
||
const About = ({ certificate }) => { | ||
const [copySuccess, setCopySuccess] = useState(false); | ||
|
||
const handleCopy = () => { | ||
const link = window.location.href; | ||
|
||
// Copy the link to the clipboard | ||
navigator.clipboard.writeText(link) | ||
.then(() => { | ||
setCopySuccess(true); // Show success message | ||
setTimeout(() => setCopySuccess(false), 2000); // Clear the message after 2 seconds | ||
}) | ||
.catch((err) => { | ||
console.error("Failed to copy: ", err); | ||
}); | ||
}; | ||
|
||
const handleDownload = async () => { | ||
try { | ||
// Fetch the certificate image from the server as a Blob | ||
const response = await fetch(`http://localhost:5000${certificate.image}`); | ||
const blob = await response.blob(); // Convert the response into a Blob | ||
|
||
// Create a URL for the Blob object | ||
const url = window.URL.createObjectURL(blob); | ||
|
||
// Create an invisible anchor element | ||
const link = document.createElement('a'); | ||
link.href = url; | ||
link.download = `ACM_PFE - ${certificate.name}_${certificate.id}.png`; // File name for the download | ||
|
||
// Append the link to the DOM, trigger click, and remove it from the DOM | ||
document.body.appendChild(link); | ||
link.click(); | ||
document.body.removeChild(link); | ||
|
||
// Revoke the object URL to free memory | ||
window.URL.revokeObjectURL(url); | ||
} catch (error) { | ||
console.error('Error downloading the file:', error); | ||
} | ||
}; | ||
|
||
return ( | ||
<div className={`${raleway.className} flex flex-col justify-between max-w-[400px] border min-h-full border-white/15 bg-white/5 p-6 rounded-2xl`}> | ||
<div> | ||
<div className="flex flex-col gap-2 pb-5 border-b border-white/50 mb-5"> | ||
<p className={`text-2xl text-white`}>About this <span className="[text-shadow:_0_2px_10px_rgb(144,70,212)] bg-gradient-to-r from-[#9046D4] to-[#FFA7FB] bg-clip-text text-transparent max-w-[120px]">Certificate</span></p> | ||
<p className={`font-mono ${jbm.className} text-xs bg-gradient-to-r from-[#9046D4] to-[#FFA7FB] bg-clip-text text-transparent max-w-[300px]`}>ID: {certificate.id} <span className="text-white">- {certificate.name}</span></p> | ||
</div> | ||
<div className="flex flex-col gap-4"> | ||
<div className="flex flex-col gap-2"> | ||
<h3 className="text-sm md:text-md font-bold text-white">PFE - Participation Certificate 2024</h3> | ||
<p className="text-xs text-[#888888] line-clamp-6"> | ||
This certificate has been awarded to <span className="text-white underline">{certificate.name}</span> for their 3 day continous participation in PFE (Programming For Everyone) where the students learnt the fudamentals of programming languages of their choice. In the workshop, the students learnt and were able to build a CLI RPG. | ||
</p> | ||
</div> | ||
<div className="flex flex-col gap-4 min-w-max mb-10 md:mb-0"> | ||
<h3 className="text-sm md:text-md text-white font-bold">Share this certificate</h3> | ||
<div className="flex flex-col md:flex-row gap-2"> | ||
<p className="p-2 border px-4 text-ellipsis overflow-hidden rounded-md text-xs text-white border-[#9046D4]/25"> | ||
{window.location.href} | ||
</p> | ||
<button onClick={() => handleCopy()} className=" text-xs py-2 md:py-0 bg-[#101010] border border-[#9046D4]/50 px-4 rounded-md text-white">{ copySuccess ? 'Copied!' : 'Copy'}</button> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
<button onClick={() => handleDownload()} className="transition-all duration-300 cursor-pointer text-center hover:shadow-[0_5px_20px_-10px] shadow-[0_10px_30px_-10px] hover:shadow-[#FFA7FB]/70 shadow-[#FFA7FB]/70 bg-gradient-to-b from-[#be6ee4] to-[#FFA7FB] font-bold border border-[#9046D4] text-black p-2 rounded-md">Download Certificate</button> | ||
{/* <button className="bg-[#101010] border border-[#9046D4]/50 font-bold text-white p-2 rounded-md"> | ||
<span className="bg-gradient-to-r from-[#9046D4] to-[#FFA7FB] bg-clip-text text-transparent max-w-[1200px]"> | ||
Download Certificate | ||
</span> | ||
</button> */} | ||
</div> | ||
) | ||
} | ||
|
||
export default About; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import Image from "next/image"; | ||
|
||
const Navbar = () => { | ||
return ( | ||
<div className="flex backdrop-blur-md justify-between p-3 px-10 m-3 border border-white/25 bg-white/5 rounded-lg"> | ||
<Image alt="ACM MPSTME" src="/acm_pfe.svg" width="60" height="60"/> | ||
<Image alt="TRC MPSTME" src="/trc.svg" width="60" height="60" /> | ||
</div> | ||
) | ||
} | ||
|
||
export default Navbar; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
"use client";; | ||
import { useRef } from "react"; | ||
import { AnimatePresence, motion, useInView } from "framer-motion"; | ||
|
||
export default function BlurFade({ | ||
children, | ||
className, | ||
variant, | ||
duration = 0.4, | ||
delay = 0, | ||
yOffset = 6, | ||
inView = false, | ||
inViewMargin = "-50px", | ||
blur = "6px" | ||
}) { | ||
const ref = useRef(null); | ||
const inViewResult = useInView(ref, { once: true, margin: inViewMargin }); | ||
const isInView = !inView || inViewResult; | ||
const defaultVariants = { | ||
hidden: { y: yOffset, opacity: 0, filter: `blur(${blur})` }, | ||
visible: { y: -yOffset, opacity: 1, filter: `blur(0px)` }, | ||
}; | ||
const combinedVariants = variant || defaultVariants; | ||
return ( | ||
(<AnimatePresence> | ||
<motion.div | ||
ref={ref} | ||
initial="hidden" | ||
animate={isInView ? "visible" : "hidden"} | ||
exit="hidden" | ||
variants={combinedVariants} | ||
transition={{ | ||
delay: 0.04 + delay, | ||
duration, | ||
ease: "easeOut", | ||
}} | ||
className={className}> | ||
{children} | ||
</motion.div> | ||
</AnimatePresence>) | ||
); | ||
} |
Oops, something went wrong.