mirror of
https://github.com/AmanTahiliani/PeerNotes.git
synced 2026-08-07 11:55:12 -04:00
refiling and api call for search (#11)
* refiling and api call for search * updating allowed hosts * update search * proper api call * search page working * navbar * small fixes * fix types --------- Co-authored-by: Sahej Panag <spanag3@gatech.edu> Co-authored-by: afazio1 <alexa.fazio04@gmail.com>
This commit is contained in:
@@ -25,7 +25,9 @@ SECRET_KEY = "django-insecure-rh0s*u8$uugtt10cxbifjriaz%@&p1w!)c2=y^undd2*nx5
|
|||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
|
|
||||||
ALLOWED_HOSTS = ["10.20.4.109", "73.7.29.136", "localhost"]
|
ALLOWED_HOSTS = ["10.20.4.109", "73.7.29.136", "localhost", "127.0.0.1", "http://localhost:5173"]
|
||||||
|
# ALLOWED_HOSTS = []
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
@@ -50,6 +52,7 @@ REST_FRAMEWORK = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
|
"corsheaders.middleware.CorsMiddleware",
|
||||||
"django.middleware.security.SecurityMiddleware",
|
"django.middleware.security.SecurityMiddleware",
|
||||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||||
"django.middleware.common.CommonMiddleware",
|
"django.middleware.common.CommonMiddleware",
|
||||||
@@ -57,7 +60,6 @@ MIDDLEWARE = [
|
|||||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||||
"django.contrib.messages.middleware.MessageMiddleware",
|
"django.contrib.messages.middleware.MessageMiddleware",
|
||||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||||
"corsheaders.middleware.CorsMiddleware",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
ROOT_URLCONF = "peer_notes.urls"
|
ROOT_URLCONF = "peer_notes.urls"
|
||||||
@@ -134,6 +136,7 @@ STATIC_URL = "static/"
|
|||||||
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
||||||
AUTH_USER_MODEL = "api.PeerUser"
|
AUTH_USER_MODEL = "api.PeerUser"
|
||||||
CORS_ALLOW_ALL_ORIGINS = True
|
CORS_ALLOW_ALL_ORIGINS = True
|
||||||
|
CORS_ALLOW_ALL_METHODS = True
|
||||||
# CORS_ALLOWED_ORIGINS = [
|
# CORS_ALLOWED_ORIGINS = [
|
||||||
# "http://localhost:5173"
|
# "http://localhost:5173"
|
||||||
# ]
|
# ]
|
||||||
|
|||||||
@@ -31,12 +31,26 @@ export default function App() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
function Navbar() {
|
function Navbar() {
|
||||||
|
const linkStyle = {
|
||||||
|
textDecoration: 'none',
|
||||||
|
color: 'black',
|
||||||
|
background: '#B1D4E0',
|
||||||
|
padding: '8px 16px',
|
||||||
|
borderRadius: '5px',
|
||||||
|
margin: '0 5px'
|
||||||
|
};
|
||||||
const session = useContext(SessionContext);
|
const session = useContext(SessionContext);
|
||||||
|
const logout = () => {
|
||||||
|
destroySessionCookie();
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
const links = session ? (
|
const links = session ? (
|
||||||
<>
|
<>
|
||||||
<Link to="/">Home</Link>
|
<img src="/PeerNotes.png" alt="PeerNotes Logo" style={{ height: '60px', marginRight: '10px' }} />
|
||||||
<Link to="/login" onClick={destroySessionCookie}>Logout</Link>
|
<Link to="/" style={linkStyle}>Home</Link>
|
||||||
|
<Link to="/search" style={linkStyle}>Search</Link>
|
||||||
|
<a onClick={logout} style={linkStyle}>Logout</a>
|
||||||
</>
|
</>
|
||||||
): (
|
): (
|
||||||
<Link to="/login">Login</Link>
|
<Link to="/login">Login</Link>
|
||||||
|
|||||||
21
frontend/src/assets/file.svg
Normal file
21
frontend/src/assets/file.svg
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="256" height="256" viewBox="0 0 256 256" xml:space="preserve">
|
||||||
|
|
||||||
|
<defs>
|
||||||
|
</defs>
|
||||||
|
<g style="stroke: none; stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: none; fill-rule: nonzero; opacity: 1;" transform="translate(1.4065934065934016 1.4065934065934016) scale(2.81 2.81)" >
|
||||||
|
<path d="M 77.474 17.28 L 61.526 1.332 C 60.668 0.473 59.525 0 58.311 0 H 15.742 c -2.508 0 -4.548 2.04 -4.548 4.548 v 80.904 c 0 2.508 2.04 4.548 4.548 4.548 h 58.516 c 2.508 0 4.549 -2.04 4.549 -4.548 V 20.496 C 78.807 19.281 78.333 18.138 77.474 17.28 z M 61.073 5.121 l 12.611 12.612 H 62.35 c -0.704 0 -1.276 -0.573 -1.276 -1.277 V 5.121 z M 74.258 87 H 15.742 c -0.854 0 -1.548 -0.694 -1.548 -1.548 V 4.548 C 14.194 3.694 14.888 3 15.742 3 h 42.332 v 13.456 c 0 2.358 1.918 4.277 4.276 4.277 h 13.457 v 64.719 C 75.807 86.306 75.112 87 74.258 87 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
|
||||||
|
<path d="M 68.193 33.319 H 41.808 c -0.829 0 -1.5 -0.671 -1.5 -1.5 s 0.671 -1.5 1.5 -1.5 h 26.385 c 0.828 0 1.5 0.671 1.5 1.5 S 69.021 33.319 68.193 33.319 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
|
||||||
|
<path d="M 34.456 33.319 H 21.807 c -0.829 0 -1.5 -0.671 -1.5 -1.5 s 0.671 -1.5 1.5 -1.5 h 12.649 c 0.829 0 1.5 0.671 1.5 1.5 S 35.285 33.319 34.456 33.319 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
|
||||||
|
<linearGradient id="SVGID_1" gradientUnits="userSpaceOnUse" x1="21.8064" y1="19.2332" x2="42.2984" y2="19.2332">
|
||||||
|
<stop offset="0%" style="stop-color:rgb(255,255,255);stop-opacity: 1"/>
|
||||||
|
<stop offset="100%" style="stop-color:rgb(0,0,0);stop-opacity: 1"/>
|
||||||
|
</linearGradient>
|
||||||
|
<line x1="-10.246" y1="0" x2="10.246" y2="0" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: url(#SVGID_1); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) "/>
|
||||||
|
<path d="M 42.298 20.733 H 21.807 c -0.829 0 -1.5 -0.671 -1.5 -1.5 s 0.671 -1.5 1.5 -1.5 h 20.492 c 0.829 0 1.5 0.671 1.5 1.5 S 43.127 20.733 42.298 20.733 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
|
||||||
|
<path d="M 68.193 44.319 H 21.807 c -0.829 0 -1.5 -0.671 -1.5 -1.5 s 0.671 -1.5 1.5 -1.5 h 46.387 c 0.828 0 1.5 0.671 1.5 1.5 S 69.021 44.319 68.193 44.319 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
|
||||||
|
<path d="M 48.191 55.319 H 21.807 c -0.829 0 -1.5 -0.672 -1.5 -1.5 s 0.671 -1.5 1.5 -1.5 h 26.385 c 0.828 0 1.5 0.672 1.5 1.5 S 49.02 55.319 48.191 55.319 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
|
||||||
|
<path d="M 68.193 55.319 H 55.544 c -0.828 0 -1.5 -0.672 -1.5 -1.5 s 0.672 -1.5 1.5 -1.5 h 12.649 c 0.828 0 1.5 0.672 1.5 1.5 S 69.021 55.319 68.193 55.319 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
|
||||||
|
<path d="M 68.193 66.319 H 21.807 c -0.829 0 -1.5 -0.672 -1.5 -1.5 s 0.671 -1.5 1.5 -1.5 h 46.387 c 0.828 0 1.5 0.672 1.5 1.5 S 69.021 66.319 68.193 66.319 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
|
||||||
|
<path d="M 68.193 77.319 H 55.544 c -0.828 0 -1.5 -0.672 -1.5 -1.5 s 0.672 -1.5 1.5 -1.5 h 12.649 c 0.828 0 1.5 0.672 1.5 1.5 S 69.021 77.319 68.193 77.319 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.9 KiB |
@@ -1,78 +0,0 @@
|
|||||||
import React, { useState } from 'react';
|
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
import '../styles/MainScreenWrapper.css';
|
|
||||||
|
|
||||||
interface Filters {
|
|
||||||
professor: string;
|
|
||||||
course: string;
|
|
||||||
topic: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const MainSearch: React.FC = () => {
|
|
||||||
const [filters, setFilters] = useState<Filters>({
|
|
||||||
professor: '',
|
|
||||||
course: '',
|
|
||||||
topic: '',
|
|
||||||
});
|
|
||||||
|
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
const handleChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
|
||||||
const { name, value } = event.target;
|
|
||||||
setFilters((prevFilters: Filters) => ({
|
|
||||||
...prevFilters,
|
|
||||||
[name]: value,
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
|
||||||
event.preventDefault();
|
|
||||||
const input = filters.toString()
|
|
||||||
const searchParams = new URLSearchParams(input)
|
|
||||||
navigate(`/results?${searchParams}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<img
|
|
||||||
src="/PeerNotes.png"
|
|
||||||
alt="PeerNotes Logo"
|
|
||||||
style={{
|
|
||||||
height: "100px"
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<form onSubmit={handleSubmit} className="filter-container">
|
|
||||||
<div className="filter-item">
|
|
||||||
<label htmlFor="professors">Professors:</label>
|
|
||||||
<select id="professors" name="professor" value={filters.professor} onChange={handleChange}>
|
|
||||||
<option value="">Select a Professor</option>
|
|
||||||
{/* temporary options */}
|
|
||||||
<option value="professor1">Professor 1</option>
|
|
||||||
<option value="professor2">Professor 2</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div className="filter-item">
|
|
||||||
<label htmlFor="courses">Course:</label>
|
|
||||||
<select id="courses" name="course" value={filters.course} onChange={handleChange}>
|
|
||||||
<option value="">Select a Course</option>
|
|
||||||
{/* temporary options */}
|
|
||||||
<option value="course1">Course 1</option>
|
|
||||||
<option value="course2">Course 2</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div className="filter-item">
|
|
||||||
<label htmlFor="topics">Topic:</label>
|
|
||||||
<select id="topics" name="topic" value={filters.topic} onChange={handleChange}>
|
|
||||||
<option value="">Select a Topic</option>
|
|
||||||
{/* temporary options */}
|
|
||||||
<option value="topic1">Topic 1</option>
|
|
||||||
<option value="topic2">Topic 2</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<button type="submit" className="submit-button">Submit</button>
|
|
||||||
</form>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default MainSearch;
|
|
||||||
@@ -8,7 +8,7 @@ export const useSessionRedirect = () => {
|
|||||||
// Redirect to home if session is defined
|
// Redirect to home if session is defined
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (session !== undefined) {
|
if (session !== undefined) {
|
||||||
navigate('/');
|
navigate('/search');
|
||||||
}
|
}
|
||||||
}, [session, navigate])
|
}, [session, navigate])
|
||||||
}
|
}
|
||||||
@@ -8,8 +8,8 @@ import {
|
|||||||
} from "react-router-dom";
|
} from "react-router-dom";
|
||||||
import Login from './screens/Login.tsx';
|
import Login from './screens/Login.tsx';
|
||||||
import Signup from './screens/Signup.tsx';
|
import Signup from './screens/Signup.tsx';
|
||||||
import Results from './components/Results.tsx';
|
import Results from './screens/Results.tsx';
|
||||||
import MainSearch from './components/MainSearch.tsx';
|
import MainSearch from './screens/MainSearch.tsx';
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
const router = createBrowserRouter([
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -21,8 +21,15 @@ export default function Login() {
|
|||||||
},
|
},
|
||||||
body: JSON.stringify({ username, password }),
|
body: JSON.stringify({ username, password }),
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(response => response.json())
|
||||||
navigate('/');
|
.then(data => {
|
||||||
|
console.log("Login response data:", data);
|
||||||
|
if (data.token) {
|
||||||
|
// localStorage.setItem('authToken', data.token);
|
||||||
|
navigate('/search');
|
||||||
|
} else {
|
||||||
|
console.error('No token received:', data);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error('Error:', error);
|
console.error('Error:', error);
|
||||||
|
|||||||
148
frontend/src/screens/MainSearch.tsx
Normal file
148
frontend/src/screens/MainSearch.tsx
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import '../styles/MainScreenWrapper.css';
|
||||||
|
import { getSessionCookie } from '../contexts/session';
|
||||||
|
import { Professor, Course, Topic } from "../types/types";
|
||||||
|
|
||||||
|
interface Filters extends Record<string, string> {
|
||||||
|
professor: string;
|
||||||
|
course: string;
|
||||||
|
topic: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MainSearch: React.FC = () => {
|
||||||
|
const [filters, setFilters] = useState<Filters>({
|
||||||
|
professor: '',
|
||||||
|
course: '',
|
||||||
|
topic: '',
|
||||||
|
});
|
||||||
|
const [professors, setProfessors] = useState<Professor[]>([]);
|
||||||
|
const [courses, setCourses] = useState<Course[]>([]);
|
||||||
|
const [topics, setTopics] = useState<Topic[]>([]);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Fetch professors
|
||||||
|
fetchProfessors();
|
||||||
|
// Fetch courses
|
||||||
|
fetchCourses();
|
||||||
|
// Fetch topics
|
||||||
|
fetchTopics();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const getAuthHeaders = () => {
|
||||||
|
const token = getSessionCookie();
|
||||||
|
// const token = localStorage.getItem('authToken');
|
||||||
|
console.log("Using token for API call:", token);
|
||||||
|
return {
|
||||||
|
'Authorization': `Token ${token}`,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchProfessors = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch('http://localhost:8000/api/professors', {
|
||||||
|
method: 'GET',
|
||||||
|
headers: getAuthHeaders(),
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
console.log("Professors fetched:", data);
|
||||||
|
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
console.log("Professors fetched:", data);
|
||||||
|
setProfessors(data);
|
||||||
|
} else {
|
||||||
|
console.error('Data fetched is not an array:', data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching professors:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchCourses = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch('http://localhost:8000/api/courses', {
|
||||||
|
method: 'GET',
|
||||||
|
headers: getAuthHeaders(),
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
setCourses(data);
|
||||||
|
} else {
|
||||||
|
console.error('Data fetched is not an array:', data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching courses:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchTopics = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch('http://localhost:8000/api/topics', {
|
||||||
|
method: 'GET',
|
||||||
|
headers: getAuthHeaders(),
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
setTopics(data);
|
||||||
|
} else {
|
||||||
|
console.error('Data fetched is not an array:', data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching topics:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||||
|
const { name, value } = event.target;
|
||||||
|
setFilters((prevFilters: Filters) => ({
|
||||||
|
...prevFilters,
|
||||||
|
[name]: value,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
event.preventDefault();
|
||||||
|
const searchParams = new URLSearchParams(filters);
|
||||||
|
navigate(`/results?${searchParams}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<img src="/PeerNotes.png" alt="PeerNotes Logo" style={{ height: "100px" }} />
|
||||||
|
<form onSubmit={handleSubmit} className="filter-container">
|
||||||
|
<div className="filter-item">
|
||||||
|
<label htmlFor="professors">Professors:</label>
|
||||||
|
<select id="professors" name="professor" value={filters.professor} onChange={handleChange}>
|
||||||
|
<option value="">Select a Professor</option>
|
||||||
|
{professors.map((professor) => (
|
||||||
|
<option key={professor.id} value={professor.id}>{professor.name}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div className="filter-item">
|
||||||
|
<label htmlFor="courses">Course:</label>
|
||||||
|
<select id="courses" name="course" value={filters.course} onChange={handleChange}>
|
||||||
|
<option value="">Select a Course</option>
|
||||||
|
{courses.map((course) => (
|
||||||
|
<option key={course.id} value={course.id}>{course.name}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div className="filter-item">
|
||||||
|
<label htmlFor="topics">Topic:</label>
|
||||||
|
<select id="topics" name="topic" value={filters.topic} onChange={handleChange}>
|
||||||
|
<option value="">Select a Topic</option>
|
||||||
|
{topics.map((topic) => (
|
||||||
|
<option key={topic.id} value={topic.id}>{topic.name}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button type="submit" className="submit-button">Submit</button>
|
||||||
|
</form>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MainSearch;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { useLocation, useNavigate } from 'react-router-dom';
|
import { useLocation, useNavigate } from 'react-router-dom';
|
||||||
import styles from '../screens/Results.module.css'; // Make sure this path is correct
|
import styles from '../styles/Results.module.css'; // Make sure this path is correct
|
||||||
|
|
||||||
interface File {
|
interface File {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -40,7 +40,7 @@ const Results: React.FC = () => {
|
|||||||
}, [location]);
|
}, [location]);
|
||||||
|
|
||||||
const handleSearchAgain = () => {
|
const handleSearchAgain = () => {
|
||||||
navigate('/');
|
navigate('/search');
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isLoading) return <div>Loading...</div>;
|
if (isLoading) return <div>Loading...</div>;
|
||||||
@@ -1,12 +1,18 @@
|
|||||||
nav {
|
nav {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 1rem 2rem;
|
padding: 1rem 2rem;
|
||||||
border: 3px solid #f1f1f1;
|
margin: 0;
|
||||||
border-radius: 5px;
|
width: 100%;
|
||||||
color: black;
|
background-color: #145DA0;
|
||||||
background-color: white;
|
border: none;
|
||||||
|
border-radius: 0;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
.App {
|
.App {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
15
frontend/src/types/types.ts
Normal file
15
frontend/src/types/types.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
export interface Professor {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
export interface Course {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
number: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Topic {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user