This commit is contained in:
Leo Wen
2025-03-13 20:42:43 -07:00
parent 9f4d0e7239
commit c449fef46a
6 changed files with 139 additions and 170 deletions
+36 -27
View File
@@ -1,42 +1,30 @@
import isMobile from "is-mobile";
import Marquee from "react-fast-marquee";
import { Link, useNavigate } from "react-router-dom";
import { isMobile } from "is-mobile";
import { useState } from "react";
interface BlogPosts {
title: string;
icon: string;
link: string;
}
export const blogPosts: BlogPosts[] = [{ title: "What is 𝐒𝐡𝐚𝐫𝐞 𝐌𝐲 𝐒𝐲𝐥𝐥𝐚𝐛𝐮𝐬 & why is it 𝖇𝖊𝖙𝖙𝖊𝖗 than ℝ𝕒𝕥𝕖 𝕄𝕪 ℙ𝕣𝕠𝕗𝕖𝕤𝕤𝕠𝕣?", icon: "star", link: "/useful_resources" }, { title: "🆄🆂🅴🅵🆄🅻 resources to not miss out for highschool & college students!", icon: "bolt", link: "/useful_resources" }]
function Layout({ children }: { children: React.ReactNode }) {
const navigate = useNavigate();
console.log('Layout rendering'); // Add this line
const [marqueeSpeed, setMarqueeSpeed] = useState(15);
const uploadSyllabi = () => {
navigate('/upload')
}
return (
<div className="bg-[#fcfffd] text-[#051008] w-full h-full">
<div className={`w-full text-left p-1 flex place-items-center justify-center border-b border-gray-300 `}>
<div className="flex-1 gap-4 place-items-center sticky grid grid-cols-3">
<text onClick={(e) => navigate("/")} className="text-xl text-[#29a847] cursor-pointer hover:drop-shadow-md font-extrabold flex-1 font-bol">📚 Share My Syllabus <span className="font-light text-purple-600">beta</span></text>
<div className="flex-1 flex place-items-center text-center">
<span className="material-symbols-rounded p-1 text-black">web_traffic</span>
<button onClick={(e) => navigate("/upload")} className="p-1 rounded-full anim-enter-width hover:drop-shadow-xl hover:bg-slate-950 font-[Parkinsans] pl-2 pr-2 font-bold bg-black">
<Marquee className="anim-enter-blur w-40" delay={1} gradient={true} gradientWidth="10%" gradientColor="black" speed={marqueeSpeed}>
<div className={`flex-1 gap-4 place-items-center sticky ${!isMobile() ? "grid grid-cols-3" : "flex"}`}>
{isMobile() ?
<button onClick={() => navigate("/upload")} className="p-1 flex-1 rounded-full hover:drop-shadow-xl hover:bg-slate-950 font-[Parkinsans] pl-2 pr-2 font-bold bg-black">
<Marquee className="anim-enter-blur max-w-screen" delay={1} gradient={true} gradientWidth="10%" gradientColor="black" speed={15}>
<div className="flex gap-1">
<span className="bg-blue-800 pl-2 gap-2 pr-2 rounded-md flex">
<span className="text-white font-sans">Click to Share A Syllabus!</span>
</span>
<span className="material-symbols-rounded text-white">web_traffic</span>
<span className="bg-purple-700 pl-2 gap-2 pr-2 place-items-center rounded-md flex">
@@ -45,13 +33,34 @@ function Layout({ children }: { children: React.ReactNode }) {
<span className="material-symbols-rounded pr-1 text-white">web_traffic</span>
</div>
</Marquee>
</button>
<span className="material-symbols-rounded p-1 text-black">add</span>
</div>
<div className="flex gap-4 font-light text-gray-600">
<Link to="/moderator" className=" text-gray-600">Moderator Panel</Link>
<Link to="/blog" className="text-gray-600">Blog</Link>
</div>
</button> :
<div>
<text onClick={() => navigate("/")} className="text-xl text-[#29a847] cursor-pointer hover:drop-shadow-md font-extrabold flex-1 font-bol">📚 Share My Syllabus <span className="font-light text-purple-600">beta</span></text>
<div className="flex-1 flex place-items-center text-center">
<span className="material-symbols-rounded p-1 text-black">web_traffic</span>
<button onClick={() => navigate("/upload")} className="p-1 rounded-full anim-enter-width hover:drop-shadow-xl hover:bg-slate-950 font-[Parkinsans] pl-2 pr-2 font-bold bg-black">
<Marquee className="anim-enter-blur w-40" delay={1} gradient={true} gradientWidth="10%" gradientColor="black" speed={15}>
<div className="flex gap-1">
<span className="bg-blue-800 pl-2 gap-2 pr-2 rounded-md flex">
<span className="text-white font-sans">Click to Share A Syllabus!</span>
</span>
<span className="material-symbols-rounded text-white">web_traffic</span>
<span className="bg-purple-700 pl-2 gap-2 pr-2 place-items-center rounded-md flex">
<span className="text-white font-bold font-sans">Sharying a syllabus awards 2 volunteer hours</span>
</span>
<span className="material-symbols-rounded pr-1 text-white">web_traffic</span>
</div>
</Marquee>
</button>
<span className="material-symbols-rounded p-1 text-black">add</span>
</div>
<div className="flex gap-4 font-light text-gray-600">
<Link to="/moderator" className=" text-gray-600">Moderator Panel</Link>
<Link to="/blog" className="text-gray-600">Blog</Link>
</div>
</div>
}
</div>
</div>
{children}
+2 -53
View File
@@ -1,16 +1,14 @@
import { useEffect, useState, useCallback } from 'react'
import { useEffect, useState } from 'react'
import './App.css'
import SyllabusListing, { ClassListing } from '../components/syllabus_comp';
import { Accordion, FileInput, } from "flowbite-react";
import server from '../main'
import Select from "react-select"
import AsyncSelect from "react-select/async";
import { Tab, TabList, TabPanel, Tabs } from "react-tabs";
import { useLocation, useNavigate } from 'react-router-dom';
import { isMobile } from "is-mobile";
import { useParams } from 'react-router-dom';
@@ -25,15 +23,10 @@ interface Filter {
function App() {
const navigate = useNavigate();
const [openModal, setOpenModal] = useState(false);
const [email, setEmail] = useState('');
const [name, setName] = useState('');
const [accordionOpen, setAccordionOpen] = useState(false);
const [tempFileId, setTempFileId] = useState<string>("");
const [querying, setQuerying] = useState(false);
const [file, setFile] = useState<File | null>(null);
const [filter, setFilter] = useState<Filter>({});
@@ -99,46 +92,8 @@ function App() {
}
}
function onCloseModal() {
setOpenModal(false);
setEmail('');
setName('');
setFile(null);
setAccordionOpen(false);
}
function uploadFile() {
let formdata = new FormData();
if (file === null) {
alert("File not uploaded");
throw new Error("File not uploaded");
}
formdata.append("upload", file);
formdata.append("email", email);
formdata.append("name", name);
fetch(`${server}/create`, {
method: 'POST',
body: formdata,
})
.then(async (response) => {
if (!response.ok) {
throw new Error(`Server error: ${response.statusText}`);
}
setAccordionOpen(true);
let data = await response.json();
setTempFileId(data.fileId);
setOpenModal(false);
alert("Upload successful! You will receive an email with your volunteer hours within 3 days.");
})
.catch((error) => {
console.error("Upload failed:", error);
alert("Upload failed: " + error.message);
});
}
useEffect(() => {
}, [tempFileId])
const location = useLocation();
@@ -160,13 +115,7 @@ function App() {
},
[location])
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files === null) return
if (e.target.files[0] === null) return
setFile(e.target.files[0]);
setAccordionOpen(true);
}
function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
if (e.key === "Enter") {
+2 -1
View File
@@ -1,6 +1,7 @@
import mime from "mime-types";
import { useEffect, useState } from "react";
import { ClassListing } from "./components/syllabus_comp";
import { ClassListing } from "../components/syllabus_comp";
import server from "../main";
export interface SyllabusReport {
id: string;
+2 -5
View File
@@ -2,13 +2,10 @@ import { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import { ClassListing } from "../components/syllabus_comp";
import FileView from "../components/FileView";
import { MarkdownView } from "../components/MarkdownView";
import { G4F } from "g4f"
import server from '../main'
import { MDXEditor } from '@mdxeditor/editor'
import { headingsPlugin } from '@mdxeditor/editor'
function Syllabus() {
const { id } = useParams();
@@ -39,7 +36,7 @@ function Syllabus() {
</div>
<div className="relative flex-1 h-full">
<div className="relative z-10 flex-1 h-full">
<MDXEditor className="flex-1 h-full text-3xl text-black font-bold font-[Parkinsans] text-center" markdown={`${syllabus?.content}`} plugins={[headingsPlugin()]} readOnly></MDXEditor>
<text>quick overview feature added soon</text>
</div>
</div>
</div>
+1
View File
@@ -1,4 +1,5 @@
import React, { useEffect, useState } from "react";
import server from "../main";
export interface Class {
id: number;
+96 -84
View File
@@ -6,6 +6,7 @@ import Select from "react-select";
import AsyncSelect from "react-select/async";
import server from "./main";
import { useNavigate } from "react-router-dom";
import isMobile from "is-mobile";
export interface AmbiguousClass extends Class {
school: School;
@@ -102,97 +103,108 @@ function Welcome() {
}, [selectedCollege, selectedProfessor, selectedClass, shouldNavigate, navigate]);
return (
<div className="w-full text-left h-full bg-[#fafdfb] gap-2 text-[#051008] place-items-center p-32 flex flex-col">
<h1 className="font-[Parkinsans] text-5xl w-2/3 font-extrabold">📚 Enter your <span className="text-[#47c068]"> School District, Course Name, or Professor's Name, </span> to begin!</h1>
<div className={`w-full text-left h-full bg-[#fafdfb] gap-2 text-[#051008] place-items-center ${!isMobile() ? "p-32" : "p-4"} flex flex-col`}>
<Select
className="p-1 text-lg bg-[#fafdfb] w-2/3 rounded-md border-gray-300"
placeholder="School District: i.e. RCCD, Chaffey, SBVC, etc."
options={collegeOptions}
getOptionLabel={(option: School) => option.name}
getOptionValue={(option: School) => option.name}
onChange={(selectedOption: School | null) => {
if (selectedOption) {
setSelectedCollege(selectedOption.name);
setShouldNavigate(true);
}
}}
isSearchable={true}
/>
{!isMobile() &&
<div>
<h1 className="font-[Parkinsans] text-5xl w-2/3 font-extrabold">📚 Enter your <span className="text-[#47c068]"> School District, Course Name, or Professor's Name, </span> to begin!</h1>
<AsyncSelect
className="p-1 text-lg bg-[#fafdfb] w-2/3 rounded-md border-gray-300"
placeholder="Course Name: ENG-1A, General Psych, etc."
loadOptions={async (inputValue) => {
if (!inputValue) {
inputValue = "A";
console.log('inputValue', inputValue);
}
const responses = await Promise.all(
collegeOptions.map(async (college) => {
const res = await fetch(`${server}/search/class/?q=${inputValue}&s=${college.name}`, {
method: "POST",
});
const data = await res.json();
return data.map((item: AmbiguousClass) => ({
...item,
school: college,
}));
})
);
return responses.flat();
}}
defaultOptions={defaultClassOptions}
formatOptionLabel={(option: AmbiguousClass) => (
<div>
<div>{option.className}</div>
<div style={{ fontSize: '0.8em', color: '#888' }}>{option.school.name}</div>
</div>
)}
getOptionValue={(option: AmbiguousClass) => option.className}
onChange={(selectedOption: AmbiguousClass | null) => {
if (selectedOption) {
setSelectedClass(selectedOption.className);
setShouldNavigate(true);
}
}}
isSearchable={true}
cacheOptions
<Select
className="p-1 text-lg bg-[#fafdfb] w-2/3 rounded-md border-gray-300"
placeholder="School District: i.e. RCCD, Chaffey, SBVC, etc."
options={collegeOptions}
getOptionLabel={(option: School) => option.name}
getOptionValue={(option: School) => option.name}
onChange={(selectedOption: School | null) => {
if (selectedOption) {
setSelectedCollege(selectedOption.name);
setShouldNavigate(true);
}
}}
isSearchable={true}
/>
/>
<AsyncSelect
className="p-1 text-lg bg-[#fafdfb] w-2/3 rounded-md border-gray-300"
placeholder="Course Name: ENG-1A, General Psych, etc."
loadOptions={async (inputValue) => {
if (!inputValue) {
inputValue = "A";
console.log('inputValue', inputValue);
}
const responses = await Promise.all(
collegeOptions.map(async (college) => {
const res = await fetch(`${server}/search/class/?q=${inputValue}&s=${college.name}`, {
method: "POST",
});
const data = await res.json();
return data.map((item: AmbiguousClass) => ({
...item,
school: college,
}));
})
);
return responses.flat();
}}
defaultOptions={defaultClassOptions}
formatOptionLabel={(option: AmbiguousClass) => (
<div>
<div>{option.className}</div>
<div style={{ fontSize: '0.8em', color: '#888' }}>{option.school.name}</div>
</div>
)}
getOptionValue={(option: AmbiguousClass) => option.className}
onChange={(selectedOption: AmbiguousClass | null) => {
if (selectedOption) {
setSelectedClass(selectedOption.className);
setShouldNavigate(true);
}
}}
isSearchable={true}
cacheOptions
<AsyncSelect
className="p-1 text-lg bg-[#fafdfb] w-2/3 rounded-md border-gray-300"
placeholder="Professor Name: John Doe, Jane Smith, etc."
loadOptions={async (inputValue) => {
const responses = await Promise.all(
collegeOptions.map(async (college) => {
const res = await fetch(`${server}/search/professor/?q=${inputValue}&s=${college.name}`, {
method: "POST",
});
const data = await res.json();
return data;
})
);
return responses.flat();
}}
defaultOptions={defaultProfessorOptions}
getOptionLabel={(option: Professor) => option.name}
getOptionValue={(option: Professor) => option.name}
onChange={(selectedOption: Professor | null) => {
if (selectedOption) {
setSelectedProfessor(selectedOption.name);
setShouldNavigate(true);
}
}}
isSearchable={true}
cacheOptions
/>
/>
<AsyncSelect
className="p-1 text-lg bg-[#fafdfb] w-2/3 rounded-md border-gray-300"
placeholder="Professor Name: John Doe, Jane Smith, etc."
loadOptions={async (inputValue) => {
const responses = await Promise.all(
collegeOptions.map(async (college) => {
const res = await fetch(`${server}/search/professor/?q=${inputValue}&s=${college.name}`, {
method: "POST",
});
const data = await res.json();
return data;
})
);
return responses.flat();
}}
defaultOptions={defaultProfessorOptions}
getOptionLabel={(option: Professor) => option.name}
getOptionValue={(option: Professor) => option.name}
onChange={(selectedOption: Professor | null) => {
if (selectedOption) {
setSelectedProfessor(selectedOption.name);
setShouldNavigate(true);
}
}}
isSearchable={true}
cacheOptions
/>
</div>
}
{isMobile() &&
<div className="text-center flex place-items-center flex-col gap-4 pt-4">
<img className="w-1/2 text-center" src="https://akns-images.eonline.com/eol_images/Entire_Site/201363/rs_600x600-130703124143-600.Gru.mh.070313.jpg?fit=around%7C1080:1080&output-quality=90&crop=1080:1080;center,top"></img>
<h1 className="font-sans font-black text-2xl text-blue-600">Search Syllabuses in Your School!</h1>
</div>
}
{/* Search button as fallback */}
<div className="flex flex-col text-center gap-2">
<text className="relative">or </text>
{!isMobile() && <text className="relative">or </text>}
<button
className="p-2 px-4 bg-black text-white rounded-xl drop-shadow-md hover:drop-shadow-xl cursor-pointer transition-colors"
onClick={() => setShouldNavigate(true)}
@@ -201,7 +213,7 @@ function Welcome() {
</button>
</div>
<div className="flex-1"></div>
{!isMobile() ? <div className="flex-1"></div> : <div className="h-1/3"></div>}
<div className="relative w-full overflow-hidden">
<div className="absolute top-0 bottom-0 left-0 w-8 bg-gradient-to-r from-[#fafdfb] to-transparent z-10"></div>
<div className="absolute top-0 bottom-0 right-0 w-8 bg-gradient-to-l from-[#fafdfb] to-transparent z-10"></div>