Newer
Older
My-Portfolio / frontend / src / components / sections / projects / Project.jsx
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;