Skip to content

Commit

Permalink
feat: Integrated Cert Page
Browse files Browse the repository at this point in the history
  • Loading branch information
yashd-dev committed Oct 14, 2024
1 parent e41c385 commit f2d085a
Show file tree
Hide file tree
Showing 9 changed files with 447 additions and 7 deletions.
4 changes: 4 additions & 0 deletions next.config.mjs
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;
26 changes: 19 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"@radix-ui/react-select": "^2.1.1",
"@sanity/client": "^6.21.3",
"@sanity/image-url": "^1.0.2",
"axios": "^1.7.7",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"cva": "^0.0.0",
Expand Down
76 changes: 76 additions & 0 deletions src/app/certificates/[slug]/page.js
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>
);
}
86 changes: 86 additions & 0 deletions src/app/components/about.jsx
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;
12 changes: 12 additions & 0 deletions src/app/components/navbar.jsx
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;
42 changes: 42 additions & 0 deletions src/app/components/ui/blur-fade.jsx
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>)
);
}
Loading

0 comments on commit f2d085a

Please sign in to comment.