refactor: move ProjectCard component and add prop types validation

This commit is contained in:
Alvalens 2025-03-18 12:37:11 +07:00
parent f1edb19b5a
commit 3aa7aedeca
3 changed files with 88 additions and 22 deletions

View file

@ -1,10 +1,10 @@
import Link from "next/link"; import Link from "next/link";
import { motion } from "framer-motion";
import Image from "next/image"; import Image from "next/image";
export default function projectCard({project, index, activeCategory}){ import { motion } from "framer-motion";
console.log(project.category[0], activeCategory); import PropTypes from "prop-types";
console.log(project.category.includes(activeCategory));
return ( export default function ProjectCard({ project, index, activeCategory }) {
return (
<> <>
{project.category.includes(parseInt(activeCategory)) && ( {project.category.includes(parseInt(activeCategory)) && (
<Link href={"projects/" + project.slug} key={index}> <Link href={"projects/" + project.slug} key={index}>
@ -56,3 +56,10 @@ export default function projectCard({project, index, activeCategory}){
</> </>
); );
} }
ProjectCard.propTypes = {
project: PropTypes.object.isRequired,
index: PropTypes.number.isRequired,
activeCategory: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
.isRequired,
};

View file

@ -0,0 +1,65 @@
import Link from "next/link";
import Image from "next/image";
import { motion } from "framer-motion";
import PropTypes from "prop-types";
export default function ProjectCard({ project, index, activeCategory }) {
return (
<>
{project.category.includes(parseInt(activeCategory)) && (
<Link href={"projects/" + project.slug} key={index}>
<motion.div
className="z-10 relative flex justify-center items-start flex-col mb-5 md:px-10 w-full h-auto bg-gray-400 group/tes py-20 px-5 md:py-2 aspect-video "
initial={{
opacity: 0,
x: -200,
}}
whileInView={{
opacity: 1,
x: 0,
}}
transition={{
type: "spring",
}}>
<Image
src={project.thumbnail}
alt="Alvalens"
layout="fill"
objectFit="cover"
placeholder="blur"
className="bg-slate-950 opacity-10 group-hover/tes:opacity-100 transition-all ease duration-500"
blurDataURL={project.thumbnail}
/>
<div className="absolute top-0 left-0 bg-gray-600 px-4 py-2">
<h4 className="text-white">{project.year}</h4>
</div>
<div className="transition-all ease duration-500 opacity-100 content text-center group-hover/tes:opacity-0 z-10">
<h1 className="text-3xl font-bold mb-3">{project.title}</h1>
<p>
{project.desc[0].length > 125
? `${project.desc[0].slice(0, 125)}...`
: project.desc[0]}
</p>
<div className="flex justify-center items-center flex-row mt-5 flex-wrap">
{project.tech.map((t, index) => (
<span
key={index}
className="m-1 px-4 py-2 bg-gray-600 text-white ">
{t}
</span>
))}
</div>
</div>
</motion.div>
</Link>
)}
</>
);
}
ProjectCard.propTypes = {
project: PropTypes.object.isRequired,
index: PropTypes.number.isRequired,
activeCategory: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
.isRequired,
};

View file

@ -12,7 +12,7 @@ import ReactChat3 from "@/public/image/projects/web/trivia/trivia3.png";
import ProjectAll from "@/public/image/projects.png"; import ProjectAll from "@/public/image/projects.png";
import Hr from "@/components/Hr"; import Hr from "@/components/Hr";
import ProjectCard from "./(project-card)"; import ProjectCard from "./components/ProjectCard";
import Projects from "@/json/data.json"; import Projects from "@/json/data.json";
import FixedButon from "@/components/FixedButton"; import FixedButon from "@/components/FixedButton";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
@ -22,12 +22,11 @@ const category = {
1: "Web Development", 1: "Web Development",
2: "AI & Machine Learning", 2: "AI & Machine Learning",
9: "Other", 9: "Other",
} };
export default function Page() { export default function Page() {
const [activeCategory, setActiveCategory] = useState(1); const [activeCategory, setActiveCategory] = useState(1);
const projects = Projects.Projects.filter ((item) => item.show === true const projects = Projects.Projects.filter((item) => item.show === true);
);
useEffect(() => { useEffect(() => {
window.scrollTo(0, 0); window.scrollTo(0, 0);
@ -36,10 +35,7 @@ export default function Page() {
<> <>
<main className="overflow-hidden"> <main className="overflow-hidden">
<FixedButon href="/#projects"> <FixedButon href="/#projects">
<FontAwesomeIcon <FontAwesomeIcon icon={faChevronLeft} className="text-black pr-10" />
icon={faChevronLeft}
className="text-black pr-10"
/>
</FixedButon> </FixedButon>
<div className="relative h-screen w-screen gap-4 p-10 flex justify-center items-center flex-col mb-10 overflow-hidden"> <div className="relative h-screen w-screen gap-4 p-10 flex justify-center items-center flex-col mb-10 overflow-hidden">
<div className="z-0 mb-48 md:mb-0 md:absolute top-1/4 md:right-[10%] md:-translate-y-16 "> <div className="z-0 mb-48 md:mb-0 md:absolute top-1/4 md:right-[10%] md:-translate-y-16 ">
@ -181,15 +177,13 @@ export default function Page() {
Generative Trivia Generative Trivia
</h2> </h2>
<p className="text-gray-600 text-justify title text-lg"> <p className="text-gray-600 text-justify title text-lg">
A generative trivia website built with Next.js, A generative trivia website built with Next.js, Firebase,
Firebase, NextAuth, Google OAuth, and TypeScript. NextAuth, Google OAuth, and TypeScript. Users can generate trivia
Users can generate trivia questions for predefined questions for predefined school subjects and answer them with a
school subjects and answer them with a unique unique interactive input style. The application supports user
interactive input style. The application supports authentication with Google and stores data in Firestore. You can
user authentication with Google and stores data in see the source code of this project on my github account or
Firestore. You can see the source code of this preview the project by clicking the button below.
project on my github account or preview the project
by clicking the button below.
</p>{" "} </p>{" "}
<div className="mt-3"> <div className="mt-3">
<Button variation="primary"> <Button variation="primary">