diff --git a/app/Loader/Loader.css b/app/Loader/Loader.css new file mode 100644 index 0000000..508780e --- /dev/null +++ b/app/Loader/Loader.css @@ -0,0 +1,45 @@ +/* HTML:
*/ +.loader { + width: 40px; + margin: auto; + aspect-ratio: 1; + --c: linear-gradient(#000 0 0); + --r1: radial-gradient(farthest-side at bottom, #000 93%, #0000); + --r2: radial-gradient(farthest-side at top, #000 93%, #0000); + background: var(--c), var(--r1), var(--r2), var(--c), var(--r1), var(--r2), + var(--c), var(--r1), var(--r2); + background-repeat: no-repeat; + animation: l2 1s infinite alternate; +} +@keyframes l2 { + 0%, + 25% { + background-size: 8px 0, 8px 4px, 8px 4px, 8px 0, 8px 4px, 8px 4px, 8px 0, + 8px 4px, 8px 4px; + background-position: 0 50%, 0 calc(50% - 2px), 0 calc(50% + 2px), 50% 50%, + 50% calc(50% - 2px), 50% calc(50% + 2px), 100% 50%, 100% calc(50% - 2px), + 100% calc(50% + 2px); + } + 50% { + background-size: 8px 100%, 8px 4px, 8px 4px, 8px 0, 8px 4px, 8px 4px, 8px 0, + 8px 4px, 8px 4px; + background-position: 0 50%, 0 calc(0% - 2px), 0 calc(100% + 2px), 50% 50%, + 50% calc(50% - 2px), 50% calc(50% + 2px), 100% 50%, 100% calc(50% - 2px), + 100% calc(50% + 2px); + } + 75% { + background-size: 8px 100%, 8px 4px, 8px 4px, 8px 100%, 8px 4px, 8px 4px, + 8px 0, 8px 4px, 8px 4px; + background-position: 0 50%, 0 calc(0% - 2px), 0 calc(100% + 2px), 50% 50%, + 50% calc(0% - 2px), 50% calc(100% + 2px), 100% 50%, 100% calc(50% - 2px), + 100% calc(50% + 2px); + } + 95%, + 100% { + background-size: 8px 100%, 8px 4px, 8px 4px, 8px 100%, 8px 4px, 8px 4px, + 8px 100%, 8px 4px, 8px 4px; + background-position: 0 50%, 0 calc(0% - 2px), 0 calc(100% + 2px), 50% 50%, + 50% calc(0% - 2px), 50% calc(100% + 2px), 100% 50%, 100% calc(0% - 2px), + 100% calc(100% + 2px); + } +} diff --git a/app/Loader/Loader.jsx b/app/Loader/Loader.jsx new file mode 100644 index 0000000..d765bf9 --- /dev/null +++ b/app/Loader/Loader.jsx @@ -0,0 +1,7 @@ +import "./Loader.css"; + +function Loader() { + return
; +} + +export default Loader; diff --git a/app/components/Footer.js b/app/components/Footer.js index 794e805..25817d6 100644 --- a/app/components/Footer.js +++ b/app/components/Footer.js @@ -9,7 +9,7 @@ import Link from "next/link"; export default function Footer() { return ( ); } diff --git a/app/components/Home/Hero.js b/app/components/Home/Hero.js index 1a10486..dadf672 100644 --- a/app/components/Home/Hero.js +++ b/app/components/Home/Hero.js @@ -1,16 +1,27 @@ import React from "react"; import { PiHandSwipeLeftDuotone } from "react-icons/pi"; import { IoMdArrowForward } from "react-icons/io"; +import { Boxes } from "../ui/background-boxes"; const Hero = () => { return (
-

Ethical Spectacle Research ;)

-

Developers that write *clean* code.

-

Swipe

-

Scroll

+

+ Ethical Spectacle Research ;) +

+

+ Developers that write *clean* code. +

+

+ + Swipe +

+

+ + Scroll +

Research.

@@ -30,9 +41,16 @@ const Hero = () => { taking leadership roles, etc.

- -

Become a Member.

-
+
+

+ Become a Member. +

+
+ +
diff --git a/app/components/Home/NewHero.jsx b/app/components/Home/NewHero.jsx new file mode 100644 index 0000000..b898d96 --- /dev/null +++ b/app/components/Home/NewHero.jsx @@ -0,0 +1,62 @@ +"use client"; +import { Boxes } from "../ui/background-boxes"; +import { cn } from "../../../lib/utils"; +import { FaAnglesDown } from "react-icons/fa6"; +import { IoIosArrowForward } from "react-icons/io"; + +function NewHero() { + return ( +
+
+ + +

+ Ethical Spectacle Research +

+

+ We are an Arizona-based non-profit dedicated to software development, + community building, and the publication of ethical technology and social + research. Our judging panel featured four esteemed professionals, + including university professors, global ethical leaders, and venture + capitalists. +

+ + {/*
+ +

Sign Up

+ +
+ +

Research Projects

+ +
+
*/} + + + + +
+ ); +} + +export default NewHero; diff --git a/app/components/Home/ResearchProjectAgents.js b/app/components/Home/ResearchProjectAgents.js index 3016029..d33a486 100644 --- a/app/components/Home/ResearchProjectAgents.js +++ b/app/components/Home/ResearchProjectAgents.js @@ -3,13 +3,17 @@ import { PiHandSwipeLeftDuotone } from "react-icons/pi"; const AgentsProjectCarousel = () => { return ( -
+

Project 002

Agent Network Effects

-

We're studying the ability of LLM agents to police network effects.

-

+

+ We're studying the ability of LLM agents to police network effects. +

+

+ +

diff --git a/app/components/Home/ResearchProjectBias.js b/app/components/Home/ResearchProjectBias.js index 1566956..efd674f 100644 --- a/app/components/Home/ResearchProjectBias.js +++ b/app/components/Home/ResearchProjectBias.js @@ -4,13 +4,18 @@ import { PiHandSwipeLeftDuotone } from "react-icons/pi"; const BiasProjectCarousel = () => { return ( -
+

Project 001

Bias Detection

-

A text classification model trained to detect generalizations, unfairness, and stereotypes in text.

-

+

+ A text classification model trained to detect generalizations, + unfairness, and stereotypes in text. +

+

+ +

Research.

diff --git a/app/components/NavBar.js b/app/components/NavBar.js index 81dae0d..95edd0b 100644 --- a/app/components/NavBar.js +++ b/app/components/NavBar.js @@ -24,11 +24,7 @@ const Navbar = () => { const position = useScrollPosition(); return ( -
@@ -95,8 +107,14 @@ const Navbar = () => { {/* Mobile Menu Overlay */} {isOpen && ( -
setIsOpen(false)}> -
e.stopPropagation()}> +
setIsOpen(false)} + > +
e.stopPropagation()} + >

Menu

{/* Navigation Links */} @@ -107,7 +125,13 @@ const Navbar = () => { > Home 🏠 - setIsOpen(false)} className="p-4 text-xl border-b border-gray-300">Research 🔬 + setIsOpen(false)} + className="p-4 text-xl border-b border-gray-300" + > + Research 🔬 + setIsOpen(false)} diff --git a/app/components/ui/background-boxes.jsx b/app/components/ui/background-boxes.jsx new file mode 100644 index 0000000..6bb8af9 --- /dev/null +++ b/app/components/ui/background-boxes.jsx @@ -0,0 +1,76 @@ +"use client"; +import React from "react"; +import { motion } from "framer-motion"; +import { cn } from "../../../lib/utils"; + +export const BoxesCore = ({ className, ...rest }) => { + const rows = new Array(150).fill(1); + const cols = new Array(100).fill(1); + let colors = [ + "--sky-300", + "--pink-300", + "--green-300", + "--yellow-300", + "--red-300", + "--purple-300", + "--blue-300", + "--indigo-300", + "--violet-300", + ]; + const getRandomColor = () => { + return colors[Math.floor(Math.random() * colors.length)]; + }; + + return ( +
+ {rows.map((_, i) => ( + + {cols.map((_, j) => ( + + {j % 2 === 0 && i % 2 === 0 ? ( + + + + ) : null} + + ))} + + ))} +
+ ); +}; + +export const Boxes = React.memo(BoxesCore); diff --git a/app/page.js b/app/page.js index d98e9d0..87511eb 100644 --- a/app/page.js +++ b/app/page.js @@ -1,20 +1,22 @@ -'use client'; -import React from 'react'; -import Hero from './components/Home/Hero'; -import PicsCarousel from './components/Home/PicsCarousel'; -import BiasProjectCarousel from './components/Home/ResearchProjectBias'; -import AgentsProjectCarousel from './components/Home/ResearchProjectAgents'; -import Partners from './components/Home/Partners'; +"use client"; +import React from "react"; +import Hero from "./components/Home/Hero"; +import PicsCarousel from "./components/Home/PicsCarousel"; +import BiasProjectCarousel from "./components/Home/ResearchProjectBias"; +import AgentsProjectCarousel from "./components/Home/ResearchProjectAgents"; +import Partners from "./components/Home/Partners"; +import NewHero from "./components/Home/NewHero"; const Home = () => { return ( -
+ <> + {/* */} -
+ ); }; diff --git a/app/research/ResearchProjects.js b/app/research/ResearchProjects.js index e6f6145..f7a32d1 100644 --- a/app/research/ResearchProjects.js +++ b/app/research/ResearchProjects.js @@ -1,9 +1,10 @@ -'use client'; -import React, { useState, useEffect } from 'react'; -import { useAuth } from '../context/AuthContext'; -import { FaArrowRight } from 'react-icons/fa'; +"use client"; +import React, { useState, useEffect } from "react"; +import { useAuth } from "../context/AuthContext"; +import { FaArrowRight } from "react-icons/fa"; +import Loader from "@/Loader/Loader"; -const { API_URL_PROD } = require('../config/config'); +const { API_URL_PROD } = require("../config/config"); const ResearchProjects = () => { const { isLoggedIn, userEmail } = useAuth(); @@ -16,35 +17,42 @@ const ResearchProjects = () => { try { const res = await fetch(`${API_URL_PROD}/get_all_research_projects`); const data = await res.json(); - const publishedProjects = data.filter(project => project.published === 1); + const publishedProjects = data.filter( + (project) => project.published === 1 + ); setProjects(publishedProjects); if (isLoggedIn) { - publishedProjects.forEach(project => fetchApplications(project.id)); + publishedProjects.forEach((project) => fetchApplications(project.id)); } } catch (error) { - console.error('Error fetching projects:', error); + console.error("Error fetching projects:", error); } }; const fetchApplications = async (projectId) => { try { - const res = await fetch(`${API_URL_PROD}/get_user_research_applications`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ email: userEmail }) - }); + const res = await fetch( + `${API_URL_PROD}/get_user_research_applications`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ email: userEmail }), + } + ); const data = await res.json(); - const userApplication = data.find(app => app.project_id === projectId); + const userApplication = data.find( + (app) => app.project_id === projectId + ); if (userApplication) { setApplications((prev) => ({ ...prev, - [projectId]: userApplication.status + [projectId]: userApplication.status, })); } } catch (error) { - console.error('Error fetching applications:', error); + console.error("Error fetching applications:", error); } }; @@ -53,66 +61,81 @@ const ResearchProjects = () => { const applyForProject = async (project_id) => { try { - const response = await fetch(`${API_URL_PROD}/submit_researcher_application`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - project_id, - email: userEmail - }) - }); + const response = await fetch( + `${API_URL_PROD}/submit_researcher_application`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + project_id, + email: userEmail, + }), + } + ); if (response.ok) { setApplications((prev) => ({ ...prev, - [project_id]: 'pending' + [project_id]: "pending", })); setErrorMessages((prev) => ({ ...prev, - [project_id]: '' // Clear error message on successful application + [project_id]: "", // Clear error message on successful application })); } else { const errorText = await response.text(); setErrorMessages((prev) => ({ ...prev, - [project_id]: errorText + [project_id]: errorText, })); } } catch (error) { - console.error('Error applying for project:', error); + console.error("Error applying for project:", error); setErrorMessages((prev) => ({ ...prev, - [project_id]: 'An unexpected error occurred.' + [project_id]: "An unexpected error occurred.", })); } }; return (
-

Research Projects

+

+ Research Projects +

{projects.length > 0 ? (
{projects.map((project) => ( -
+

- + {project.title}

-

{project.description}

+

+ {project.description} +

{isLoggedIn ? ( applications[project.id] ? ( -

Status: {applications[project.id]}

+

+ Status: {applications[project.id]} +

) : ( -
))}
) : ( -
Working on it...
+ )}
); diff --git a/lib/utils.js b/lib/utils.js new file mode 100644 index 0000000..abc308d --- /dev/null +++ b/lib/utils.js @@ -0,0 +1,6 @@ +import { ClassValue, clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs) { + return twMerge(clsx(inputs)); +} diff --git a/package-lock.json b/package-lock.json index 40ddacc..75c3163 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,8 +22,9 @@ "@tippyjs/react": "^4.2.6", "@vercel/analytics": "^1.2.2", "@vercel/speed-insights": "^1.0.10", + "clsx": "^2.1.1", "date-fns": "^3.6.0", - "framer-motion": "^11.2.10", + "framer-motion": "^11.3.27", "moment": "^2.30.1", "next": "14.2.2", "react": "^18.3.1", @@ -40,6 +41,7 @@ "sharp": "^0.33.3", "slick-carousel": "^1.8.1", "swiper": "^11.1.1", + "tailwind-merge": "^2.5.2", "tippy.js": "^6.3.7" }, "devDependencies": { @@ -2209,9 +2211,9 @@ } }, "node_modules/framer-motion": { - "version": "11.2.10", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.2.10.tgz", - "integrity": "sha512-/gr3PLZUVFCc86a9MqCUboVrALscrdluzTb3yew+2/qKBU8CX6nzs918/SRBRCqaPbx0TZP10CB6yFgK2C5cYQ==", + "version": "11.3.27", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.3.27.tgz", + "integrity": "sha512-Uf72PFEZuei/1IeCmGJ236EIl6VUri7SLovtTGg/cOAzMHG01CXXg1aL3ofuTuYr6Sq1QALlcEDaTig0PXb4Dw==", "dependencies": { "tslib": "^2.4.0" }, @@ -3814,6 +3816,15 @@ "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==" }, + "node_modules/tailwind-merge": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.5.2.tgz", + "integrity": "sha512-kjEBm+pvD+6eAwzJL2Bi+02/9LFLal1Gs61+QB7HvTfQQ0aXwC5LGT8PEt1gS0CWKktKe6ysPTAy3cBC5MeiIg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, "node_modules/tailwindcss": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz", diff --git a/package.json b/package.json index 98970be..32579d5 100644 --- a/package.json +++ b/package.json @@ -24,8 +24,9 @@ "@tippyjs/react": "^4.2.6", "@vercel/analytics": "^1.2.2", "@vercel/speed-insights": "^1.0.10", + "clsx": "^2.1.1", "date-fns": "^3.6.0", - "framer-motion": "^11.2.10", + "framer-motion": "^11.3.27", "moment": "^2.30.1", "next": "14.2.2", "react": "^18.3.1", @@ -42,6 +43,7 @@ "sharp": "^0.33.3", "slick-carousel": "^1.8.1", "swiper": "^11.1.1", + "tailwind-merge": "^2.5.2", "tippy.js": "^6.3.7" }, "devDependencies": { diff --git a/tailwind.config.js b/tailwind.config.js index b11cf0e..a03be69 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,3 +1,9 @@ +const defaultTheme = require("tailwindcss/defaultTheme"); +const colors = require("tailwindcss/colors"); +const { + default: flattenColorPalette, +} = require("tailwindcss/lib/util/flattenColorPalette"); + /** @type {import('tailwindcss').Config} */ module.exports = { content: [ @@ -13,25 +19,37 @@ module.exports = { "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))", }, maxWidth: { - '1440': '1440px' + 1440: "1440px", }, colors: { - 'cpink': '#FE93B1', - 'cpinklight': '#FE93B1', - 'cream': '#F9F4F0', + cpink: "#FE93B1", + cpinklight: "#FE93B1", + cream: "#F9F4F0", }, keyframes: { - 'border-spin': { - '100%': { transform: 'rotate(-360deg)' } - } + "border-spin": { + "100%": { transform: "rotate(-360deg)" }, + }, }, animation: { - 'border-spin': 'border-spin 4s linear infinite', - } + "border-spin": "border-spin 4s linear infinite", + }, }, fontFamily: { jost: ["Jost", "system-ui", "sans-serif"], }, }, - plugins: [], + plugins: [addVariablesForColors], }; + +// This plugin adds each Tailwind color as a global CSS variable, e.g. var(--gray-200). +function addVariablesForColors({ addBase, theme }) { + let allColors = flattenColorPalette(theme("colors")); + let newVars = Object.fromEntries( + Object.entries(allColors).map(([key, val]) => [`--${key}`, val]) + ); + + addBase({ + ":root": newVars, + }); +}