mirror of
https://github.com/Leado123/Leado123.github.io.git
synced 2026-10-01 00:32:59 +00:00
textbook cost & class name
This commit is contained in:
+83
-58
@@ -1,16 +1,17 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { DataSheetGrid, textColumn, checkboxColumn, Column, keyColumn, intColumn } from 'react-datasheet-grid';
|
||||
import "react-datasheet-grid/dist/style.css";
|
||||
import { ClassListing } from "./syllabus_comp";
|
||||
import { ClassListing, TextbookCost } from "./syllabus_comp";
|
||||
import { useParams } from "react-router-dom";
|
||||
import Select from 'react-select';
|
||||
|
||||
interface PreviewButtonProps {
|
||||
fileName: string;
|
||||
}
|
||||
|
||||
const PreviewButton = ({ fileName }: PreviewButtonProps) => {
|
||||
|
||||
const handlePreview = () => {
|
||||
const url = `/#/view/${encodeURIComponent(fileName)}`;
|
||||
const url = `#/view/${encodeURIComponent(fileName)}`;
|
||||
window.open(url, '_blank');
|
||||
};
|
||||
|
||||
@@ -21,7 +22,11 @@ const PreviewButton = ({ fileName }: PreviewButtonProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
function Admin() {
|
||||
const { highlightedSyllabusId } = useParams<{ highlightedSyllabusId?: string }>();
|
||||
|
||||
const [username, setUsername] = useState("");
|
||||
const [referenceDatabase, setReferenceDatabase] = useState<ClassListing[]>([]);
|
||||
const [wholeDatabase, setWholeDatabase] = useState<ClassListing[]>([]);
|
||||
@@ -32,68 +37,87 @@ function Admin() {
|
||||
title: "Preview",
|
||||
},
|
||||
{ ...keyColumn("reviewed", checkboxColumn), title: "Approved" },
|
||||
{ ...keyColumn("className", textColumn), title: "Class Name" },
|
||||
{ ...keyColumn("className", textColumn), title: "Class Abbrev" },
|
||||
{ ...keyColumn("fullClassName", textColumn), title: "Class Name" },
|
||||
{ ...keyColumn("textbookCost", textColumn), title: "free, cheap, moderate, expensive" },
|
||||
{ ...keyColumn("description", textColumn), title: "Description" },
|
||||
{ ...keyColumn("professor", textColumn), title: "Instructor" },
|
||||
{ ...keyColumn("length", intColumn), title: "Length" },
|
||||
{ ...keyColumn("classLength", intColumn), title: "Length" },
|
||||
{ ...keyColumn("createdByName", textColumn), title: "Submitted By" },
|
||||
{ ...keyColumn("createdByEmail", textColumn), title: "Email" },
|
||||
];
|
||||
|
||||
useEffect(() => {
|
||||
console.log(`highlightedSyllabusId is: ${highlightedSyllabusId}`);
|
||||
if (highlightedSyllabusId != null) fetchWholeDatabase();
|
||||
}, [highlightedSyllabusId]);
|
||||
|
||||
const fetchWholeDatabase = async () => {
|
||||
try {
|
||||
let formdata = new FormData();
|
||||
formdata.append("username", username);
|
||||
var response = await fetch(`https://api.sharesyllabus.me/admin/wholedatabase`, {
|
||||
method: "POST",
|
||||
body: formdata,
|
||||
});
|
||||
var data = await response.json();
|
||||
console.log(data);
|
||||
setWholeDatabase(data);
|
||||
setReferenceDatabase(data);
|
||||
} catch {
|
||||
console.log("error");
|
||||
if (!highlightedSyllabusId) {
|
||||
try {
|
||||
let formdata = new FormData();
|
||||
formdata.append("username", username);
|
||||
const response = await fetch(`https://api.sharesyllabus.me/admin/wholedatabase`, {
|
||||
method: "POST",
|
||||
body: formdata,
|
||||
});
|
||||
const data = await response.json();
|
||||
console.log(data);
|
||||
setWholeDatabase(data);
|
||||
setReferenceDatabase(data);
|
||||
} catch (error) {
|
||||
console.log("error", error);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
console.log(highlightedSyllabusId);
|
||||
const response = await fetch(`https://api.sharesyllabus.me/syllabus/${highlightedSyllabusId}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
});
|
||||
const data = await response.json();
|
||||
console.log(`data is: ${data}`);
|
||||
setWholeDatabase([...wholeDatabase, data]);
|
||||
setReferenceDatabase([...referenceDatabase, data]);
|
||||
} catch (error) {
|
||||
console.log("error", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const changeServerDatabase = async () => {
|
||||
try {
|
||||
databaseChanges.forEach(async (change) => {
|
||||
for (const change of databaseChanges) {
|
||||
const { fileName, ...sendableChange } = change;
|
||||
|
||||
|
||||
const {fileName, ...sendableChange} = change;
|
||||
|
||||
var response = await fetch(`https://api.sharesyllabus.me/admin/change/`, {
|
||||
const response = await fetch(`https://api.sharesyllabus.me/admin/change/`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username,
|
||||
id: sendableChange.id,
|
||||
data: sendableChange,
|
||||
})
|
||||
}).then((res) => {
|
||||
if (res.ok) {
|
||||
console.log("success");
|
||||
fetchWholeDatabase();
|
||||
alert("success!");
|
||||
} else {
|
||||
console.log("error");
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
console.log(response);
|
||||
});
|
||||
} catch {
|
||||
console.log("error");
|
||||
}
|
||||
}
|
||||
if (!response.ok) {
|
||||
console.log("error");
|
||||
}
|
||||
}
|
||||
fetchWholeDatabase();
|
||||
alert("success!");
|
||||
} catch (error) {
|
||||
console.log("error", error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const changes = wholeDatabase.filter((item, index) => {
|
||||
return JSON.stringify(item) !== JSON.stringify(referenceDatabase[index]);
|
||||
return JSON.stringify(item) !== JSON.stringify(referenceDatabase[index]);
|
||||
});
|
||||
setDatabaseChanges(changes);
|
||||
console.log(changes);
|
||||
@@ -101,13 +125,14 @@ function Admin() {
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center w-full min-h-screen p-4">
|
||||
<div>{highlightedSyllabusId}</div>
|
||||
<div className="text-black mb-4 w-full flex">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Username"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
onKeyDown={ (e) => {
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
fetchWholeDatabase();
|
||||
}
|
||||
@@ -121,26 +146,26 @@ function Admin() {
|
||||
Fetch Whole Database
|
||||
</button>
|
||||
<div className="flex-1 flex items-center justify-end">
|
||||
<span className="text-white">
|
||||
<span className="text-white">
|
||||
{databaseChanges.length} changes
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={changeServerDatabase}
|
||||
className="ml-2 p-2 bg-purple-700 text-white rounded"
|
||||
onClick={changeServerDatabase}
|
||||
className="ml-2 p-2 bg-purple-700 text-white rounded"
|
||||
>
|
||||
Push Changes
|
||||
Push Changes
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex flex-1 w-full h-full overflow-hidden">
|
||||
<div className=" w-full h-full ">
|
||||
<DataSheetGrid
|
||||
className="w-full h-full"
|
||||
value={wholeDatabase}
|
||||
columns={columns}
|
||||
onChange={setWholeDatabase}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-1 w-full h-full overflow-hidden">
|
||||
<div className="w-full h-full">
|
||||
<DataSheetGrid
|
||||
className="w-full h-full"
|
||||
value={wholeDatabase}
|
||||
columns={columns}
|
||||
onChange={setWholeDatabase}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
+5
-2
@@ -16,6 +16,9 @@ export const classOptions = [ // TODO REMOVE
|
||||
{ key: "physics", label: "Physics" },
|
||||
];
|
||||
|
||||
//TODO: MAKE when you scroll to bottom of page, adds more results
|
||||
//TODO: make it so that when you type in stuff, it automatically searches on the server, but there is a tick rate for responses
|
||||
|
||||
var searchQuery = "";
|
||||
|
||||
function findIfMobile() {
|
||||
@@ -179,7 +182,7 @@ function App() {
|
||||
</div>
|
||||
<div className="listing">
|
||||
{syllabi.map((syllabus, i) => {
|
||||
return <SyllabusListing key={i} fileName={syllabus.fileName} className={syllabus.className} description={syllabus.description} classLength={syllabus.classLength} professor={syllabus.professor} />
|
||||
return <SyllabusListing key={i} fileName={syllabus.fileName} className={syllabus.className} description={syllabus.description} fullClassName={syllabus.fullClassName} textbookCost={syllabus.textbookCost} classLength={syllabus.classLength} professor={syllabus.professor} />
|
||||
})}
|
||||
|
||||
</div>
|
||||
@@ -209,7 +212,7 @@ function App() {
|
||||
|
||||
<div className="listing">
|
||||
{syllabi.map((syllabus, i) => {
|
||||
return <SyllabusListing key={i} fileName={syllabus.fileName} className={syllabus.className} description={syllabus.description} classLength={syllabus.classLength} professor={syllabus.professor} />
|
||||
return <SyllabusListing key={i} fileName={syllabus.fileName} className={syllabus.className} fullClassName={syllabus.fullClassName} textbookCost={syllabus.textbookCost} description={syllabus.description} classLength={syllabus.classLength} professor={syllabus.professor} />
|
||||
})}
|
||||
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
function Flag() {
|
||||
|
||||
const params = useParams();
|
||||
const syllabusId = params.id;
|
||||
|
||||
const [reportTitle, setReportTitle] = useState("");
|
||||
const [reportBody, setReportBody] = useState("");
|
||||
const [reportBy, setReportBy] = useState("");
|
||||
|
||||
function submitReport() {
|
||||
fetch(`https://api.sharesyllabus.me/report/${syllabusId}`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
reportType: "syllabus",
|
||||
reportTitle,
|
||||
reportBody,
|
||||
reportBy,
|
||||
syllabusId
|
||||
}),
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
if (data.msg) {
|
||||
alert(data.msg);
|
||||
} else {
|
||||
alert("Report submitted successfully");
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
alert("An error occurred while submitting the report");
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full h-full p-8 ">
|
||||
<div className="w-full h-full space-y-6 bg-zinc-950 shadow-lg flex flex-col shadow-black p-8 rounded-lg">
|
||||
<nav>
|
||||
<text className="text-4xl font-semibold flex items-center justify-center align-middle">🚩 File a report on a Syllabus</text>
|
||||
<text className="text-gray-400 text-xs">syllabus: {syllabusId}</text>
|
||||
</nav>
|
||||
|
||||
<div className="space-y-6 flex-1 flex flex-col text-left">
|
||||
<div className="flex justify-center items-center">
|
||||
<text className="flex-1">Title of Report:</text>
|
||||
<button onClick={submitReport} className="justify-center rounded-md p-2 flex font-bold bg-blue-700">Submit Report<span className="material-symbols-rounded pl-2">mail</span></button>
|
||||
</div>
|
||||
<input className="w-full p-2 rounded-lg" value={reportTitle} placeholder="Enter the title of your report" onChange={(e) => {setReportTitle(e.target.value)}}></input>
|
||||
<text className="pt-1 pb-1">Email (OPTIONAL: to get in touch):</text>
|
||||
<input className="w-full p-2 rounded-lg" value={reportBy} placeholder="example@company.com" onChange={(e) => setReportBy(e.target.value)}></input>
|
||||
<text className="pt-1 pb-1">Details:</text>
|
||||
<textarea className="w-full p-2 flex-1 rounded-lg text-black" value={reportBody} placeholder="What is wrong with this syllabus?: incorrect class for syllabus, bad description...." onChange={(e) => setReportBody(e.target.value)}></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Flag;
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
import mime from "mime-types";
|
||||
import { useEffect, useState } from "react";
|
||||
import { ClassListing } from "./syllabus_comp";
|
||||
|
||||
export interface SyllabusReport {
|
||||
id: string;
|
||||
reportTitle: string;
|
||||
reportBody: string;
|
||||
reportBy: string;
|
||||
reportDate: string;
|
||||
syllabusId: string;
|
||||
}
|
||||
|
||||
function Reports() {
|
||||
|
||||
const [username, setUsername] = useState("");
|
||||
const [reports, setReports] = useState<SyllabusReport[]>([]);
|
||||
|
||||
function obtainReports() {
|
||||
fetch("https://api.sharesyllabus.me/admin/getreports", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ username }),
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
console.log(data);
|
||||
setReports(data);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
const [currentReport, setCurrentReport] = useState<SyllabusReport | null>(null);
|
||||
const [currentSyllabus, setCurrentSyllabus] = useState<ClassListing | null>(null);
|
||||
const [currentFileName, setCurrentFileName] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
if (currentSyllabus == null || currentSyllabus?.mimeType == null) return;
|
||||
setCurrentFileName(`${currentSyllabus?.id}.${mime.extension(currentSyllabus.mimeType)}`)
|
||||
}, [currentSyllabus])
|
||||
|
||||
function viewReport(sId: string, report: SyllabusReport) {
|
||||
setCurrentReport(report);
|
||||
fetch(`https://api.sharesyllabus.me/admin/getsyllabus/${sId}`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ username }),
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
console.log(data);
|
||||
setCurrentSyllabus(data);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async function previewSyllabus() {
|
||||
const url = `#/view/${encodeURIComponent(currentFileName)}`;
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
|
||||
async function editSyllabusEntry() {
|
||||
const url = `#/admin/${currentSyllabus?.id}`;
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center w-full min-h-screen p-4">
|
||||
<div className="text-black mb-4 w-full flex">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Username"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
obtainReports()
|
||||
}
|
||||
}}
|
||||
className="p-2 border border-gray-300 rounded"
|
||||
/>
|
||||
<button
|
||||
onClick={obtainReports}
|
||||
className="ml-2 p-2 bg-blue-500 text-white rounded"
|
||||
>
|
||||
Obtain Reports
|
||||
</button>
|
||||
<div className="flex-1 flex items-center justify-end">
|
||||
<span className="text-white">
|
||||
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 w-full h-full">
|
||||
<div className=" w-full h-full grid grid-cols-4 gap-4">
|
||||
{reports.map((report, i) => <ReportComponent report={report} viewReport={viewReport} key={i} />)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full bg-zinc-950 grid grid-cols-2 p-4 rounded-md">
|
||||
<div className="flex flex-col gap-2 text-left">
|
||||
<text className="font-bold">
|
||||
{currentReport?.reportTitle}
|
||||
</text>
|
||||
<text>
|
||||
{currentReport?.reportBody}
|
||||
</text>
|
||||
</div>
|
||||
<div className="bg-white p-4 rounded-md text-black flex">
|
||||
<div className="flex flex-1 flex-col text-left">
|
||||
<text className="font-bold">
|
||||
{currentSyllabus?.className}
|
||||
</text>
|
||||
<text>
|
||||
{currentSyllabus?.professor}
|
||||
</text>
|
||||
<text>
|
||||
{currentSyllabus?.description}{currentSyllabus?.fileName}
|
||||
</text>
|
||||
<text>
|
||||
classLength: {currentSyllabus?.classLength} wk
|
||||
</text>
|
||||
</div>
|
||||
<div className="flex-col flex gap-2">
|
||||
<button className="aspect-square bg-blue-600 p-2 rounded-md text-yellow-300" onClick={previewSyllabus}><span className="material-symbols-rounded">open_in_new</span></button>
|
||||
<button className="flex-1 bg-yellow-300 p-2 rounded-md flex items-center" onClick={editSyllabusEntry}><span className="material-symbols-rounded">edit</span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div >
|
||||
)
|
||||
}
|
||||
|
||||
interface ReportComponentProps {
|
||||
report: SyllabusReport;
|
||||
viewReport: (sId: string, report: SyllabusReport) => void;
|
||||
}
|
||||
|
||||
function ReportComponent({ report, viewReport }: ReportComponentProps) {
|
||||
const date = new Date(report.reportDate).toLocaleDateString("en-US");
|
||||
|
||||
const view = (e: React.MouseEvent<HTMLDivElement, MouseEvent>): void => {
|
||||
e.stopPropagation();
|
||||
viewReport(report.syllabusId, report);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="bg-white text-black p-2 rounded-md text-left flex flex-col hover:bg-gray-200" onClick={view}>
|
||||
<div className="flex items-center space-x-2">
|
||||
<text className="font-bold text-lg">{report.reportTitle}</text>
|
||||
<text className="font-light text-md text-gray-500 flex-1">{date}</text>
|
||||
<button className="shadow-lg rounded-md aspect-square text-white bg-black p-1 items-center flex hover:bg-red-600"><span className="material-symbols-rounded text-md">delete</span></button>
|
||||
</div>
|
||||
<text>{report.reportBody}</text>
|
||||
<text className="text-xs font-light">{report.reportBy}</text>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Reports;
|
||||
@@ -16,6 +16,7 @@ function View() {
|
||||
|
||||
const { file } = useParams();
|
||||
|
||||
// TODO: make it work on mobile (pdf viewer doesen't work)
|
||||
|
||||
const isPDF = checkIfPdf(file || "");
|
||||
|
||||
|
||||
+18
-19
@@ -1,22 +1,21 @@
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { Route, Routes, HashRouter } from 'react-router-dom';
|
||||
import './index.css'
|
||||
import App from './App.tsx'
|
||||
import View from './View.tsx'
|
||||
import Admin from './Admin.tsx'
|
||||
|
||||
|
||||
|
||||
import './index.css';
|
||||
import App from './App.tsx';
|
||||
import View from './View.tsx';
|
||||
import Admin from './Admin.tsx';
|
||||
import Flag from './Flag.tsx';
|
||||
import Reports from './Reports.tsx';
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
|
||||
|
||||
<HashRouter>
|
||||
<Routes>
|
||||
<Route path="/" element={<App />} />
|
||||
<Route path="/view/:file" element={<View />} />
|
||||
<Route path="/admin" element={<Admin />} />
|
||||
</Routes>
|
||||
</HashRouter>
|
||||
|
||||
)
|
||||
<HashRouter>
|
||||
<Routes>
|
||||
<Route path="/" element={<App />} />
|
||||
<Route path="/view/:file" element={<View />} />
|
||||
<Route path="/admin" element={<Admin />} />
|
||||
<Route path="/admin/:highlightedSyllabusId" element={<Admin />} />
|
||||
<Route path="/flag/:id" element={<Flag />} />
|
||||
<Route path="/reports" element={<Reports />} />
|
||||
</Routes>
|
||||
</HashRouter>
|
||||
);
|
||||
|
||||
+70
-5
@@ -2,6 +2,16 @@ import "./syllabuslisting.css";
|
||||
import "./App.tsx";
|
||||
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
export enum TextbookCost {
|
||||
free,
|
||||
cheap,
|
||||
moderate,
|
||||
expensive
|
||||
}
|
||||
|
||||
// TODO: implement fullClassName and textbook cost in mobile
|
||||
|
||||
export interface ClassListing {
|
||||
className: string;
|
||||
@@ -12,13 +22,35 @@ export interface ClassListing {
|
||||
reviewed?: boolean;
|
||||
createdByName?: string;
|
||||
createdByEmail?: string;
|
||||
mimeType?: string;
|
||||
textbookCost?: string;
|
||||
fullClassName?: string;
|
||||
id?: string;
|
||||
}
|
||||
function findIfMobile() {
|
||||
//return true;
|
||||
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
||||
}
|
||||
function SyllabusListing({ className, professor, fileName, description, classLength }: ClassListing) {
|
||||
function SyllabusListing({ className, professor, fileName, description, classLength, fullClassName, textbookCost }: ClassListing) {
|
||||
|
||||
const [hideMore, setHideMore] = useState(true);
|
||||
|
||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||
|
||||
const id = fileName.split(".")[0];
|
||||
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (buttonRef.current && !buttonRef.current.contains(event.target as Node)) {
|
||||
setHideMore(true);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleClickOutside);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -26,21 +58,54 @@ function SyllabusListing({ className, professor, fileName, description, classLen
|
||||
navigate(`/view/${encodeURIComponent(`${fileName}`)}`);
|
||||
}
|
||||
|
||||
async function handleFlag() {
|
||||
navigate(`/flag/${encodeURIComponent(`${id}`)}`);
|
||||
}
|
||||
|
||||
const textbookCostElement = () => {
|
||||
if (textbookCost === "free") {
|
||||
return <span className="text-xs text-green-500 font-bold">🤑 Free</span>;
|
||||
} else if (textbookCost === "cheap") {
|
||||
return <span className="text-xs text-yellow-400 font-bold">💸 Cheap</span>;
|
||||
} else if (textbookCost === "moderate") {
|
||||
return <span className="text-xs text-orange-600 font-bold">💵 Moderate</span>;
|
||||
} else {
|
||||
return <span className="text-xs text-red-600 font-bold">💰 Expensive</span>;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!findIfMobile()) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="w-full flex space-x-4 bg-white text-black rounded-lg p-3">
|
||||
<div className="w-full flex space-x-4 bg-white text-black rounded-lg p-3" >
|
||||
<div className="flex text-left flex-col w-1/4">
|
||||
<span className="font-extrabold">{className}</span>
|
||||
<span className="text-xs text-gray-600">{fullClassName}</span>
|
||||
{textbookCostElement()}
|
||||
<span className="flex-1 italic">{professor}</span>
|
||||
{classLength > 7 ? <span className="text-xs font-light text-green-600">{classLength} weeks course (normal)</span> : <span className="text-xs font-light text-red-600">{classLength} weeks course</span>}
|
||||
{classLength > 7 ? <span className="text-xs font-light text-green-600">{classLength} weeks course</span> : <span className="text-xs font-light text-red-600">{classLength} weeks course</span>}
|
||||
</div>
|
||||
<div className="flex-1 flex flex-col text-center">
|
||||
<span className="w-3/4 font-serif">"{description}"</span>
|
||||
</div>
|
||||
<button className="aspect-square flex items-center align-middle justify-center rounded-lg hover:bg-slate-200 text-black" onClick={handleView}><span className="material-symbols-rounded text-2xl">open_in_new</span></button>
|
||||
<div className="flex flex-col">
|
||||
<button className="aspect-square flex items-center align-middle justify-center hover:bg-slate-200 rounded-sm text-black" onClick={handleView}>
|
||||
<span className="material-symbols-rounded flex-1 text-2xl">open_in_new</span>
|
||||
</button>
|
||||
<span className="flex-1"></span>
|
||||
<button className="aspect-square flex items-center align-middle justify-center hover:bg-slate-200 rounded-sm text-black" onClick={() => setHideMore(!hideMore)}>
|
||||
<span className="material-symbols-rounded text-2xl">more_horiz</span>
|
||||
<div className="relative" hidden={hideMore}>
|
||||
<button ref={buttonRef} onClick={handleFlag} className="absolute border-black border-2 -left-14 top-2 drop-shadow-lg bg-white rounded-lg aspect-square p-4">
|
||||
<span className="material-symbols-rounded text-red-600 text-2xl">flag</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
@@ -52,7 +117,7 @@ function SyllabusListing({ className, professor, fileName, description, classLen
|
||||
<span className=" font-extrabold">{className}</span>
|
||||
<span className="flex-1 italic">{professor}</span>
|
||||
<span className="w-4/5 font-serif">"{description}"</span>
|
||||
{classLength > 7 ? <span className="text-xs font-light text-green-600">{classLength} weeks course (normal)</span> : <span className="text-xs font-light text-red-600">{classLength} weeks course</span>}
|
||||
{classLength > 7 ? <span className="text-xs font-light text-green-600">{classLength} weeks course</span> : <span className="text-xs font-light text-red-600">{classLength} weeks course</span>}
|
||||
</div>
|
||||
<button className="flex items-center h-full p-2 rounded-lg bg-slate-200 text-black" onClick={handleView}>
|
||||
<span className="material-symbols-rounded text-2xl">open_in_new</span>
|
||||
|
||||
Reference in New Issue
Block a user