add upload file boilerplate + changes to results page

This commit is contained in:
afazio1
2024-04-21 15:42:12 -04:00
parent ae25f65fdd
commit b460067dfa
9 changed files with 206 additions and 14 deletions

View File

@@ -23,7 +23,7 @@ export default function App() {
<>
<SessionContext.Provider value={session}>
<Navbar />
<div className="App" style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', height: '90vh' , gap: 15}}>
<div className="App">
<Outlet />
</div>
</SessionContext.Provider>
@@ -49,7 +49,7 @@ function Navbar() {
<>
<div>
<img src="/PeerNotes.png" alt="PeerNotes Logo" style={{ height: '60px' }} />
<Link to="/" style={linkStyle}>Home</Link>
<Link to="/register" style={linkStyle}>Upload</Link>
<Link to="/search" style={linkStyle}>Search</Link>
</div>
<div>

View File

@@ -10,6 +10,7 @@ import Login from './screens/Login.tsx';
import Signup from './screens/Signup.tsx';
import Results from './screens/Results.tsx';
import MainSearch from './screens/MainSearch.tsx';
import RegisterFile from './screens/RegisterFile.tsx';
const router = createBrowserRouter([
{
@@ -25,6 +26,10 @@ const router = createBrowserRouter([
path: "results",
element: <Results/>,
},
{
path: "register",
element: <RegisterFile />,
},
]
},

View File

@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import '../styles/MainScreenWrapper.css';
import { getAuthHeaders } from '../utils/getAuthHeaders';
import { Professor, Course, Topic } from "../types/types";
import { Professor, Course, Topic, Semester } from "../types/types";
interface Filters extends Record<string, string> {
professor: string;
@@ -19,6 +19,7 @@ const MainSearch: React.FC = () => {
const [professors, setProfessors] = useState<Professor[]>([]);
const [courses, setCourses] = useState<Course[]>([]);
const [topics, setTopics] = useState<Topic[]>([]);
const [semesters, setSemesters] = useState<Semester[]>([]);
const navigate = useNavigate();
useEffect(() => {
@@ -28,6 +29,8 @@ const MainSearch: React.FC = () => {
fetchCourses();
// Fetch topics
fetchTopics();
// Fetch semesters
fetchSemesters();
}, []);
@@ -82,6 +85,22 @@ const MainSearch: React.FC = () => {
console.error('Error fetching topics:', error);
}
};
const fetchSemesters = async () => {
try {
const response = await fetch('http://localhost:8000/api/semesters', {
method: 'GET',
headers: getAuthHeaders(),
});
const data = await response.json();
if (Array.isArray(data)) {
setSemesters(data);
} else {
console.error('Data fetched is not an array:', data);
}
} catch (error) {
console.error('Error fetching courses:', error);
}
};
const handleChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
const { name, value } = event.target;
@@ -128,6 +147,15 @@ const MainSearch: React.FC = () => {
))}
</select>
</div>
<div className="filter-item">
<label htmlFor="semesters">Semester:</label>
<select id="semester" name="semester" value={filters.semester} onChange={handleChange}>
<option value="">Select a Semester</option>
{semesters.map((semester) => (
<option key={semester.id} value={semester.id}>{semester.name}</option>
))}
</select>
</div>
<button type="submit" className="submit-button">Submit</button>
</form>
</>

View File

@@ -0,0 +1,66 @@
import "../styles/RegisterFile.css";
import { RegisteredFile } from "../types/types";
import { useState, useEffect } from "react";
export default function RegisterFile() {
const [registeredFiles, setRegisteredFiles] = useState<RegisteredFile[]>([]);
useEffect(() => {
fetchRegisteredFiles();
}, [])
const fetchRegisteredFiles = () => {
return
fetch("http://localhost:5000/files") // replace with local api endpoint
.then(response => response.json())
.then(data => {
setRegisteredFiles(data);
})
.catch(error => console.error(error));
}
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
// handle file upload
const formData = new FormData(event.target as HTMLFormElement);
const file = formData.get('file');
console.log(file, typeof file)
}
return (
<div className="container">
<div className="file-upload-card">
<h1>Upload a File</h1>
<p>Host a local file for the GT community!</p>
<form onSubmit={handleSubmit}>
<label htmlFor="file">File</label>
<input type="file" name="file" id="file" />
<button type="submit" className="submit-button">Submit</button>
</form>
</div>
<div>
<h1>Registered Files</h1>
<table>
<thead>
<tr>
<th>File Name</th>
<th>Original Author</th>
<th>Professor</th>
<th>Semester</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{registeredFiles.map((file) => (
<tr key={file.id}>
<td>{file.filename}</td>
<td>{file.original_author.username}</td>
<td>{file.professor.name}</td>
<td>{file.semester.name}</td>
<td>{file.status}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
)
}

View File

@@ -49,17 +49,34 @@ const Results: React.FC = () => {
{isLoading ? (
<p>Loading...</p>
) : files.length ? (
files.map((file) => (
<div key={file.id} className={styles.fileCard}>
<h3>{file.filename}</h3>
<p>Course: {file.course.name} {file.course.number}</p>
<p>Professor: {file.professor.name}</p>
<p>Semester: {file.semester.name}</p>
<p>Upvotes: {file.upvotes.length}</p>
<p>Downvotes: {file.downvotes.length}</p>
<p><a href="">Download</a></p>
</div>
))
<table>
<thead>
<tr>
<th>Filename</th>
<th>Course</th>
<th>Professor</th>
<th>Semester</th>
<th>Upvotes</th>
<th>Downvotes</th>
<th>Original Author</th>
<th>Download</th>
</tr>
</thead>
<tbody>
{files.map((file) => (
<tr key={file.id}>
<td>{file.filename}</td>
<td>{file.course.name} {file.course.number}</td>
<td>{file.professor.name}</td>
<td>{file.semester.name}</td>
<td>{file.upvotes.length}</td>
<td>{file.downvotes.length}</td>
<td>{file.original_author.username}</td>
<td><a href="">Download</a></td>
</tr>
))}
</tbody>
</table>
) : (
<p>No results found.</p>
)}

View File

@@ -1,3 +1,6 @@
html {
width: 100%;
}
nav {
display: flex;
justify-content: space-between;
@@ -26,10 +29,12 @@ nav div {
justify-content: center; /* If you're centering the content */
height: 100vh; /* Full height */
margin-top: -100px; /* Move up */
width: 100%;
}
body {
background-color: #0C2D48;
font-family: 'Hiragino Sans', 'Hiragino Kaku Gothic ProN', Meiryo, sans-serif;
width: 100%;
}

View File

@@ -0,0 +1,48 @@
h1 {
color: white;
}
.container {
display: flex;
gap: 10rem;
justify-content: space-between;
}
.file-upload-card {
padding: 1em;
background-color: #B1D4E0;
border-radius: 5px;
}
.file-upload-card h1 {
color: black;
}
.file-card {
padding: 1em;
background-color: #2E8BC0;
border-radius: 5px;
display: flex;
flex-direction: row;
}
.submit-button {
padding: 8px 16px;
margin-top: 20px; /* Adds space above the button */
background-color: #2E8BC0; /* Example background color */
color: white; /* Text color */
border: none;
border-radius: 4px;
cursor: pointer;
font-family: 'Hiragino Sans', 'Hiragino Kaku Gothic ProN', Meiryo, sans-serif;
}
th {
text-align: center;
padding: 1rem;
border: 1px solid #FFFFFF;
color: white;
}
td {
text-align: center;
border: 1px solid #FFFFFF;
background-color: #B1D4E0;
color: black;
}

View File

@@ -71,3 +71,18 @@
.searchAgainButton:hover {
background-color: #B1D4E0; /* lighter shade for hover effect */
}
table {
width: 100%;
}
th {
text-align: center;
padding: 1rem;
border: 1px solid #FFFFFF;
}
td {
text-align: center;
border: 1px solid #FFFFFF;
background-color: #B1D4E0;
color: black;
}

View File

@@ -37,3 +37,11 @@ export interface File {
upvotes: number[];
downvotes: number[];
}
enum Status {
HOSTED = "HOSTED",
PRIVATE = 'PRIVATE',
}
export interface RegisteredFile extends File {
status: Status;
}