diff --git a/public/landing/bg.svg b/public/landing/bg.svg deleted file mode 100644 index 09fff21..0000000 --- a/public/landing/bg.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/landing/discord-app.png b/public/landing/discord-app.png deleted file mode 100644 index 7de6f90..0000000 Binary files a/public/landing/discord-app.png and /dev/null differ diff --git a/public/landing/discord-pc-bg.png b/public/landing/discord-pc-bg.png deleted file mode 100644 index 7b64bb5..0000000 Binary files a/public/landing/discord-pc-bg.png and /dev/null differ diff --git a/public/landing/screen-bg.png b/public/landing/screen-bg.png new file mode 100644 index 0000000..82071d6 Binary files /dev/null and b/public/landing/screen-bg.png differ diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index c7564a9..9334cf7 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -94,7 +94,7 @@ export default function Login() { diff --git a/src/app/globals.css b/src/app/globals.css index 5784955..068318c 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -71,7 +71,7 @@ body { display: flex; justify-content: space-between; align-items: center; - gap: 16px; + gap: 12px; /* Remove animation from here so it only applies when all images are loaded */ } @@ -81,35 +81,7 @@ body { @keyframes scroll { to { - transform: translateX(calc(-100% - 16px)); + transform: translateX(calc(-100% - 12px)); } } -/* Discord PC */ - -.pc-stand { - background: linear-gradient( - 180deg, - rgba(64, 64, 64, 0.1) 0%, - rgba(64, 64, 64, 0.2) 13.27%, - rgba(64, 64, 64, 0.25) 27.21%, - rgba(64, 64, 64, 0.48) 60.58%, - rgba(64, 64, 64, 0.54) 76.36%, - rgba(64, 64, 64, 0.45) 92.14% - ); -} - -.pc-base { - background: linear-gradient( - 90deg, - rgba(64, 64, 64, 0.6) 0%, - rgba(64, 64, 64, 0.3) 6.5%, - rgba(64, 64, 64, 0.6) 19.18%, - rgba(64, 64, 64, 0.3) 32.93%, - rgba(64, 64, 64, 0.3) 66.46%, - rgba(64, 64, 64, 0.6) 81%, - rgba(64, 64, 64, 0.3) 94%, - rgba(64, 64, 64, 0.6) 100% - ); - box-shadow: 0px -7px 2px 0px rgba(0, 0, 0, 0.18); -} \ No newline at end of file diff --git a/src/components/Navbar/Navbar.tsx b/src/components/Navbar/Navbar.tsx index 1ab937f..9b23b8c 100644 --- a/src/components/Navbar/Navbar.tsx +++ b/src/components/Navbar/Navbar.tsx @@ -23,6 +23,8 @@ export default function Nav() { src={"/iconography/spcb-color.png"} height={40} width={40} + unoptimized + quality={100} alt="" className="hidden lg:block" /> diff --git a/src/components/landing/About.tsx b/src/components/landing/About.tsx index b2c9d71..1e1ee9e 100644 --- a/src/components/landing/About.tsx +++ b/src/components/landing/About.tsx @@ -16,14 +16,14 @@ type Card = { const cards: Card[] = [ { bg: "/landing/cards/blue.png", - border: ["353F57", "191A23"], + border: ["233B66", "2E3042"], icon: "/landing/card-icons/social.png", title: "Connect with hundreds of tech enthusiasts.", desc: "Meet like-minded individuals with industry experience in Computer Science & Engineering.", }, { bg: "/landing/cards/orange.png", - border: ["4D402B", "1E1A19"], + border: ["5D4C30", "483B23"], icon: "/landing/card-icons/pc-parts.png", title: "Hands-on experience with the latest tech.", desc: "Build PCs, demo the latest technology, and learn new skills, all for free.", @@ -37,7 +37,7 @@ const cards: Card[] = [ }, { bg: "/landing/cards/purple.png", - border: ["363048", "18171E"], + border: ["3E4265", "292737"], icon: "/landing/card-icons/events.png", title: "Experience engaging and unique events", desc: "Build PCs, demo the latest technology, and learn new skills, all for free.", @@ -116,13 +116,13 @@ const About: React.FC = () => { return (
-

Why SPCB?

-

+

Why SPCB?

+

Discover some of the many reasons to join the Society of PC Building

{/* Slider */} -
+
-
+
+

Stay Connected with SPCB on Discord!

-

+

Discord is how we communicate with each other and discuss everything related to PC building. -
- It’s our main channel for sharing builds, troubleshooting, and - exchanging ideas.

- -
-
- -
-

- Join the Society of
- PC Building Discord! -

- - Join Now - -
+ +
+
+
+ +
+

+ Join the Society of
+ PC Building Discord! +

+ + Join Now +
@@ -45,79 +43,158 @@ export default function DiscordPC() { ); } -const Mac: React.FC = () => { - return ( -
-
- -
-
-
-
-
-
- ); -}; +const messages = [ + { + user: "Cole", + content: "Welcome to the SPCB Discord! πŸ‘‹ Introduce yourself!", + time: "10:01 AM", + color: "#1f2937", + }, + { + user: "Claudio", + content: "Hey everyone, I’m Claudio. Just built my first PC last month πŸ”₯", + time: "10:03 AM", + color: "#111827", + }, + { + user: "Angela", + content: + "Reminder: Our build showcase event is this Friday @ 6PM in CSE-101!", + time: "10:10 AM", + color: "#334155", + }, + { + user: "Wilbert", + content: "Does anyone want to help me build my computer?", + time: "10:15 AM", + color: "#1e293b", + }, +]; const Screen: React.FC = () => { return ( -
-
-
-
- - -
- - +
+
+ +
+
+
-
-
-
- ); -}; - -const Taskbar: React.FC = () => { - function getFormattedDate(): string { - const date = new Date(); - - const year = date.getFullYear(); +
+

The Society of PC Building

+
+ {/* SPCB Main section */} +
+

+ SPCB Main +

+
+ πŸ“’ +

announcements

+
+
+ βœ… +

rules-and-info

+
+
+ πŸ‘‹ +

welcome

+
+
- const month = (date.getMonth() + 1).toString().padStart(2, "0"); - const day = date.getDate().toString().padStart(2, "0"); + {/* General Club Affairs section */} +
+

+ General Club Affairs +

+
+ πŸ’¬ +

general

+ πŸ‘₯ +
+
+ πŸ§‘β€πŸ€β€πŸ§‘ +

introductions

+
+
+ πŸ—£οΈ +

event-suggestions

+
+
+ πŸ“· +

picture-gallery

+
+
+ πŸ“„ +

slides-and-recordings

+
+
+ 🧟 +

swamp-monster

+
+
+
+
+
+
+

#general

+ {/* Sample messages */} +
+ {messages.map((msg, index) => ( +
+
+
+

+ + {msg.user} + {" "} + + {" "} + {msg.time} + +

+

{msg.content}

+
+
+ ))} +
+
- return `${year}-${month}-${day}`; - } - return ( -
-
- File - Edit - View - Window -
-
- {getFormattedDate()} +
+
+

+ + + + Message #general +

+ +
+
+
+
-
- ); -}; - -const ScreenMain: React.FC = () => { - return ( -
- +
); }; diff --git a/src/components/landing/Events.tsx b/src/components/landing/Events.tsx index 03aa6d8..4164c28 100644 --- a/src/components/landing/Events.tsx +++ b/src/components/landing/Events.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useState } from "react"; import { motion } from "framer-motion"; import GlowingLine from "../decorations/GlowingLine"; +import Link from "next/link"; const Events: React.FC = () => { const [isDesktop, setIsDesktop] = useState(false); @@ -19,9 +20,12 @@ const Events: React.FC = () => {
{/* Background Gradients */} -

+

Popular Events

+

+ We hope to see you at one soon! +

{/* Activities Section */}
@@ -33,13 +37,15 @@ const Events: React.FC = () => { thickness={3} circleSize={8} /> - + {isDesktop && ( + + )} { thickness={2} circleSize={6} /> - + {isDesktop && ( + + )} { thickness={2} circleSize={6} /> - + {isDesktop && ( + + )} {/* Content Cards */}
@@ -100,21 +110,23 @@ const Events: React.FC = () => { transition={{ duration: 0.7, ease: "easeOut" }} viewport={{ once: true, amount: 0.2 }} > -
-
- Socials -
-
-

- Socials -

+ +
+
+ Socials +
+
+

+ Socials +

+
-
+ {/* GBMs Card */} diff --git a/src/components/landing/Faq.tsx b/src/components/landing/Faq.tsx index 9639fc0..13f6697 100644 --- a/src/components/landing/Faq.tsx +++ b/src/components/landing/Faq.tsx @@ -1,12 +1,8 @@ -import React, { useState, useEffect } from "react"; -import Monitor from "./ui/Monitor"; +import React, { useState } from "react"; +import { motion, AnimatePresence } from "framer-motion"; +import { FaChevronDown } from "react-icons/fa6"; -interface FAQ { - question: string; - answer: string; -} - -const faqs: FAQ[] = [ +const faqs = [ { question: "Who can become a member?", answer: @@ -15,63 +11,82 @@ const faqs: FAQ[] = [ { question: "What kind of events does SPCB hold?", answer: - "SPCB holds many events such as GBMs, PC builds, socials, gaming events, industry speakers and more. Follow us on Instagram and view the event calendar to stay updated. ", + "SPCB holds many events such as GBMs, PC builds, socials, gaming events, industry speakers and more. Follow us on Instagram and view the event calendar to stay updated.", }, { question: "How can I get involved?", answer: "Join our Discord, attend our events, and apply for officer roles each year. It's a simple way to connect and make a difference.", }, + { + question: "What should I bring to the build events?", + answer: + "We supply all the parts needed to build the pcs. You just need to show up!.", + }, + { + question: "Why should I join?", + answer: + "The Society of PC Building has a variety of hands-on workshops, industry speakers, and socials. SPCB is a great place to expand your network, learn new skills, and have fun!", + }, ]; -export default function Faq(): JSX.Element { - const [isMobile, setIsMobile] = useState(false); - - useEffect(() => { - const checkIfMobile = () => { - setIsMobile(window.innerWidth < 620); - }; - checkIfMobile(); - window.addEventListener("resize", checkIfMobile); - return () => window.removeEventListener("resize", checkIfMobile); - }, []); - +export default function FAQ() { return ( -
-

+
+

Frequently Asked Questions

- -
-
- {faqs.map((faq, i) => ( - - ))} -
- {!isMobile && ( -
-
- -
-
- )} +

+ For any questions you may still have. +

+
+ {faqs.map((faq, index) => ( + + ))}
); } -interface ItemProps { - faq: FAQ; - i: number; -} +type FaqTabProps = { + question: string; + answer: string; +}; + +const FaqTab: React.FC = ({ question, answer }) => { + const [isOpen, setIsOpen] = useState(false); -const Item: React.FC = ({ faq, i }) => { return ( -
-

- 0{i + 1}. {faq.question} -

-

{faq.answer}

+
setIsOpen((prev) => !prev)} + > +
+

{question}

+ + + +
+ + {isOpen && ( + +
+ {answer} +
+
+ )} +
); }; diff --git a/src/components/landing/ui/Connect.tsx b/src/components/landing/ui/Connect.tsx deleted file mode 100644 index 2e34418..0000000 --- a/src/components/landing/ui/Connect.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import React, { useEffect, useState } from "react"; -import { motion, AnimatePresence, Variants } from "framer-motion"; -import { FaLinkedinIn } from "react-icons/fa6"; - -// Define an interface for the notification objects. -interface Notification { - name: string; - time: string; -} - -const notifications: Notification[] = [ - { name: "Claudio Sciotto", time: "5m Ago" }, - { name: "Wilbert Hernandez", time: "10m Ago" }, - { name: "Colin Mendoza", time: "15m Ago" }, - { name: "Angela Ung", time: "20m Ago" }, -]; - -const containerVariants: Variants = { - animate: { - transition: { - staggerChildren: 1.1, - }, - }, -}; - -const messageVariants: Variants = { - initial: { opacity: 0, y: 20 }, - animate: { - opacity: 1, - y: 0, - transition: { - duration: 1.1, - }, - }, - exit: { - opacity: 0, - transition: { - duration: 0.8, - }, - }, -}; - -export default function Terminal(): JSX.Element { - const [key, setKey] = useState(0); - - useEffect(() => { - const timer = setInterval(() => { - setKey((prev) => prev + 1); - }, 7500); - - return () => clearInterval(timer); - }, []); - - return ( -
- - - {notifications.map((noti, i) => ( - - ))} - - -
- ); -} - -interface ItemProps { - noti: Notification; -} - -const Item: React.FC = ({ noti }): JSX.Element => { - return ( - -
-
- -
-
-
-
-

New Notification

-

{noti.time}

-
-

- {noti.name} would like to connect - with you! -

-
-
- ); -}; diff --git a/src/components/landing/ui/Counter.tsx b/src/components/landing/ui/Counter.tsx deleted file mode 100644 index 465d33d..0000000 --- a/src/components/landing/ui/Counter.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import React, { useEffect, useState, useRef } from "react"; -import { animate, useInView } from "framer-motion"; - -const AnimatedCountUp: React.FC = () => { - const [count, setCount] = useState(0); - const containerRef = useRef(null); - - const isInView = useInView(containerRef, { amount: 0.5 }); - - const animationControlRef = useRef | null>(null); - - const runAnimation = () => { - setCount(0); - - if (animationControlRef.current) { - animationControlRef.current.stop(); - } - - animationControlRef.current = animate(0, 10000000, { - duration: 5, - ease: "linear", - onUpdate: (latest) => setCount(Math.floor(latest)), - }); - }; - - useEffect(() => { - let intervalId: number; - - if (isInView) { - runAnimation(); - - intervalId = window.setInterval(() => { - runAnimation(); - }, 10000); - } - - return () => { - if (intervalId) clearInterval(intervalId); - if (animationControlRef.current) animationControlRef.current.stop(); - }; - }, [isInView]); - - return ( -
-
- ${count.toLocaleString()} -
-
- Just kidding... but we are still #1 -
-
- ); -}; - -export default AnimatedCountUp; diff --git a/src/components/landing/ui/Monitor.tsx b/src/components/landing/ui/Monitor.tsx deleted file mode 100644 index 893310a..0000000 --- a/src/components/landing/ui/Monitor.tsx +++ /dev/null @@ -1,231 +0,0 @@ -import React, { useState, useEffect } from "react"; -import motion from "framer-motion"; - -interface ProcessType { - name: string; - cpu: number; - ram: number; -} - -const initialProcesses: ProcessType[] = [ - { name: "spcb.exe", cpu: 2, ram: 2 }, - { name: "events.exe", cpu: 6, ram: 1 }, - { name: "projects.exe", cpu: 4, ram: 4 }, - { name: "about.exe", cpu: 7, ram: 3 }, -]; - -const style = ` - @keyframes fadeValue { - 0% { - color: #79C7FD; - } - 100% { - color: #C1C1C1; - } - } - - .animate-value { - animation: fadeValue 500ms linear forwards; - } -`; - -const Monitor: React.FC = () => { - const [processes, setProcesses] = useState(initialProcesses); - const [animatingItems, setAnimatingItems] = useState>(new Set()); - - useEffect(() => { - const interval = setInterval(() => { - // Use the functional update so we have the latest processes - setProcesses((prevProcesses) => { - const updatedProcesses = prevProcesses.map((process) => ({ - ...process, - cpu: Math.floor(Math.random() * 12) + 1, - ram: Math.floor(Math.random() * 12) + 1, - })); - // Trigger animation for all items using the updated processes - setAnimatingItems(new Set(updatedProcesses.map((p) => p.name))); - // Clear animation classes after the animation completes - setTimeout(() => { - setAnimatingItems(new Set()); - }, 500); - return updatedProcesses; - }); - }, 2500); - - return () => clearInterval(interval); - }, []); - - return ( - <> - -
-
-
-
- - GPU Usage - -
- RTX 5090 -
- -
- -
- - Processes - -
- NAME - CPU - RAM -
- {processes.map((process) => ( - - ))} -
- -
-
- - Temperatures - -
- NAME - TEMP -
-
- cpu1_core - 35C -
-
- gpu_rtx5090 - 50C -
-
- cpu2_core - 38C -
-
- -
- - CPU Usage - -
- - Ryzen 9950X3D - -
- -
-
- -

SPCB CAM 2025

-
-
-
- - ); -}; - -export default Monitor; - -interface ProcessProps { - process: ProcessType; - isAnimating: boolean; -} - -const Process: React.FC = ({ process, isAnimating }) => { - return ( -
- {process.name} - - {process.cpu}% - - - {process.ram}% - -
- ); -}; - -interface AnimatedLineGraphProps { - color: string; -} - -const AnimatedLineGraph: React.FC = ({ color }) => { - const width = 300; - const height = 100; - const numPoints = 30; - // Compute spacing so points are evenly distributed. - const spacing = width / (numPoints - 1); - - // Generate a full set of initial points. - const generateInitialPoints = (): [number, number][] => { - const points: [number, number][] = []; - let lastY = height / 2; - for (let i = 0; i < numPoints; i++) { - // Adjust y value with a random walk. - lastY = lastY + (Math.random() - 0.5) * 20; - lastY = Math.max(10, Math.min(height - 10, lastY)); - points.push([i * spacing, lastY]); - } - return points; - }; - - // Pre-populate the state with the generated points. - const [points, setPoints] = useState<[number, number][]>( - generateInitialPoints, - ); - - useEffect(() => { - const interval = setInterval(() => { - setPoints((prevPoints) => { - // Remove the oldest point. - const newPoints = prevPoints.slice(1); - // Create a new y value using the last point’s y value. - const lastY = newPoints[newPoints.length - 1][1]; - let newY = lastY + (Math.random() - 0.5) * 20; - newY = Math.max(10, Math.min(height - 10, newY)); - // Append the new point at the right edge. - newPoints.push([width, newY]); - - // Recalculate the x coordinates to keep the points evenly spaced. - return newPoints.map((point, index) => [index * spacing, point[1]]); - }); - }, 1000); // Update every 1000ms - - return () => clearInterval(interval); - }, [spacing, width, height]); - - return ( - - p.join(",")).join(" ")} - fill="none" - stroke={color} - strokeWidth="2" - strokeLinejoin="round" - /> - - ); -}; diff --git a/src/components/landing/ui/Scroller.tsx b/src/components/landing/ui/Scroller.tsx index 017de73..f61ef55 100644 --- a/src/components/landing/ui/Scroller.tsx +++ b/src/components/landing/ui/Scroller.tsx @@ -20,7 +20,7 @@ export const Scroller: React.FC = ({ items, maxHeight }) => { const animationClass = loadedCount === totalImages ? "animate-scroll" : ""; return ( -
+
    {items.map((src, index) => ( @@ -47,7 +47,7 @@ const Item: React.FC = ({ src, onLoad }) => { alt="" onLoad={onLoad} style={{ height: "100%", width: "auto" }} - className="scroller-item" + className="scroller-item rounded-md" /> ); }; diff --git a/src/components/landing/ui/evervault-card.tsx b/src/components/landing/ui/evervault-card.tsx deleted file mode 100644 index 721187c..0000000 --- a/src/components/landing/ui/evervault-card.tsx +++ /dev/null @@ -1,152 +0,0 @@ -import React, { useState, useEffect, useRef, MouseEvent } from "react"; -import { - motion, - useMotionValue, - useMotionTemplate, - useInView, - animate, - MotionValue, -} from "framer-motion"; - -// Helper function to join class names. -const cn = (...classes: (string | undefined | null | false)[]): string => - classes.filter(Boolean).join(" "); - -// Generates a random string of the given length. -const characters = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; -const generateRandomString = (length: number): string => { - let result = ""; - for (let i = 0; i < length; i++) { - result += characters.charAt( - Math.floor(Math.random() * characters.length) - ); - } - return result; -}; - -interface EvervaultCardProps { - text?: string; - className?: string; -} - -export const EvervaultCard: React.FC = ({ - text, - className, -}) => { - // Motion values to control the mask’s center. - const mouseX = useMotionValue(0); - const mouseY = useMotionValue(0); - - const [randomString, setRandomString] = useState(""); - - // Ref for the card container. - const containerRef = useRef(null); - // Check if at least 50% of the container is in view. - const isInView = useInView(containerRef, { amount: 0.5 }); - - // Set an initial random string on mount. - useEffect(() => { - setRandomString(generateRandomString(1500)); - }, []); - - // Smoothly animate the mask position on mouse movement. - - - // When the container is in view, update the mask's position smoothly at intervals. - useEffect(() => { - if (isInView && containerRef.current) { - const intervalId = setInterval(() => { - if (containerRef.current) { - const rect = containerRef.current.getBoundingClientRect(); - const randomX = Math.random() * rect.width; - const randomY = Math.random() * rect.height; - animate(mouseX, randomX, { type: "spring", stiffness: 100, damping: 20 }); - animate(mouseY, randomY, { type: "spring", stiffness: 100, damping: 20 }); - // Optionally update the random string as well. - setRandomString(generateRandomString(1500)); - } - }, 1000); // Adjust this interval (in ms) as desired. - return () => clearInterval(intervalId); - } - }, [isInView, mouseX, mouseY]); - - return ( -
    -
    - -
    - -
    -
    -
    - ); -}; - -interface CardPatternProps { - mouseX: MotionValue; - mouseY: MotionValue; - randomString: string; -} - -export const CardPattern: React.FC = ({ - mouseX, - mouseY, - randomString, -}) => { - // Create a CSS mask using a radial gradient centered on the mouse values. - const maskImage = useMotionTemplate`radial-gradient(250px at ${mouseX}px ${mouseY}px, white, transparent)`; - const style = { maskImage, WebkitMaskImage: maskImage }; - - return ( -
    -
    - - -

    - {randomString} -

    -
    -
    - ); -}; - -interface IconProps extends React.SVGProps { - className?: string; -} - -export const Icon: React.FC = ({ className, ...rest }) => { - return ( - - - - ); -}; - -export default EvervaultCard;