diff --git a/package-lock.json b/package-lock.json index a9e2926..7f06eab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "react-firebase-hooks": "^5.1.1", "react-hot-toast": "^2.4.1", "react-icons": "^5.3.0", + "react-use-measure": "^2.1.7", "recharts": "^2.15.1", "tailwind-merge": "^2.5.2", "tailwindcss-animate": "^1.0.7" @@ -5859,6 +5860,21 @@ "react-dom": ">=16.6.0" } }, + "node_modules/react-use-measure": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.7.tgz", + "integrity": "sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.13", + "react-dom": ">=16.13" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", diff --git a/package.json b/package.json index 0b84e8d..acd1aad 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "react-firebase-hooks": "^5.1.1", "react-hot-toast": "^2.4.1", "react-icons": "^5.3.0", + "react-use-measure": "^2.1.7", "recharts": "^2.15.1", "tailwind-merge": "^2.5.2", "tailwindcss-animate": "^1.0.7" diff --git a/public/hero/h1.png b/public/hero/h1.png deleted file mode 100644 index 84bd694..0000000 Binary files a/public/hero/h1.png and /dev/null differ diff --git a/public/hero/h2.png b/public/hero/h2.png deleted file mode 100644 index 4b77001..0000000 Binary files a/public/hero/h2.png and /dev/null differ diff --git a/public/hero/h3.png b/public/hero/h3.png deleted file mode 100644 index b2c0010..0000000 Binary files a/public/hero/h3.png and /dev/null differ diff --git a/public/hero/h4.png b/public/hero/h4.png deleted file mode 100644 index 1e7dbc3..0000000 Binary files a/public/hero/h4.png and /dev/null differ diff --git a/public/hero/h5.png b/public/hero/h5.png deleted file mode 100644 index 7ea7853..0000000 Binary files a/public/hero/h5.png and /dev/null differ diff --git a/public/hero/hero-background.png b/public/hero/hero-background.png new file mode 100644 index 0000000..5124c1a Binary files /dev/null and b/public/hero/hero-background.png differ diff --git a/public/hero/hero1.png b/public/hero/hero1.png new file mode 100644 index 0000000..c0c0907 Binary files /dev/null and b/public/hero/hero1.png differ diff --git a/public/hero/hero2.png b/public/hero/hero2.png new file mode 100644 index 0000000..014aa43 Binary files /dev/null and b/public/hero/hero2.png differ diff --git a/public/hero/hero3.png b/public/hero/hero3.png new file mode 100644 index 0000000..854bf14 Binary files /dev/null and b/public/hero/hero3.png differ diff --git a/public/hero/hero4.png b/public/hero/hero4.png new file mode 100644 index 0000000..f42f1ab Binary files /dev/null and b/public/hero/hero4.png differ diff --git a/public/hero/hero5.png b/public/hero/hero5.png new file mode 100644 index 0000000..2a1aa41 Binary files /dev/null and b/public/hero/hero5.png differ diff --git a/public/hero/hero6.png b/public/hero/hero6.png new file mode 100644 index 0000000..fdf5e02 Binary files /dev/null and b/public/hero/hero6.png differ diff --git a/public/hero/hero7.png b/public/hero/hero7.png new file mode 100644 index 0000000..08037c1 Binary files /dev/null and b/public/hero/hero7.png differ diff --git a/public/hero/hero8.png b/public/hero/hero8.png new file mode 100644 index 0000000..19423fd Binary files /dev/null and b/public/hero/hero8.png differ diff --git a/public/hero/hero9.png b/public/hero/hero9.png new file mode 100644 index 0000000..7f513f9 Binary files /dev/null and b/public/hero/hero9.png differ diff --git a/public/hero/lights.png b/public/hero/lights.png deleted file mode 100644 index 6f47c36..0000000 Binary files a/public/hero/lights.png and /dev/null differ diff --git a/public/hero/mobo.svg b/public/hero/mobo.svg deleted file mode 100644 index 50d5c4f..0000000 --- a/public/hero/mobo.svg +++ /dev/null @@ -1,265 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/images/login-bg.svg b/public/images/login-bg.svg deleted file mode 100644 index 2801d1d..0000000 --- a/public/images/login-bg.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/public/landing/background.svg b/public/landing/background.svg deleted file mode 100644 index bb3bd14..0000000 --- a/public/landing/background.svg +++ /dev/null @@ -1,48 +0,0 @@ - - {/* Base black background */} - - - {/* Gradient overlay for radial left */} - - - {/* Gradient overlay for radial left */} - - - {/* Gradient overlay for bottomright */} - - - {/* Gradient overlay for radial left */} - - - {/* White polygon */} - {isDesktop && ( - - )} - - - - {/* Radial cold gradient */} - - - - - - {/* Radial warm gradient */} - - - - - - {/* bottom right gradient */} - - - - - - {/* bottom left Radial warm gradient */} - - - - - - \ No newline at end of file diff --git a/public/landing/logos/meta.png b/public/landing/logos/meta.png index 72ffa9f..4a3795a 100644 Binary files a/public/landing/logos/meta.png and b/public/landing/logos/meta.png differ diff --git a/public/landing/logos/microsoft.png b/public/landing/logos/microsoft.png index aa2d5d2..3657f7f 100644 Binary files a/public/landing/logos/microsoft.png and b/public/landing/logos/microsoft.png differ diff --git a/public/landing/logos/nvidia.png b/public/landing/logos/nvidia.png index 53b8abc..7192745 100644 Binary files a/public/landing/logos/nvidia.png and b/public/landing/logos/nvidia.png differ diff --git a/public/landing/logos/roblox.png b/public/landing/logos/roblox.png index d30e6a5..dbbbbc9 100644 Binary files a/public/landing/logos/roblox.png and b/public/landing/logos/roblox.png differ diff --git a/public/landing/logos/samsung.png b/public/landing/logos/samsung.png index 806accf..2c67408 100644 Binary files a/public/landing/logos/samsung.png and b/public/landing/logos/samsung.png differ diff --git a/public/landing/logos/ukg.png b/public/landing/logos/ukg.png index b3bf920..f380097 100644 Binary files a/public/landing/logos/ukg.png and b/public/landing/logos/ukg.png differ diff --git a/src/app/globals.css b/src/app/globals.css index c29a5b6..1a7ac70 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -28,20 +28,6 @@ body { background-repeat: no-repeat; } -.noise-bg { - position: fixed; - inset: 0; - z-index: 1; - pointer-events: none; - opacity: 0.02; - background: - repeating-radial-gradient(#fff 0 0.0001%, #000 0 0.0002%) 50% 0/2500px - 2500px, - repeating-conic-gradient(#fff 0 0.0001%, #000 0 0.0002%) 50% 50%/2500px - 2500px; - background-blend-mode: difference; -} - .monthly-event { background: radial-gradient( 138.71% 110.11% at 93.15% -36.58%, @@ -80,7 +66,7 @@ body { .hero-btn:hover { /* Apply a full (all-around) shadow with #79C7FD */ - box-shadow: 0 0 20px 5px rgba(121, 199, 253, 0.7); + box-shadow: 0 0 16px 4px rgba(121, 199, 253, 0.7); } .hero-fade { @@ -91,26 +77,26 @@ body { background: linear-gradient(0deg, #080d1400 0%, #080d14 100%); } -/* Custom minimalist scrollbar */ -.custom-scrollbar::-webkit-scrollbar { - width: 4px; -} -.custom-scrollbar::-webkit-scrollbar-track { - background: transparent; -} +/* infinite carousel */ -.custom-scrollbar::-webkit-scrollbar-thumb { - background: rgba(59, 130, 246, 0.2); - border-radius: 4px; +.scroller-list { + list-style: none; + flex-shrink: 0; + min-width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + gap: 16px; + animation: scroll 20s linear infinite; } -.custom-scrollbar::-webkit-scrollbar-thumb:hover { - background: rgba(59, 130, 246, 0.5); +.scroller-container:hover .scroller-list{ + animation-play-state: paused; } -/* Firefox */ -.custom-scrollbar { - scrollbar-width: thin; - scrollbar-color: rgba(59, 130, 246, 0.2) transparent; -} +@keyframes scroll { + to { + transform: translateX(calc(-100% - 16px)); + } +} \ No newline at end of file diff --git a/src/app/icon.ico b/src/app/icon.ico deleted file mode 100644 index 7610987..0000000 Binary files a/src/app/icon.ico and /dev/null differ diff --git a/src/app/icon.png b/src/app/icon.png new file mode 100644 index 0000000..9827d40 Binary files /dev/null and b/src/app/icon.png differ diff --git a/src/components/landing/Hero.tsx b/src/components/landing/Hero.tsx index 175c4ef..e03af12 100644 --- a/src/components/landing/Hero.tsx +++ b/src/components/landing/Hero.tsx @@ -5,33 +5,57 @@ import AnimatedText from "./ui/TextFade"; import { FaDiscord } from "react-icons/fa"; import Image from "next/image"; import useDiscordCount from "@/hooks/useDiscordCount"; -import LogoCarousel from "./LogoCarousel"; +import { Scroller } from "./ui/Scroller"; + + +const imgs: string[] = [ + "/hero/hero1.png", + "/hero/hero2.png", + "/hero/hero3.png", + "/hero/hero4.png", + "/hero/hero5.png", + "/hero/hero6.png", + "/hero/hero7.png", + "/hero/hero8.png", + "/hero/hero9.png", +]; + +const logos: string[] = [ + "/landing/logos/roblox.png", + "/landing/logos/nvidia.png", + "/landing/logos/microsoft.png", + "/landing/logos/samsung.png", + "/landing/logos/ukg.png", + "/landing/logos/meta.png" +]; + + export default function Hero() { const [contentVisible, setContentVisible] = useState(false); + + const discordCount = useDiscordCount(); return ( -
+
+ {/* bg image */} Lights Background - Mobo Background -
-
-
-
+ +
+
+
{contentVisible && (
-

+

{/* Buttons */} - - {contentVisible && ( - - + + {contentVisible && ( + - Get Involved - - + Get Involved + + + Join {discordCount} on Discord + + + + )} + +

+
+ + {/* Image carousel */} +
+
+ + {contentVisible && ( + - Join {discordCount} Discord Members{" "} - - - - )} - + + + )} + +
+
- {/* Logos Section */} + {/* Officer Experience Logos*/} +
{contentVisible && ( -

+

Led by a team of officers with experience at:

-
- - - +
+ + + - + -

+ More!

+

+ More!

+
+
+
- )}
- - {/* Image Section */}
); } - -type HeroImagesProps = { - contentVisible: boolean; -}; - -const HeroImages: React.FC = ({ contentVisible }) => { - return ( - <> - - {contentVisible && ( - -
- - -
-
- -
-
- - -
-
- )} -
-
- - {contentVisible && ( - - - - - - - - )} - -
- - ); -}; diff --git a/src/components/landing/LogoCarousel.tsx b/src/components/landing/LogoCarousel.tsx deleted file mode 100644 index 2c724c8..0000000 --- a/src/components/landing/LogoCarousel.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; -import { motion } from 'framer-motion'; - -interface Logo { - src: string; - alt: string; -} - -const LogoCarousel: React.FC = () => { - const logos: Logo[] = [ - { src: "/landing/logos/roblox.png", alt: "Roblox" }, - { src: "/landing/logos/nvidia.png", alt: "NVIDIA" }, - { src: "/landing/logos/microsoft.png", alt: "Microsoft" }, - { src: "/landing/logos/samsung.png", alt: "Samsung" }, - { src: "/landing/logos/ukg.png", alt: "UKG" }, - { src: "/landing/logos/meta.png", alt: "Meta" } - ]; - - // Duplicate the logos array to create a seamless infinite effect - const duplicatedLogos = [...logos, ...logos]; - - return ( -
- - {duplicatedLogos.map((logo, index) => ( -
- {logo.alt} -
- ))} - + More! -
-
- ); -}; - -export default LogoCarousel; diff --git a/src/components/landing/ui/Scroller.tsx b/src/components/landing/ui/Scroller.tsx new file mode 100644 index 0000000..e082fd9 --- /dev/null +++ b/src/components/landing/ui/Scroller.tsx @@ -0,0 +1,39 @@ +import React from "react"; +import Image from "next/image"; + +type Picture = { + src: string; +}; + +type ScrollerProps = { + items: string[]; + maxHeight: number +}; + +export const Scroller: React.FC = ({ items, maxHeight }) => { + return ( +
+
    + {items.map((picture, index) => ( + + ))} +
+
    + {items.map((picture, index) => ( + + ))} +
+
+ ); +}; + +const Item: React.FC = ({ src }) => { + return ( + + ); +}; diff --git a/src/components/projects/ProjectsPage.tsx b/src/components/projects/ProjectsPage.tsx index 8aebddd..037b4dd 100644 --- a/src/components/projects/ProjectsPage.tsx +++ b/src/components/projects/ProjectsPage.tsx @@ -1,35 +1,46 @@ +"use client"; + + 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"; +import dynamic from "next/dynamic"; +import { ArrowUpRight } from "lucide-react"; + + + 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 + return b.semester.year - a.semester.year; } - // If years are equal, compare terms + const termComparison = termOrder[b.semester.term] - termOrder[a.semester.term]; if (termComparison !== 0) { return termComparison; } - // If terms are equal, compare dates + return b.buildDate.toMillis() - a.buildDate.toMillis(); }); }; + export default function ProjectsPage() { const [projects, setProjects] = useState([]); const [loading, setLoading] = useState(true); const [selectedProject, setSelectedProject] = useState(null); + const [imageLoaded, setImageLoaded] = useState>({}); + useEffect(() => { const fetchProjects = async () => { @@ -47,32 +58,29 @@ export default function ProjectsPage() { } }; + fetchProjects(); }, []); + useEffect(() => { - // When selectedProject changes (modal opens/closes) if (selectedProject) { - // Prevent scrolling on the main page when modal is open document.body.style.overflow = "hidden"; } else { - // Re-enable scrolling when modal is closed document.body.style.overflow = ""; } - // Cleanup function to ensure scrolling is re-enabled if component unmounts + return () => { document.body.style.overflow = ""; }; }, [selectedProject]); + return (
- {/* Applied noise background for consistency */} - - {/* Hero Section with Title */} + {/* Hero */}
- {/* Decorative Lines */} - {/* Title Content */} +
-

- Our Projects +

+ Latest Projects

Discover our latest custom PC builds @@ -120,11 +128,32 @@ export default function ProjectsPage() {

- {/* Projects Grid */} -
+ + {/* Projects */} +
{loading ? ( -
-
+
+ {Array.from({ length: 5 }).map((_, index) => ( +
+ {/* Image Skeleton */} +
+ + + {/* Title Skeleton */} +
+ + + {/* Date Skeleton */} +
+ + + {/* Description Skeleton */} +
+
+
+
+
+ ))}
) : (
@@ -132,25 +161,40 @@ export default function ProjectsPage() {
setSelectedProject(project)} - className="group relative cursor-pointer overflow-hidden rounded-lg bg-gray-950 p-4 shadow-white-glow transition-transform hover:scale-105" + className="group relative cursor-pointer overflow-hidden rounded-sm p-4" > {/* Project Image */} -
+
+ {!imageLoaded[project.id] && ( +
+ )} {project.Title} + setImageLoaded((prev) => ({ + ...prev, + [project.id]: true, + })) + } + className={`h-full w-full object-cover transition-transform group-hover:scale-105 ${ + !imageLoaded[project.id] ? "opacity-0" : "opacity-100" + } transition-opacity`} />
+ {/* Project Info */}
-

- {project.Title} +

+ {project.Title}

-

+

{project.semester.term + " " + project.semester.year}

+ + {project.Description} +
))} @@ -158,7 +202,8 @@ export default function ProjectsPage() { )}
- {/* Project Modal */} + + {/* Modal */} {selectedProject && ( ); } + + +type Props = { + children: string; + lines?: number; + className?: string; +}; + + +const TruncatedText: React.FC = ({ + children, + lines = 2, + className = "", +}) => { + const clampClass = + { + 1: "line-clamp-1", + 2: "line-clamp-2", + 3: "line-clamp-3", + 4: "line-clamp-4", + 5: "line-clamp-5", + 6: "line-clamp-6", + }[lines] || "line-clamp-2"; + + + return ( +

+ {children} +

+ ); +}; + + + + +