Skip to content
Snippets Groups Projects
Commit 62a4d6f6 authored by Johann Ruiz's avatar Johann Ruiz
Browse files

Pushing up subscription work

parents 25577990 d2a902db
No related branches found
No related tags found
1 merge request!2Subscribing
Pipeline #10802 failed
This commit is part of merge request !2. Comments created here will be created in the context of that merge request.
import AppHeader from "./components/AppHeader"; import AppHeader from "./components/AppHeader";
import AppFooter from "./components/AppFooter"; import AppFooter from "./components/AppFooter";
import WelcomePage from "./pages/WelcomePage.tsx"; import WelcomePage from "./pages/WelcomePage";
import SignUpStudentPage from "./pages/SignUpStudentPage.tsx"; import SignUpStudentPage from "./pages/SignUpStudentPage";
import SignUpProfessorPage from "./pages/SignUpProfessorPage.tsx"; import SignUpProfessorPage from "./pages/SignUpProfessorPage";
import LoginPage from "./pages/LoginPage.tsx"; import LoginPage from "./pages/LoginPage";
import LabsPage from "./pages/LabsPage.tsx"; import LabsPage from "./pages/LabsPage";
import OpeningsPage from "./pages/OpeningsPage.tsx"; import OpeningsPage from "./pages/OpeningsPage";
import DiscussionPage from "./pages/DiscussionPage.tsx"; import DiscussionPage from "./pages/DiscussionPage";
import ProfilePage from "./pages/ProfilePage.tsx"; import ProfilePage from "./pages/ProfilePage";
import './App.css' import './App.css';
import {Route, Routes} from "react-router-dom"; import { Route, Routes } from "react-router-dom";
import { import {
ADMIN_ROLE,
adminPagePath, adminPagePath,
discussionPagePath, discussionPagePath,
labsPagePath, labsPagePath,
loginPagePath, loginPagePath,
openingsPagePath, profilePagePath, signupConfirmationPagePath, openingsPagePath,
PROFESSOR_ROLE,
profilePagePath,
signupConfirmationPagePath,
signUpProfessorPagePath, signUpProfessorPagePath,
signUpStudentPagePath, signUpStudentPagePath,
STUDENT_ROLE,
welcomePagePath welcomePagePath
} from "./utils.ts"; } from "./utils";
import DiscussionItemPage from "./pages/DiscussionItemPage.tsx"; import DiscussionItemPage from "./pages/DiscussionItemPage";
import AdminPage from "./pages/AdminPage.tsx"; import AdminPage from "./pages/AdminPage";
import SignUpConfirmationPage from "./pages/SignUpConfirmationPage.tsx"; import SignUpConfirmationPage from "./pages/SignUpConfirmationPage";
import ProtectedRoute from "./components/ProtectedRoute.tsx";
export default function App() { export default function App() {
return ( return (
<div className="app"> <div className="app">
<AppHeader/> <AppHeader />
<Routes> <Routes>
<Route path={welcomePagePath} element={<WelcomePage/>}/> <Route path={welcomePagePath} element={<WelcomePage />} />
<Route path={signUpStudentPagePath} element={<SignUpStudentPage/>}/> <Route path={signUpStudentPagePath} element={<SignUpStudentPage />} />
<Route path={signUpProfessorPagePath} element={<SignUpProfessorPage/>}/> <Route path={signUpProfessorPagePath} element={<SignUpProfessorPage />} />
<Route path={loginPagePath} element={<LoginPage/>}/> <Route path={loginPagePath} element={<LoginPage />} />
<Route path={signupConfirmationPagePath} element={<SignUpConfirmationPage/>}/> <Route path={signupConfirmationPagePath} element={<SignUpConfirmationPage />} />
<Route path={adminPagePath} element={<AdminPage/>}/> <Route
<Route path={labsPagePath} element={<LabsPage/>}/> path={adminPagePath}
<Route path={`${labsPagePath}/:collegeName`} element={<LabsPage/>}/> element={<ProtectedRoute component={AdminPage} roles={[ADMIN_ROLE]} />}
<Route path={`${labsPagePath}/:collegeName/:majorName`} element={<LabsPage/>}/> />
<Route path={openingsPagePath} element={<OpeningsPage/>}/> <Route
<Route path={discussionPagePath} element={<DiscussionPage/>}/> path={labsPagePath}
<Route path={`${discussionPagePath}/:id`} element={<DiscussionItemPage/>}/> element={<ProtectedRoute component={LabsPage} roles={[STUDENT_ROLE, PROFESSOR_ROLE, ADMIN_ROLE]} />}
<Route path={`${profilePagePath}/:role/:fullName`} element={<ProfilePage/>}/> />
<Route
path={`${labsPagePath}/:collegeName`}
element={<ProtectedRoute component={LabsPage} roles={[STUDENT_ROLE, PROFESSOR_ROLE, ADMIN_ROLE]} />}
/>
<Route
path={`${labsPagePath}/:collegeName/:majorName`}
element={<ProtectedRoute component={LabsPage} roles={[STUDENT_ROLE, PROFESSOR_ROLE, ADMIN_ROLE]} />}
/>
<Route
path={openingsPagePath}
element={<ProtectedRoute component={OpeningsPage} roles={[STUDENT_ROLE, PROFESSOR_ROLE, ADMIN_ROLE]} />}
/>
<Route
path={discussionPagePath}
element={<ProtectedRoute component={DiscussionPage} roles={[STUDENT_ROLE, PROFESSOR_ROLE, ADMIN_ROLE]} />}
/>
<Route
path={`${discussionPagePath}/:id`}
element={<ProtectedRoute component={DiscussionItemPage} roles={[STUDENT_ROLE, PROFESSOR_ROLE, ADMIN_ROLE]} />}
/>
<Route
path={`${profilePagePath}/:role/:fullName`}
element={<ProtectedRoute component={ProfilePage} roles={[STUDENT_ROLE, PROFESSOR_ROLE]} />}
/>
</Routes> </Routes>
<AppFooter/> <AppFooter />
</div> </div>
); );
} }
import {OpeningsItem, ResearchLabItem} from "../types.ts"; import {OpeningsItem} from "../types.ts";
import "./OpeningListItem.css" import "./OpeningListItem.css"
import {fetchResearchLabById} from "../services.ts"; import { useState} from "react";
import {useUserContext} from "../contexts/UserContext.tsx";
import {useEffect, useState} from "react";
import OpeningPopup from "./OpeningPopup.tsx"; import OpeningPopup from "./OpeningPopup.tsx";
export default function OpeningListItem(props: { opening: OpeningsItem }) { export default function OpeningListItem(props: { opening: OpeningsItem }) {
const {token} = useUserContext();
const id = props.opening.id; const id = props.opening.id;
const name = props.opening.name; const name = props.opening.name;
const type = props.opening.type; const type = props.opening.type;
const lab_id = props.opening.labId; const labName = props.opening.labName;
const [lab, setLab] = useState<ResearchLabItem>();
useEffect(() => {
fetchResearchLabById(lab_id, token)
.then((data) => setLab(data))
.catch((error) => console.error(error));
}, [lab_id, token]);
const labName = lab?.name;
const [showPopup, setShowPopup] = useState(false); const [showPopup, setShowPopup] = useState(false);
const togglePopup = () => { const togglePopup = () => {
...@@ -33,7 +22,7 @@ export default function OpeningListItem(props: { opening: OpeningsItem }) { ...@@ -33,7 +22,7 @@ export default function OpeningListItem(props: { opening: OpeningsItem }) {
<p className="opening-type">{type}</p> <p className="opening-type">{type}</p>
</li> </li>
{showPopup && ( {showPopup && (
<OpeningPopup opening={props.opening} onClose={togglePopup} labName={labName}/> <OpeningPopup opening={props.opening} onClose={togglePopup}/>
)} )}
</> </>
); );
......
...@@ -5,12 +5,12 @@ import "./Popup.css" ...@@ -5,12 +5,12 @@ import "./Popup.css"
interface OpeningPopupProps { interface OpeningPopupProps {
opening: OpeningsItem | null; opening: OpeningsItem | null;
labName?: string
onClose: () => void; onClose: () => void;
} }
const OpeningPopup: React.FC<OpeningPopupProps> = ({opening, onClose, labName}) => { const OpeningPopup: React.FC<OpeningPopupProps> = ({opening, onClose}) => {
if (!opening) return null; if (!opening) return null;
console.log(opening)
return ( return (
<div className="popup-info-overlay"> <div className="popup-info-overlay">
...@@ -27,13 +27,18 @@ const OpeningPopup: React.FC<OpeningPopupProps> = ({opening, onClose, labName}) ...@@ -27,13 +27,18 @@ const OpeningPopup: React.FC<OpeningPopupProps> = ({opening, onClose, labName})
</div> </div>
<div> <div>
<h3>Research Lab</h3> <h3>Research Lab</h3>
<p>{labName}</p> <p>{opening.labName}</p>
</div> </div>
<div> <div>
<h3>Posted By</h3>
<p>{opening.professor.firstName} {opening.professor.lastName}</p>
<p>{opening.professor.email}</p>
</div>
<section>
<Link to={`${opening.url}`} target="_blank"> <Link to={`${opening.url}`} target="_blank">
<button className="primary-button">Go to Application</button> <button className="primary-button">Go to Application</button>
</Link> </Link>
</div> </section>
</div> </div>
</div> </div>
); );
......
import {number, object, string, ValidationError} from "yup"; import {object, string, ValidationError} from "yup";
import {useResearchLabContext} from "../contexts/ResearchLabContext.tsx"; import {useResearchLabContext} from "../contexts/ResearchLabContext.tsx";
import {Form, Formik, FormikHelpers} from "formik"; import {Form, Formik, FormikHelpers} from "formik";
import TextInput from "./form/TextInput.tsx"; import TextInput from "./form/TextInput.tsx";
...@@ -6,6 +6,8 @@ import {SelectInput} from "./form/SelectInput.tsx"; ...@@ -6,6 +6,8 @@ import {SelectInput} from "./form/SelectInput.tsx";
import './PopupForm.css'; import './PopupForm.css';
import React from "react"; import React from "react";
import {RadioInput} from "./form/RadioInput.tsx"; import {RadioInput} from "./form/RadioInput.tsx";
import {useProfessorUserContext} from "../contexts/ProfessorUserContext.tsx";
import {ProfessorUser} from "../types.ts";
interface FormPopupProps { interface FormPopupProps {
onClose: () => void; onClose: () => void;
...@@ -17,7 +19,8 @@ interface FormData { ...@@ -17,7 +19,8 @@ interface FormData {
description: string; description: string;
type: 'Credit' | 'Paid' | 'Volunteer'; type: 'Credit' | 'Paid' | 'Volunteer';
url: string; url: string;
labId: number; labName: string;
professor: ProfessorUser;
} }
const initialValues: FormData = { const initialValues: FormData = {
...@@ -25,29 +28,33 @@ const initialValues: FormData = { ...@@ -25,29 +28,33 @@ const initialValues: FormData = {
description: '', description: '',
type: 'Credit', type: 'Credit',
url: '', url: '',
labId: 1 labName: '',
professor: {} as ProfessorUser
} }
const validationSchema = object({
name: string()
.required("The name of the Opening is required."),
description: string()
.required("The description of the Opening is required."),
type: string()
.oneOf(['Credit', 'Paid', 'Volunteer'], 'Invalid Opening Type')
.required("The Opening type is required."),
url: string()
.required("The URL to the opening is required.")
.url("Please enter a valid URL."),
labId: number()
.integer("Lab ID must be an integer")
.required("The name of the research lab this opening is for is required.")
})
const OpeningsPopupForm: React.FC<FormPopupProps> = ({onClose, onCreate}) => { const OpeningsPopupForm: React.FC<FormPopupProps> = ({onClose, onCreate}) => {
const {professor} = useProfessorUserContext();
const {labs} = useResearchLabContext(); const {labs} = useResearchLabContext();
const validationSchema = object({
name: string()
.required("The name of the Opening is required."),
description: string()
.required("The description of the Opening is required."),
type: string()
.oneOf(['Credit', 'Paid', 'Volunteer'], 'Invalid Opening Type')
.required("The Opening type is required."),
url: string()
.required("The URL to the opening is required.")
.url("Please enter a valid URL."),
labName: string()
.required("You can only add an opening for your lab!")
.test('is-professor-lab', 'You can only add an opening for your lab!', function (value) {
return value === professor?.lab?.name;
})
})
const handleSubmit = async ( const handleSubmit = async (
values: FormData, values: FormData,
actions: FormikHelpers<FormData> actions: FormikHelpers<FormData>
...@@ -56,7 +63,20 @@ const OpeningsPopupForm: React.FC<FormPopupProps> = ({onClose, onCreate}) => { ...@@ -56,7 +63,20 @@ const OpeningsPopupForm: React.FC<FormPopupProps> = ({onClose, onCreate}) => {
if (result instanceof ValidationError) { if (result instanceof ValidationError) {
actions.setSubmitting(false); actions.setSubmitting(false);
} else { } else {
onCreate(values); const selectedLab = labs.find(lab => lab.name === values.labName)
if (professor) {
const sendingData = {
name: values.name,
description: values.description,
type: values.type,
url: values.url,
labName: selectedLab ? selectedLab.name : '',
professor: professor
}
onCreate(sendingData);
} else {
console.log("professor is null")
}
} }
} }
...@@ -84,8 +104,7 @@ const OpeningsPopupForm: React.FC<FormPopupProps> = ({onClose, onCreate}) => { ...@@ -84,8 +104,7 @@ const OpeningsPopupForm: React.FC<FormPopupProps> = ({onClose, onCreate}) => {
</div> </div>
<TextInput label="URL to Apply" name="url"/> <TextInput label="URL to Apply" name="url"/>
<SelectInput label="Research Lab:" name="labId" <SelectInput label="Research Lab:" name="labName" options={labs} valueKey="name"></SelectInput>
options={labs}></SelectInput>
<button type="submit" className="primary-button">CREATE</button> <button type="submit" className="primary-button">CREATE</button>
</Form> </Form>
</Formik> </Formik>
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
.popup-info-content h2 { .popup-info-content h2 {
margin-top: 0; margin-top: 0;
margin-bottom: 10px;
} }
.popup-info-content .close { .popup-info-content .close {
...@@ -44,7 +45,7 @@ ...@@ -44,7 +45,7 @@
text-decoration: underline; text-decoration: underline;
} }
.popup-info-content div a button { .popup-info-content section {
padding-top: 10px;
} }
...@@ -77,7 +77,8 @@ ...@@ -77,7 +77,8 @@
height: 100px; height: 100px;
} }
.field-error { .field-error,
.checkout-field-error {
color: var(--error-text-color); color: var(--error-text-color);
font-size: smaller; font-size: smaller;
font-style: italic; font-style: italic;
......
import React from 'react';
import {Navigate} from 'react-router-dom';
import {useUserContext} from "../contexts/UserContext.tsx";
import {loginPagePath, NO_TOKEN} from "../utils.ts";
interface ProtectedRouteProps {
component: React.ComponentType<any>;
roles: string[];
}
const ProtectedRoute: React.FC<ProtectedRouteProps> = ({component: Component, roles}) => {
const {token, role} = useUserContext();
if (token === NO_TOKEN || !roles.includes(role)) {
return <Navigate to={loginPagePath}/>;
}
return <Component/>;
};
export default ProtectedRoute;
// Update SelectInput component in `src/components/form/SelectInput.tsx`
import {Field, ErrorMessage} from 'formik'; import {Field, ErrorMessage} from 'formik';
import React from 'react'; import React from 'react';
import {CollegeItem, MajorItem, ResearchLabItem} from "../../types.ts"; import {CollegeItem, MajorItem, ResearchLabItem} from "../../types.ts";
...@@ -6,15 +7,16 @@ interface SelectInputProps { ...@@ -6,15 +7,16 @@ interface SelectInputProps {
label: string; label: string;
name: string; name: string;
options: MajorItem[] | CollegeItem[] | ResearchLabItem[]; options: MajorItem[] | CollegeItem[] | ResearchLabItem[];
valueKey?: 'id' | 'name';
} }
export const SelectInput: React.FC<SelectInputProps> = ({label, name, options}) => { export const SelectInput: React.FC<SelectInputProps> = ({label, name, options, valueKey = 'id'}) => {
return ( return (
<div> <div>
<label htmlFor={name}>{label}</label> <label htmlFor={name}>{label}</label>
<Field as="select" name={name}> <Field as="select" name={name}>
{options.map((option: MajorItem | CollegeItem | ResearchLabItem) => ( {options.map((option: MajorItem | CollegeItem | ResearchLabItem) => (
<option key={option.id} value={option.id}> <option key={option.id} value={valueKey === 'id' ? option.id : option.name}>
{option.name} {option.name}
</option> </option>
))} ))}
...@@ -22,4 +24,4 @@ export const SelectInput: React.FC<SelectInputProps> = ({label, name, options}) ...@@ -22,4 +24,4 @@ export const SelectInput: React.FC<SelectInputProps> = ({label, name, options})
<ErrorMessage className="checkout-field-error" name={name} component="span"/> <ErrorMessage className="checkout-field-error" name={name} component="span"/>
</div> </div>
); );
}; };
\ No newline at end of file
...@@ -6,6 +6,7 @@ import axios from "axios"; ...@@ -6,6 +6,7 @@ import axios from "axios";
import OpeningsPopupForm from "../components/OpeningsPopupForm.tsx"; import OpeningsPopupForm from "../components/OpeningsPopupForm.tsx";
import OpeningsList from "../components/OpeningsList.tsx"; import OpeningsList from "../components/OpeningsList.tsx";
import {useOpeningsContext} from "../contexts/OpeningsContext.tsx"; import {useOpeningsContext} from "../contexts/OpeningsContext.tsx";
import {ProfessorUser} from "../types.ts";
export default function OpeningsPage() { export default function OpeningsPage() {
...@@ -23,8 +24,10 @@ export default function OpeningsPage() { ...@@ -23,8 +24,10 @@ export default function OpeningsPage() {
description: string; description: string;
type: string; type: string;
url: string; url: string;
labId: number; labName: string;
professor: ProfessorUser;
}) => { }) => {
console.log(formData);
axios.post(`${openingsAPI}`, formData, { axios.post(`${openingsAPI}`, formData, {
headers: { headers: {
'Authorization': `Bearer ${token}`, 'Authorization': `Bearer ${token}`,
......
...@@ -41,4 +41,70 @@ ...@@ -41,4 +41,70 @@
.profile-section ul li { .profile-section ul li {
margin: 5px 0; margin: 5px 0;
font-size: var(--default-font-size); font-size: var(--default-font-size);
} }
\ No newline at end of file
/* Additional Styles */
/* Ensure input and textarea text is black */
.profile-page .text-input {
color: black; /* Set text color to black */
background-color: white; /* Ensure background is white for better contrast */
border: 1px solid #ccc; /* Add border for better visibility */
padding: 8px; /* Add padding for better user experience */
font-size: 14px; /* Set font size */
width: 100%; /* Make input take full width */
box-sizing: border-box; /* Include padding in width */
}
/* Button styling */
.profile-page .call-to-action-button {
background-color: var(--primary-color); /* Use primary color for button */
color: white; /* Button text color */
border: none; /* Remove border */
padding: 10px 20px; /* Add padding */
font-size: 16px; /* Set font size */
cursor: pointer; /* Change cursor to pointer on hover */
margin-top: 10px; /* Add margin to separate from other elements */
}
/* Button hover effect */
.profile-page .call-to-action-button:hover {
background-color: darken(var(--primary-color), 10%); /* Darken button background on hover */
}
/* Label styling */
.profile-page .edit-section label {
display: block;
margin-bottom: 10px;
}
/* Input and textarea styling */
.profile-page .edit-section input,
.profile-page .edit-section textarea {
width: 100%; /* Full width input */
margin-top: 5px; /* Space between label and input */
margin-bottom: 15px; /* Space between inputs */
box-sizing: border-box; /* Ensure padding is included in width calculation */
}
/* Textarea specific styling */
.profile-page .edit-section textarea {
height: 100px; /* Set height for textarea */
resize: vertical; /* Allow vertical resizing */
}
/* Profile header styling */
.profile-header {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 1em;
}
/* Edit section styling */
.profile-page .edit-section {
display: flex;
flex-direction: column;
width: 100%;
max-width: 600px;
}
import './ProfilePage.css' import './ProfilePage.css';
import {useParams} from "react-router-dom"; import { useParams } from "react-router-dom";
import {NO_TOKEN, PROFESSOR_ROLE, STUDENT_ROLE} from "../utils.ts"; import { NO_TOKEN, PROFESSOR_ROLE, STUDENT_ROLE } from "../utils.ts";
import {useUserContext} from "../contexts/UserContext.tsx"; import { useUserContext } from "../contexts/UserContext.tsx";
import {useEffect, useState} from "react"; import { useEffect, useState } from "react";
import {fetchProfessorByFullName, fetchStudentByFullName} from "../services.ts"; import { fetchProfessorByFullName, fetchStudentByFullName, updateProfessorProfile, updateStudentProfile } from "../services.ts";
import {MajorItem, ResearchLabItem} from "../types.ts"; import { MajorItem, ResearchLabItem } from "../types.ts";
interface ProfileUser { interface ProfileUser {
id: number; id: number;
...@@ -37,32 +37,62 @@ const situationLabelMap: { [key: string]: string } = { ...@@ -37,32 +37,62 @@ const situationLabelMap: { [key: string]: string } = {
}; };
export default function ProfilePage() { export default function ProfilePage() {
const {role, fullName} = useParams(); const { role, fullName } = useParams();
const {token} = useUserContext(); const { token } = useUserContext();
const [profileUser, setProfileUser] = useState<ProfileUser>(); const [profileUser, setProfileUser] = useState<ProfileUser>();
const [isEditing, setIsEditing] = useState(false);
const [editData, setEditData] = useState<Partial<ProfileUser>>({});
useEffect(() => { useEffect(() => {
const fetchProfileDetails = async () => { const fetchProfileDetails = async () => {
if (token !== NO_TOKEN) { if (token !== NO_TOKEN && fullName) {
if (fullName) { try {
try { if (role === PROFESSOR_ROLE) {
if (role === PROFESSOR_ROLE) { const result = await fetchProfessorByFullName(fullName, token);
const result = await fetchProfessorByFullName(fullName, token); setProfileUser(result);
setProfileUser(result); } else if (role === STUDENT_ROLE) {
} const result = await fetchStudentByFullName(fullName, token);
if (role === STUDENT_ROLE) { setProfileUser(result);
const result = await fetchStudentByFullName(fullName, token);
setProfileUser(result);
}
} catch (error) {
console.error("Failed to load user with name: " + fullName, error);
} }
} catch (error) {
console.error("Failed to load user with name: " + fullName, error);
} }
} }
} };
fetchProfileDetails(); fetchProfileDetails();
}, [fullName, role, token]); }, [fullName, role, token]);
const handleEditChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const { name, value } = e.target;
if (name.startsWith("lab.")) {
const labName = name.split(".")[1];
setEditData(prevData => ({
...prevData,
lab: {
...prevData.lab,
[labName]: value
} as ResearchLabItem
}));
} else {
setEditData({ ...editData, [name]: value });
}
};
const handleEditSave = async () => {
try {
if (role === PROFESSOR_ROLE && profileUser?.id && editData.lab) {
await updateProfessorProfile(profileUser.id, { lab: editData.lab }, token);
setProfileUser(prev => prev ? ({ ...prev, lab: { ...prev.lab, ...editData.lab } }) : undefined);
} else if (role === STUDENT_ROLE && profileUser?.id) {
await updateStudentProfile(profileUser.id, editData, token);
setProfileUser(prev => prev ? ({ ...prev, ...editData }) : undefined);
}
setIsEditing(false);
} catch (error) {
console.error("Failed to update profile", error);
}
};
const displayStudentYear = profileUser?.year ? studentTypeMap[profileUser.year] : undefined; const displayStudentYear = profileUser?.year ? studentTypeMap[profileUser.year] : undefined;
const aboutMeArray = profileUser?.aboutMe ? profileUser.aboutMe.split(', ') : []; const aboutMeArray = profileUser?.aboutMe ? profileUser.aboutMe.split(', ') : [];
const selectedLabels = aboutMeArray.map(value => situationLabelMap[value]); const selectedLabels = aboutMeArray.map(value => situationLabelMap[value]);
...@@ -73,66 +103,132 @@ export default function ProfilePage() { ...@@ -73,66 +103,132 @@ export default function ProfilePage() {
<> <>
<div className='profile-header'> <div className='profile-header'>
<h1><span>{profileUser.firstName}</span> <span>{profileUser.lastName}</span></h1> <h1><span>{profileUser.firstName}</span> <span>{profileUser.lastName}</span></h1>
<button className="call-to-action-button" onClick={() => setIsEditing(!isEditing)}>
{isEditing ? 'Cancel' : 'Edit Profile'}
</button>
</div> </div>
<div className='profile-section'> {isEditing ? (
<h2>Contact Information</h2> <div className='edit-section'>
<p><span className="attribute">Email:</span> {profileUser.email}</p> {role === PROFESSOR_ROLE && (
</div> <>
{role === STUDENT_ROLE && ( <h2>Edit Lab Information</h2>
<> <label>
<div className='profile-section'> Lab Name:
<h2>Academic Information</h2> <input
<p><span className="attribute"></span>{displayStudentYear}</p> className="text-input"
<p><span className="attribute">College of </span> {profileUser.major?.college}</p> type="text"
<p><span className="attribute">Majoring in </span> {profileUser.major?.name}</p> name="lab.name"
</div> value={editData.lab?.name || profileUser.lab?.name || ''}
<div className='profile-section'> onChange={handleEditChange}
/>
<h2>Current Situation</h2> </label>
<ul> <label>
{selectedLabels.map((label, index) => ( URL:
<li key={index}>{label}</li> <input
))} className="text-input"
</ul> type="text"
</div> name="lab.url"
</> value={editData.lab?.url || profileUser.lab?.url || ''}
)} onChange={handleEditChange}
{role === STUDENT_ROLE && profileUser.subscriptions && ( />
</label>
<label>
Description:
<textarea
className="text-input"
name="lab.description"
value={editData.lab?.description || profileUser.lab?.description || ''}
onChange={handleEditChange}
/>
</label>
</>
)}
{role === STUDENT_ROLE && (
<>
<h2>Edit Academic Information</h2>
<label>
Year:
<input
className="text-input"
type="text"
name="year"
value={editData.year || profileUser.year || ''}
onChange={handleEditChange}
/>
</label>
<label>
About Me:
<textarea
className="text-input"
name="aboutMe"
value={editData.aboutMe || profileUser.aboutMe || ''}
onChange={handleEditChange}
/>
</label>
</>
)}
<button className="call-to-action-button" onClick={handleEditSave}>Save</button>
</div>
) : (
<> <>
<div className='profile-section'> <div className='profile-section'>
<h2>Subscriptions</h2> <h2>Contact Information</h2>
<ul> <p><span className="attribute">Email:</span> {profileUser.email}</p>
{profileUser.subscriptions.map((sub, index) => (
<li key={index}>{sub}</li>
))}
</ul>
</div> </div>
{role === STUDENT_ROLE && (
<>
<div className='profile-section'>
<h2>Academic Information</h2>
<p>{displayStudentYear}</p>
<p><span className="attribute">College of </span> {profileUser.major?.college}</p>
<p><span className="attribute">Majoring in </span> {profileUser.major?.name}</p>
</div>
<div className='profile-section'>
<h2>Current Situation</h2>
<ul>
{selectedLabels.map((label, index) => (
<li key={index}>{label}</li>
))}
</ul>
</div>
</>
)}
{role === STUDENT_ROLE && profileUser.subscriptions && (
<>
<div className='profile-section'>
<h2>Subscriptions</h2>
<ul>
{profileUser.subscriptions.map((sub, index) => (
<li key={index}>{sub}</li>
))}
</ul>
</div>
</>
)}
{role === PROFESSOR_ROLE && (
<div className='profile-section'>
<h2>Academic Information</h2>
<p><span className="attribute">Professor</span></p>
<p><span className="attribute">College of</span> {profileUser.college}</p>
</div>
)}
{(role === PROFESSOR_ROLE && profileUser.lab) && (
<div className='profile-section'>
<h2>Research Lab Information</h2>
<p><span className="attribute">Lab Name:</span> {profileUser.lab?.name}</p>
<p><span className="attribute">College:</span> {profileUser.lab?.major.college}</p>
<p><span className="attribute">Major:</span> {profileUser.lab?.major.name}</p>
<p><span className="attribute">Principal Investigator:</span> {profileUser.lab?.principleInvestigator}</p>
<p><span className="attribute">URL:</span> {profileUser.lab?.url}</p>
<p><span className="attribute">Description:</span> {profileUser.lab?.description}</p>
</div>
)}
</> </>
)} )}
{role === PROFESSOR_ROLE && (
<div className='profile-section'>
<h2>Academic Information</h2>
<p><span className="attribute"></span>Professor</p>
<p><span className="attribute">College of</span> {profileUser.college}</p>
</div>
)}
{(role === PROFESSOR_ROLE && profileUser.lab) && (
<div className='profile-section'>
<h2>Research Lab Information</h2>
<p><span className="attribute">Lab Name:</span> {profileUser.lab?.name}</p>
<p><span className="attribute">College:</span> {profileUser.lab?.major.college}</p>
<p><span className="attribute">Major:</span> {profileUser.lab?.major.name}</p>
<p><span
className="attribute">Principle Investigator:</span> {profileUser.lab?.principleInvestigator}
</p>
<p><span className="attribute">URL:</span> {profileUser.lab?.url}</p>
<p><span className="attribute">Description:</span> {profileUser.lab?.description}</p>
</div>
)}
</> </>
) : ( ) : (
<p>Loading...</p> <p>Loading...</p>
)} )}
</div> </div>
); );
} }
\ No newline at end of file
...@@ -214,4 +214,24 @@ export async function signUp(signUpForm: SignupForm): Promise<SignUpResponse> { ...@@ -214,4 +214,24 @@ export async function signUp(signUpForm: SignupForm): Promise<SignUpResponse> {
throw new Error("An unexpected error occurred"); throw new Error("An unexpected error occurred");
} }
} }
} }
\ No newline at end of file
export const updateProfessorProfile = async (id: number, data: Partial<ProfessorUser>, token: string) => {
const response = await axios.put(`${professorsAPI}/${id}`, data, {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
}
});
return response.data as ProfessorUser;
};
export const updateStudentProfile = async (id: number, data: Partial<StudentUser>, token: string) => {
const response = await axios.put(`${studentsAPI}/${id}`, data, {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
}
});
return response.data as StudentUser;
};
\ No newline at end of file
...@@ -34,7 +34,8 @@ export interface OpeningsItem { ...@@ -34,7 +34,8 @@ export interface OpeningsItem {
description: string; description: string;
type: string; type: string;
url: string; url: string;
labId: number; labName: string;
professor: ProfessorUser;
} }
export interface CollegeItem { export interface CollegeItem {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment