-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
17 changed files
with
660 additions
and
211 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
"use client"; | ||
import React, { useEffect, useRef } from "react"; | ||
import { LuSquareSlash } from "react-icons/lu"; | ||
|
||
interface SearchBarProps { | ||
searchQuery: string; | ||
setSearchQuery: (query: string) => void; | ||
} | ||
|
||
export function SearchBar({ searchQuery, setSearchQuery }: SearchBarProps) { | ||
const searchbox = useRef<HTMLInputElement>(null); | ||
const [isMounted, setIsMounted] = React.useState(false); | ||
|
||
useEffect(() => { | ||
function keyHandler(e: KeyboardEvent) { | ||
if (e.metaKey && e.key === "k") { | ||
e.preventDefault(); | ||
searchbox.current?.focus(); | ||
} else if (e.key === "/") { | ||
e.preventDefault(); | ||
searchbox.current?.focus(); | ||
} | ||
if (e.key === "Escape" && searchbox.current) { | ||
searchbox.current.blur(); | ||
} | ||
} | ||
|
||
setIsMounted(true); | ||
window.addEventListener("keydown", keyHandler); | ||
|
||
return () => { | ||
window.removeEventListener("keydown", keyHandler); | ||
}; | ||
}, []); | ||
|
||
if (!isMounted) return null; | ||
return ( | ||
<div className="sticky z-50 bottom-6 left-0 flex w-full items-center justify-center duration-200 focus-within:mb-8 focus-within:scale-105 active:mb-8 active:scale-105"> | ||
<input | ||
tabIndex={0} | ||
ref={searchbox} | ||
type="text" | ||
placeholder="Search files and folders" | ||
value={searchQuery} | ||
onChange={(e) => setSearchQuery(e.target.value)} | ||
className="relative z-10 w-[250px] animate-fastfade rounded-xl bg-light-button px-4 py-2 text-lg font-medium shadow-lg outline-hidden backdrop-blur-lg dark:backdrop-blur-lg transition-all duration-200 md:w-[350px] dark:bg-dark-button" | ||
/> | ||
<div className="relative right-8 z-10 text-xl opacity-50"> | ||
<LuSquareSlash /> | ||
</div> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,194 +1,70 @@ | ||
"use client"; | ||
import React, { useEffect, useState } from "react"; | ||
import Fuse from "fuse.js"; | ||
import type { Course } from "@/types/Course"; | ||
import Card from "./components/Card"; | ||
import { Link } from 'next-view-transitions' | ||
import Image from 'next/image' | ||
import React from 'react' | ||
|
||
export type CoursePapers = { | ||
name: string; | ||
code: string; | ||
urls: { | ||
semester: string; | ||
urls: { | ||
period: string; | ||
url: string; | ||
raw: string; | ||
}[]; | ||
}[]; | ||
}; | ||
export default function library() { | ||
return ( | ||
<main | ||
className="relative p-2 flex items-center justify-center flex-col h-full w-full gap-5" | ||
style={{ | ||
fontFamily: "'Bricolage Grotesque'", | ||
}} | ||
> | ||
<div className="flex flex-col justify-center mb-6 px-2 gap-5 items-center"> | ||
<div | ||
id="title" | ||
className="title transition duration-200 flex flex-col gap-1 dark:text-dark-color items-center justify-start text-light-color" | ||
> | ||
<h1 className="font-semibold text-4xl">Library.</h1> | ||
|
||
export default function Library({ courses, files }: { courses: Course[]; files: CoursePapers[] }) { | ||
const [searchQuery, setSearchQuery] = useState(""); | ||
const [semester, setSemester] = useState(0); | ||
const [searchResults, setSearchResults] = useState<CoursePapers[]>([]); | ||
const sortedFiles = files.sort((a, b) => | ||
a.code.startsWith("21") ? -1 : b.code.startsWith("21") ? 1 : 0, | ||
); | ||
const [mounted, setMounted] = useState(false); | ||
<p className="dark:text-dark-color text-light-color text-center transition duration-200 opacity-50 text-md font-medium"> | ||
Get access to Semester previous year papers or resources, all from ClassPro. | ||
</p> | ||
</div> | ||
</div> | ||
<div className='flex gap-4 flex-col items-center justify-center md:flex-row'> | ||
<Link href="/academia/library/pyq"> | ||
<div className="relative"> | ||
<div className="relative"> | ||
<Image | ||
src="/library/sem.png" | ||
className='rounded-3xl border-2 aspect-square object-cover object-left-top border-light-accent dark:border-dark-accent' | ||
|
||
useEffect(() => { | ||
setMounted(true); | ||
}, []); | ||
|
||
const fuse = new Fuse(sortedFiles, { | ||
keys: ["name", "code"], | ||
threshold: 0.3, | ||
}); | ||
|
||
const handleSearch = (query: string) => { | ||
setSearchQuery(query); | ||
if (query.length > 1) { | ||
const results = fuse.search(query).map((result) => result.item); | ||
setSearchResults(results); | ||
} else { | ||
setSearchResults([]); | ||
} | ||
}; | ||
|
||
return ( | ||
<main | ||
className="relative p-2" | ||
style={{ | ||
fontFamily: "'Bricolage Grotesque'", | ||
}} | ||
> | ||
<div className="flex flex-col md:flex-row justify-between mb-6 px-2 gap-5 items-center"> | ||
<div | ||
id="title" | ||
className="title transition duration-200 flex flex-col gap-1 dark:text-dark-color items-center md:items-start justify-start text-light-color" | ||
> | ||
<h1 className="font-semibold text-4xl">Library.</h1> | ||
|
||
<p className="dark:text-dark-color text-light-color transition duration-200 opacity-50 text-md font-medium"> | ||
Past exam papers at your fingertips. | ||
</p> | ||
</div> | ||
<input | ||
type="text" | ||
placeholder="Search for papers" | ||
value={searchQuery} | ||
onChange={(e) => handleSearch(e.target.value)} | ||
className="w-full py-2 px-4 rounded-xl focus:outline-hidden bg-light-input dark:bg-dark-input max-w-80 text-light-color dark:text-dark-color" | ||
/> | ||
</div> | ||
|
||
<div className="bg-light-background-light/50 pb-16 relative min-h-screen shadow-lg dark:bg-dark-background-darker/50 backdrop-blur-xs w-full h-full rounded-t-3xl p-4"> | ||
<div className="flex overflow-x-auto pb-4 gap-4 items-center mb-2 justify-start w-full"> | ||
<button | ||
type="button" | ||
onClick={() => setSemester(0)} | ||
disabled={searchQuery.length >= 1} | ||
className={`${ | ||
searchQuery.length < 1 && semester === 0 | ||
? "dark:bg-dark-accent bg-light-accent rounded-full text-light-background-light dark:text-dark-background-darker" | ||
: "dark:bg-dark-accent/20 bg-light-accent/20 rounded-xl text-light-accent dark:text-dark-accent" | ||
} transition-all px-3 py-2 min-w-32 text-base w-full disabled:opacity-20 disabled:cursor-not-allowed font-semibold`} | ||
> | ||
My Papers | ||
</button> | ||
<div className="h-9 w-3 rounded-full bg-light-background-dark dark:bg-dark-background-light" /> | ||
{Array.from({ length: 8 }, (_, i) => ( | ||
<button | ||
key={i} | ||
type="button" | ||
onClick={() => setSemester(i + 1)} | ||
disabled={searchQuery.length >= 1} | ||
className={`${ | ||
searchQuery.length < 1 && semester === i + 1 | ||
? "dark:bg-dark-accent bg-light-accent rounded-full text-light-background-light dark:text-dark-background-darker" | ||
: "dark:bg-dark-accent/20 bg-light-accent/20 rounded-xl text-light-accent dark:text-dark-accent" | ||
} transition-all duration-500 px-3 py-2 min-w-32 text-base w-full disabled:opacity-20 disabled:cursor-not-allowed font-semibold`} | ||
> | ||
Semester {i + 1} | ||
</button> | ||
))} | ||
</div> | ||
{semester === 0 ? null : ( | ||
<p className="w-full text-center text-light-color dark:text-dark-color gap-1 flex items-center justify-center opacity-40 font-medium text-sm mb-4"> | ||
Results:{" "} | ||
<strong className="text-light-accent dark:text-dark-accent"> | ||
{searchResults.length > 1 | ||
? searchResults.length | ||
: sortedFiles.filter((file) => | ||
file.urls.some((url) => Number(url.semester) === semester), | ||
).length}{" "} | ||
courses | ||
</strong>{" "} | ||
with{" "} | ||
<strong className="text-light-accent dark:text-dark-accent"> | ||
{searchResults.length > 1 | ||
? searchResults.reduce( | ||
(acc, file) => | ||
acc + | ||
file.urls.reduce( | ||
(urlAcc, urlGroup) => urlAcc + urlGroup.urls.length, | ||
0, | ||
), | ||
0, | ||
) | ||
: sortedFiles | ||
.filter((file) => | ||
file.urls.some( | ||
(url) => Number(url.semester) === semester, | ||
), | ||
) | ||
.reduce( | ||
(acc, file) => | ||
acc + | ||
file.urls.reduce( | ||
(urlAcc, urlGroup) => urlAcc + urlGroup.urls.length, | ||
0, | ||
), | ||
0, | ||
)}{" "} | ||
papers | ||
</strong> | ||
</p> | ||
)} | ||
{mounted && ( | ||
<div className="grid xl:grid-cols-3 lg:grid-cols-2 grid-cols-1 gap-4"> | ||
{(searchResults.length >= 1 | ||
? searchResults.sort((a, b) => { | ||
const aSemester = Math.min( | ||
...a.urls.map((url) => Number(url.semester)), | ||
); | ||
const bSemester = Math.min( | ||
...b.urls.map((url) => Number(url.semester)), | ||
); | ||
return aSemester - bSemester; | ||
}) | ||
: semester === 0 | ||
? sortedFiles.filter((a) => | ||
courses.some((b) => a.code === b.code), | ||
) | ||
: sortedFiles.filter((file) => | ||
file.urls.some((url) => Number(url.semester) === semester), | ||
) | ||
).map((result, index) => ( | ||
<Card result={result} key={index} /> | ||
))} | ||
</div> | ||
)} | ||
<p className="w-full absolute bottom-6 text-center text-light-color dark:text-dark-color gap-1 flex items-center justify-center opacity-40 font-medium text-sm mt-4"> | ||
Database of | ||
<strong className="text-light-accent dark:text-dark-accent"> | ||
{sortedFiles.length} courses | ||
</strong>{" "} | ||
with{" "} | ||
<strong className="text-light-accent dark:text-dark-accent"> | ||
{sortedFiles.reduce( | ||
(acc, file) => | ||
acc + | ||
file.urls.reduce( | ||
(urlAcc, urlGroup) => urlAcc + urlGroup.urls.length, | ||
0, | ||
), | ||
0, | ||
)}{" "} | ||
papers | ||
</strong> | ||
</p> | ||
</div> | ||
</main> | ||
); | ||
width={400} | ||
height={500} | ||
alt="Sem papers" | ||
/> | ||
<div className="absolute inset-x-0 bottom-0 h-72 bg-gradient-to-t from-black to-transparent rounded-b-3xl" /> | ||
</div> | ||
<div className='absolute bottom-0 left-0 flex flex-col gap-1 p-8'> | ||
<h1 className="font-semibold text-2xl">Sem PYQs.</h1> | ||
<p className="dark:text-dark-color text-light-color transition duration-200 opacity-50 text-base font-medium"> | ||
Get your hands on all the semester papers you need. | ||
</p> | ||
</div> | ||
</div> | ||
</Link> | ||
<Link href="/academia/library/resources"> | ||
<div className="relative"> | ||
<div className="relative"> | ||
<Image | ||
src="/library/resources.png" | ||
className='rounded-3xl aspect-square border-2 object-cover object-left-top border-light-accent dark:border-dark-accent' | ||
width={400} | ||
height={500} | ||
alt="Resource papers" | ||
/> | ||
<div className="absolute inset-x-0 bottom-0 h-72 bg-gradient-to-t from-black to-transparent rounded-b-3xl" /> | ||
</div> | ||
<div className='absolute bottom-0 left-0 flex flex-col gap-1 p-8'> | ||
<h1 className="font-semibold text-2xl">Resources.</h1> | ||
<p className="dark:text-dark-color text-light-color transition duration-200 opacity-50 text-base font-medium"> | ||
A dumpster fire of available files from the web. | ||
</p> | ||
</div> | ||
</div> | ||
</Link> | ||
</div> | ||
</main> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.