+ {/* Add Semester Selection */}
+
+
+
+
+
+
+
+
+
+
+
+
)}
@@ -329,7 +394,7 @@ export default function AddForm({
@@ -339,7 +404,7 @@ export default function AddForm({
{error &&
{error}
}
-
{success && (
{isEditing
diff --git a/src/components/admin/projects/EditForm.tsx b/src/components/admin/projects/EditForm.tsx
index 44b1485..a3c30b5 100644
--- a/src/components/admin/projects/EditForm.tsx
+++ b/src/components/admin/projects/EditForm.tsx
@@ -64,7 +64,6 @@ export default function EditForm() {
try {
const projectRef = doc(db, "Projects", selectedProject.id);
- // Convert ProjectFormData to a plain object that Firestore can handle
const firestoreData = {
Youtube: updatedData.Youtube,
Description: updatedData.Description,
@@ -72,6 +71,10 @@ export default function EditForm() {
Photos: updatedData.Photos,
Image: updatedData.Image,
Builders: updatedData.Builders,
+ semester: {
+ term: updatedData.semester.term,
+ year: updatedData.semester.year,
+ },
Parts: {
RAM: updatedData.Parts.RAM,
Cooling: updatedData.Parts.Cooling,
@@ -88,16 +91,13 @@ export default function EditForm() {
// Update local state
setProjects((prevProjects) =>
- prevProjects.map((project) =>
- project.id === selectedProject.id
- ? { ...project, ...updatedData }
- : project,
+ prevProjects.map((proj) =>
+ proj.id === selectedProject.id ? { ...proj, ...updatedData } : proj,
),
);
setIsModalOpen(false);
setSelectedProject(null);
- //alert('Project updated successfully');
} catch (err) {
console.error("Error updating project:", err);
setError("Failed to update project");
@@ -163,26 +163,30 @@ export default function EditForm() {
{/* Projects List */}
-
- {projects
- .filter((project) =>
- project.Title.toLowerCase().includes(searchQuery.toLowerCase()),
- )
- .map((project) => (
+ {projects
+ .filter(
+ (
+ proj, // Changed from project to proj
+ ) => proj.Title.toLowerCase().includes(searchQuery.toLowerCase()),
+ )
+ .map(
+ (
+ proj, // Changed from project to proj
+ ) => (
-
{project.Title}
+
{proj.Title}
- ))}
-
+ ),
+ )}
{/* Edit Modal */}
{isModalOpen && selectedProject && (
diff --git a/src/components/projects/ProjectModal.tsx b/src/components/projects/ProjectModal.tsx
new file mode 100644
index 0000000..e073787
--- /dev/null
+++ b/src/components/projects/ProjectModal.tsx
@@ -0,0 +1,128 @@
+import { useState } from "react";
+import { Project } from "@/types/project";
+import { X } from "lucide-react";
+
+interface ProjectModalProps {
+ project: Project;
+ onClose: () => void;
+}
+
+export default function ProjectModal({ project, onClose }: ProjectModalProps) {
+ const [activeTab, setActiveTab] = useState<
+ "description" | "specs" | "builders"
+ >("description");
+
+ return (
+
+
+ {/* Noise background */}
+
+
+ {/* Close button */}
+
+
+
+ {/* Left side - Image */}
+
+
+

+ {/* Gradient overlay */}
+
+
+
+
+ {/* Right side - Content */}
+
+
+ {project.Title}
+
+
+ {project.semester.term} {project.semester.year}
+
+
+ {/* Tabs */}
+
+ {(["description", "specs", "builders"] as const).map((tab) => (
+
+ ))}
+
+
+ {/* Tab Content */}
+
+ {activeTab === "description" && (
+
+ {project.Description}
+
+ )}
+
+ {activeTab === "specs" && (
+
+ {Object.entries(project.Parts).map(([part, value]) => (
+
+ {part}
+ {value}
+
+ ))}
+
+ )}
+
+ {activeTab === "builders" && (
+
+ {project.Builders.map((builder, index) => (
+
+ {builder}
+
+ ))}
+
+ )}
+
+
+ {/* Links */}
+
+
+
+
+
+ );
+}
diff --git a/src/components/projects/ProjectsPage.tsx b/src/components/projects/ProjectsPage.tsx
new file mode 100644
index 0000000..0eb8485
--- /dev/null
+++ b/src/components/projects/ProjectsPage.tsx
@@ -0,0 +1,148 @@
+import GlowingLine from "@/components/decorations/GlowingLine";
+import { useEffect, useState } from "react";
+import { collection, getDocs } from "firebase/firestore";
+import { db } from "@/lib/firebase/firebase";
+import { Project } from "@/types/project";
+import ProjectModal from "./ProjectModal";
+
+const sortProjects = (projects: Project[]) => {
+ const termOrder = { Fall: 3, Summer: 2, Spring: 1 };
+
+ return [...projects].sort((a, b) => {
+ // First compare years
+ if (a.semester.year !== b.semester.year) {
+ return b.semester.year - a.semester.year; // Descending order
+ }
+
+ // If years are equal, compare terms
+ return termOrder[b.semester.term] - termOrder[a.semester.term];
+ });
+};
+
+export default function ProjectsPage() {
+ const [projects, setProjects] = useState
([]);
+ const [loading, setLoading] = useState(true);
+ const [selectedProject, setSelectedProject] = useState(null);
+
+ useEffect(() => {
+ const fetchProjects = async () => {
+ try {
+ const querySnapshot = await getDocs(collection(db, "Projects"));
+ const projectsData = querySnapshot.docs.map((doc) => ({
+ id: doc.id,
+ ...doc.data(),
+ })) as Project[];
+ setProjects(projectsData);
+ } catch (error) {
+ console.error("Error fetching projects:", error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchProjects();
+ }, []);
+
+ return (
+
+ {/* Applied noise background for consistency */}
+
+
+ {/* Hero Section with Title */}
+
+ {/* Decorative Lines */}
+
+
+
+
+
+
+ {/* Title Content */}
+
+
+ Our Projects
+
+
+ Discover our latest custom PC builds
+
+
+
+
+ {/* Projects Grid */}
+
+ {loading ? (
+
+ ) : (
+
+ {sortProjects(projects).map((project) => (
+
setSelectedProject(project)}
+ className="shadow-white-glow group relative cursor-pointer overflow-hidden rounded-lg bg-gray-950 p-4 transition-transform hover:scale-105"
+ >
+ {/* Project Image */}
+
+

+
+
+ {/* Project Info */}
+
+
+ {project.Title}
+
+
+ {project.semester.term + " " + project.semester.year}
+
+
+
+ ))}
+
+ )}
+
+
+ {/* Project Modal */}
+ {selectedProject && (
+
setSelectedProject(null)}
+ />
+ )}
+
+ );
+}
diff --git a/src/types/project.ts b/src/types/project.ts
index 9871a46..7dc77c0 100644
--- a/src/types/project.ts
+++ b/src/types/project.ts
@@ -1,25 +1,28 @@
export interface Parts {
- RAM: string;
- Cooling: string;
- Case: string;
- Motherboard: string;
- PSU: string;
- GPU: string;
- Storage: string;
- CPU: string;
- }
-
- export interface ProjectFormData {
- Youtube: string;
- Description: string;
- Parts: Parts;
- Title: string;
- Photos: string;
- Image: string;
- Builders: string[];
- }
-
- export interface Project extends ProjectFormData {
- id: string;
- }
+ RAM: string;
+ Cooling: string;
+ Case: string;
+ Motherboard: string;
+ PSU: string;
+ GPU: string;
+ Storage: string;
+ CPU: string;
+}
+export interface ProjectFormData {
+ Youtube: string;
+ Description: string;
+ Parts: Parts;
+ Title: string;
+ Photos: string;
+ Image: string;
+ Builders: string[];
+ semester: {
+ term: "Spring" | "Summer" | "Fall";
+ year: number;
+ };
+}
+
+export interface Project extends ProjectFormData {
+ id: string;
+}
diff --git a/tailwind.config.ts b/tailwind.config.ts
index 1ba8981..3f5cfbf 100644
--- a/tailwind.config.ts
+++ b/tailwind.config.ts
@@ -37,6 +37,10 @@ const config = {
fontFamily: {
Michroma: ["Michroma", "sans-serif"],
},
+ boxShadow: {
+ "white-glow": "0 0 15px rgba(255,255,255,0.15)",
+ "white-glow-hover": "0 0 30px rgba(255,255,255,0.3)",
+ },
},
},
plugins: [require("tailwindcss-animate")],