import React, { useRef, useState, useEffect } from "react"; import { motion } from "framer-motion"; import { useParams } from "react-router-dom"; import { fetchData } from "../../../api"; import Container from "./Container"; import HoverableIconList from "./HoverableIcon"; import styles from "./Project.module.css"; //SVG import RightArrow from "../../../assets/buttons/RightArrow.svg"; import LeftArrow from "../../../assets/buttons/LeftArrow.svg"; const Project = () => { const { title } = useParams(); const [projectData, setProjectData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [hoveredTech, setHoveredTech] = useState(null); const [hoveredLanguage, setHoveredLanguage] = useState(null); const [hoveredTools, setHoveredTools] = useState({}); const [toolsByType, setToolsByType] = useState({}); const [currentIndex, setCurrentIndex] = useState(0); const [direction, setDirection] = useState(1); const intervalRef = useRef(null); useEffect(() => { const fetchProjectData = async () => { try { // Fetch all projects and find the one with the matching title const allProjects = await fetchData("project_cards"); const project = allProjects.find((p) => { const projectSlug = p.title .toLowerCase() .replace(/[^a-z0-9 -]/g, "") .replace(/\s+/g, "-") .replace(/-+/g, "-"); return projectSlug === title; }); if (project) { const data = await fetchData(`project_page/${project.id}`); setProjectData(data); const groupedTools = data?.tools.reduce((acc, tool) => { if (!acc[tool.type]) acc[tool.type] = []; acc[tool.type].push(tool); return acc; }, {}); setToolsByType(groupedTools); } else { setError("Project not found"); } } catch (err) { setError(err.message); } finally { setLoading(false); } }; fetchProjectData(); }, [title]); const resetTimer = () => { clearInterval(intervalRef.current); intervalRef.current = setInterval(() => { setDirection(1); setCurrentIndex( (prevIndex) => (prevIndex + 1) % projectData?.repository?.commits?.length ); }, 6000); }; useEffect(() => { if (!projectData) return; resetTimer(); // Start the interval when data loads return () => clearInterval(intervalRef.current); // Cleanup on unmount }, [projectData]); const nextCommit = () => { setDirection(1); setCurrentIndex( (prevIndex) => (prevIndex + 1) % projectData?.repository?.commits.length ); resetTimer(); // Restart the timer }; const prevCommit = () => { setDirection(-1); setCurrentIndex((prevIndex) => prevIndex === 0 ? projectData?.repository?.commits.length - 1 : prevIndex - 1 ); resetTimer(); // Restart the timer }; if (loading) return <div>Loading...</div>; if (error) return <div>Error: {error}</div>; if (!projectData) return <div>No data found</div>; return ( <div className="min-h-screen text-white "> {/* Background Image */} <div className="absolute top-0 left-0 w-full h-full z-0"> {projectData?.background_image ? ( <img className="object-cover w-full h-full" src={projectData?.background_image} alt="Project Background" /> ) : ( <div className="absolute w-full h-full bg-[#181818]"></div> // Fallback to background color if no image )} </div> <div className="relative z-10 w-full max-w-[100rem] mx-auto px-4 justify-center"> {/* Project Logo */} <header className="flex flex-col sm:flex-row items-center gap-6 mb-10"> <img src={projectData?.project_logo} alt="Project Logo" className="w-full h-full rounded-md mt-14" /> <h1 className="text-2xl sm:text-4xl font-bold text-center sm:text-left"> {projectData?.title} </h1> </header> <Container className="bg-black/80 mb-10"> <div className="flex flex-col sm:flex-row sm:gap-6"> {/* Thumbnail */} <img src={projectData?.project_thumbnail} alt="Project Thumbnail" className="w-full sm:w-80 lg:w-96 rounded-lg" /> {/* Description */} <div className="text-lg sm:text-2xl flex-1 mt-4 sm:mt-0"> <h2 className="text-2xl sm:text-3xl lg:text-4xl font-semibold mb-2 lg:mb-6 sm:text-left text-orange-400"> Description </h2> {projectData?.project_description} </div> </div> </Container> {/* Trailer */} {projectData?.trailer && ( <Container className="bg-black/80 mb-10 max-w-auto mx-auto w-full"> <h2 className="text-2xl sm:text-3xl lg:text-4xl font-semibold mb-6 text-center"> Trailer </h2> <div className="w-full aspect-video"> <iframe className="w-full h-full rounded-lg" src={projectData.trailer} title={`${projectData.title} Trailer`} allowFullScreen ></iframe> </div> </Container> )} {/* Tech, Languages, and Tools Section */} <div className="flex flex-wrap gap-6 justify-center mb-10"> {/* Tech Section */} <Container className="bg-black/80 flex"> <HoverableIconList title="Tech" items={projectData?.tech} hoveredItem={hoveredTech} setHoveredItem={setHoveredTech} getKey={(item, index) => `tech-${index}`} /> </Container> {/* Programming Languages Section */} <Container className="bg-black/80 flex"> <HoverableIconList title="Languages" items={projectData?.programming_languages} hoveredItem={hoveredLanguage} setHoveredItem={setHoveredLanguage} getKey={(item, index) => `language-${index}`} /> </Container> {/* Tools Section */} {Object.entries(toolsByType).map(([toolType, tools]) => ( <Container key={toolType} className="bg-black/80"> <HoverableIconList title={`${toolType} Tools`} items={tools.flatMap((tool) => tool.tools)} hoveredItem={hoveredTools[toolType]} setHoveredItem={(item) => setHoveredTools((prev) => ({ ...prev, [toolType]: item })) } getKey={(item, index) => `${toolType}-${index}`} /> </Container> ))} </div> <div className="relative z-10 w-full flex flex-col md:flex-row gap-4 md:gap-6 justify-center mb-10 px-4"> {/* Detailed Info Section */} <Container className="bg-black/80 w-full md:max-w-[100px] lg:max-w-[200px] lg:max-h-[250px] md:min-w-[200px] px-4 py-6 flex flex-col items-center justify-center text-center"> <img className="relative mb-4 w-16 md:w-20 object-contain" src={projectData?.date_logo} alt="Date Logo" /> <h2 className="text-base md:text-lg mb-4 text-white"> {projectData?.start_date} - {projectData?.end_date} </h2> </Container> {projectData?.context && ( <Container className="bg-black/80 w-full md:max-w-[200px] md:max-h-[250px] md:min-w-[200px] px-4 py-6 flex flex-col items-center justify-center text-center"> <img className="relative mb-4 w-16 md:w-20 object-contain" src={projectData?.context.context_logo} alt="Context Logo" /> <h2 className="text-base md:text-lg mb-4 text-white"> {projectData?.context.project_context} </h2> </Container> )} </div> {projectData?.repository && projectData?.repository?.url && ( <div className="flex flex-col items-center justify-center min-h-[300px] w-full"> <div className={styles["activity-card"]}> <h2 className="text-3xl lg:text-5xl font-bold mb-6 text-center"> Development Activity </h2> <motion.div key={currentIndex} initial={{ x: direction * 100, opacity: 0 }} animate={{ x: 0, opacity: 1 }} exit={{ x: -direction * 100, opacity: 0 }} transition={{ duration: 0.5 }} className={styles["activity-content"]} > <img src={ projectData?.repository?.commits?.[currentIndex]?.logo || "default-logo-url" } alt="GitBucket Logo" className={styles["activity-logo"]} /> <div> <h3 className={styles["activity-title"]}> {projectData?.repository?.commits?.[currentIndex] ?.message || "No Title"} </h3> <p className={styles["activity-details"]}> Author:{" "} {projectData?.repository?.commits?.[currentIndex]?.author} -{" "} {new Date( projectData?.repository?.commits?.[currentIndex]?.date ).toLocaleString()} </p> </div> </motion.div> <a href={projectData?.repository?.commits?.[currentIndex]?.url} className={styles["activity-link"]} target="_blank" rel="noopener noreferrer" > View Commit </a> </div> {/* Buttons */} <div className={styles["activity-buttons"]}> <button onClick={prevCommit} className={styles["activity-button"]} > <img src={LeftArrow} alt="Previous" /> </button> <button onClick={nextCommit} className={styles["activity-button"]} > <img src={RightArrow} alt="Next" /> </button> </div> </div> )} {projectData?.repository && projectData?.repository?.url && ( <div className="relative justify-center flex mt-20 mb-10"> <a href={projectData?.repository?.url} className={styles["activity-link"]} target="_blank" rel="noopener noreferrer" > <div className={styles["button-container"]}> <img className={styles["button-logo"]} src={projectData?.repository?.logo} alt="Repository Logo" /> </div> </a> </div> )} </div> </div> ); }; export default Project;