diff --git a/src/App.tsx b/src/App.tsx index 8100ae4f7e522701721c79c0904368a474bfe45c..89c837aa209ad754b06a2a1e7d7bc1ba9accdc78 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,49 +1,80 @@ import AppHeader from "./components/AppHeader"; import AppFooter from "./components/AppFooter"; -import WelcomePage from "./pages/WelcomePage.tsx"; -import SignUpStudentPage from "./pages/SignUpStudentPage.tsx"; -import SignUpProfessorPage from "./pages/SignUpProfessorPage.tsx"; -import LoginPage from "./pages/LoginPage.tsx"; -import LabsPage from "./pages/LabsPage.tsx"; -import OpeningsPage from "./pages/OpeningsPage.tsx"; -import DiscussionPage from "./pages/DiscussionPage.tsx"; -import ProfilePage from "./pages/ProfilePage.tsx"; -import './App.css' -import {Route, Routes} from "react-router-dom"; +import WelcomePage from "./pages/WelcomePage"; +import SignUpStudentPage from "./pages/SignUpStudentPage"; +import SignUpProfessorPage from "./pages/SignUpProfessorPage"; +import LoginPage from "./pages/LoginPage"; +import LabsPage from "./pages/LabsPage"; +import OpeningsPage from "./pages/OpeningsPage"; +import DiscussionPage from "./pages/DiscussionPage"; +import ProfilePage from "./pages/ProfilePage"; +import './App.css'; +import { Route, Routes } from "react-router-dom"; import { + ADMIN_ROLE, adminPagePath, discussionPagePath, labsPagePath, loginPagePath, - openingsPagePath, profilePagePath, signupConfirmationPagePath, + openingsPagePath, + PROFESSOR_ROLE, + profilePagePath, + signupConfirmationPagePath, signUpProfessorPagePath, signUpStudentPagePath, + STUDENT_ROLE, welcomePagePath -} from "./utils.ts"; -import DiscussionItemPage from "./pages/DiscussionItemPage.tsx"; -import AdminPage from "./pages/AdminPage.tsx"; -import SignUpConfirmationPage from "./pages/SignUpConfirmationPage.tsx"; +} from "./utils"; +import DiscussionItemPage from "./pages/DiscussionItemPage"; +import AdminPage from "./pages/AdminPage"; +import SignUpConfirmationPage from "./pages/SignUpConfirmationPage"; +import ProtectedRoute from "./components/ProtectedRoute.tsx"; + export default function App() { return ( <div className="app"> - <AppHeader/> + <AppHeader /> <Routes> - <Route path={welcomePagePath} element={<WelcomePage/>}/> - <Route path={signUpStudentPagePath} element={<SignUpStudentPage/>}/> - <Route path={signUpProfessorPagePath} element={<SignUpProfessorPage/>}/> - <Route path={loginPagePath} element={<LoginPage/>}/> - <Route path={signupConfirmationPagePath} element={<SignUpConfirmationPage/>}/> - <Route path={adminPagePath} element={<AdminPage/>}/> - <Route path={labsPagePath} element={<LabsPage/>}/> - <Route path={`${labsPagePath}/:collegeName`} element={<LabsPage/>}/> - <Route path={`${labsPagePath}/:collegeName/:majorName`} element={<LabsPage/>}/> - <Route path={openingsPagePath} element={<OpeningsPage/>}/> - <Route path={discussionPagePath} element={<DiscussionPage/>}/> - <Route path={`${discussionPagePath}/:id`} element={<DiscussionItemPage/>}/> - <Route path={`${profilePagePath}/:role/:fullName`} element={<ProfilePage/>}/> + <Route path={welcomePagePath} element={<WelcomePage />} /> + <Route path={signUpStudentPagePath} element={<SignUpStudentPage />} /> + <Route path={signUpProfessorPagePath} element={<SignUpProfessorPage />} /> + <Route path={loginPagePath} element={<LoginPage />} /> + <Route path={signupConfirmationPagePath} element={<SignUpConfirmationPage />} /> + <Route + path={adminPagePath} + element={<ProtectedRoute component={AdminPage} roles={[ADMIN_ROLE]} />} + /> + <Route + path={labsPagePath} + element={<ProtectedRoute component={LabsPage} roles={[STUDENT_ROLE, PROFESSOR_ROLE, ADMIN_ROLE]} />} + /> + <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> - <AppFooter/> + <AppFooter /> </div> ); } diff --git a/src/components/OpeningListItem.tsx b/src/components/OpeningListItem.tsx index 32d8b962d881f837211073a93bf1e7e971f45cb3..8104dda9f7b2912bf63f7220aaacb9b2fc558ed1 100644 --- a/src/components/OpeningListItem.tsx +++ b/src/components/OpeningListItem.tsx @@ -1,24 +1,13 @@ -import {OpeningsItem, ResearchLabItem} from "../types.ts"; +import {OpeningsItem} from "../types.ts"; import "./OpeningListItem.css" -import {fetchResearchLabById} from "../services.ts"; -import {useUserContext} from "../contexts/UserContext.tsx"; -import {useEffect, useState} from "react"; +import { useState} from "react"; import OpeningPopup from "./OpeningPopup.tsx"; export default function OpeningListItem(props: { opening: OpeningsItem }) { - const {token} = useUserContext(); const id = props.opening.id; const name = props.opening.name; const type = props.opening.type; - const lab_id = props.opening.labId; - - 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 labName = props.opening.labName; const [showPopup, setShowPopup] = useState(false); const togglePopup = () => { @@ -33,7 +22,7 @@ export default function OpeningListItem(props: { opening: OpeningsItem }) { <p className="opening-type">{type}</p> </li> {showPopup && ( - <OpeningPopup opening={props.opening} onClose={togglePopup} labName={labName}/> + <OpeningPopup opening={props.opening} onClose={togglePopup}/> )} </> ); diff --git a/src/components/OpeningPopup.tsx b/src/components/OpeningPopup.tsx index 20e5e8d5dd9a5d403a836a1214699385a01afa52..d79dce136bb38026977f1651c59fe0678be1586a 100644 --- a/src/components/OpeningPopup.tsx +++ b/src/components/OpeningPopup.tsx @@ -5,12 +5,12 @@ import "./Popup.css" interface OpeningPopupProps { opening: OpeningsItem | null; - labName?: string onClose: () => void; } -const OpeningPopup: React.FC<OpeningPopupProps> = ({opening, onClose, labName}) => { +const OpeningPopup: React.FC<OpeningPopupProps> = ({opening, onClose}) => { if (!opening) return null; + console.log(opening) return ( <div className="popup-info-overlay"> @@ -27,13 +27,18 @@ const OpeningPopup: React.FC<OpeningPopupProps> = ({opening, onClose, labName}) </div> <div> <h3>Research Lab</h3> - <p>{labName}</p> + <p>{opening.labName}</p> </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"> <button className="primary-button">Go to Application</button> </Link> - </div> + </section> </div> </div> ); diff --git a/src/components/OpeningsPopupForm.tsx b/src/components/OpeningsPopupForm.tsx index c84c777d200a878ecda10305813d7d7509de32c3..5cb93d2f070e4b0ea0ceef711f15a989aff8160d 100644 --- a/src/components/OpeningsPopupForm.tsx +++ b/src/components/OpeningsPopupForm.tsx @@ -1,4 +1,4 @@ -import {number, object, string, ValidationError} from "yup"; +import {object, string, ValidationError} from "yup"; import {useResearchLabContext} from "../contexts/ResearchLabContext.tsx"; import {Form, Formik, FormikHelpers} from "formik"; import TextInput from "./form/TextInput.tsx"; @@ -6,6 +6,8 @@ import {SelectInput} from "./form/SelectInput.tsx"; import './PopupForm.css'; import React from "react"; import {RadioInput} from "./form/RadioInput.tsx"; +import {useProfessorUserContext} from "../contexts/ProfessorUserContext.tsx"; +import {ProfessorUser} from "../types.ts"; interface FormPopupProps { onClose: () => void; @@ -17,7 +19,8 @@ interface FormData { description: string; type: 'Credit' | 'Paid' | 'Volunteer'; url: string; - labId: number; + labName: string; + professor: ProfessorUser; } const initialValues: FormData = { @@ -25,29 +28,33 @@ const initialValues: FormData = { description: '', type: 'Credit', 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 {professor} = useProfessorUserContext(); 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 ( values: FormData, actions: FormikHelpers<FormData> @@ -56,7 +63,20 @@ const OpeningsPopupForm: React.FC<FormPopupProps> = ({onClose, onCreate}) => { if (result instanceof ValidationError) { actions.setSubmitting(false); } 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}) => { </div> <TextInput label="URL to Apply" name="url"/> - <SelectInput label="Research Lab:" name="labId" - options={labs}></SelectInput> + <SelectInput label="Research Lab:" name="labName" options={labs} valueKey="name"></SelectInput> <button type="submit" className="primary-button">CREATE</button> </Form> </Formik> diff --git a/src/components/Popup.css b/src/components/Popup.css index cba594e76355f88a17f7bb96fe40b936290dc54f..7ac5728a6bfeaa1c7a01c73ad6078679fa811444 100644 --- a/src/components/Popup.css +++ b/src/components/Popup.css @@ -23,6 +23,7 @@ .popup-info-content h2 { margin-top: 0; + margin-bottom: 10px; } .popup-info-content .close { @@ -44,7 +45,7 @@ text-decoration: underline; } -.popup-info-content div a button { - +.popup-info-content section { + padding-top: 10px; } diff --git a/src/components/PopupForm.css b/src/components/PopupForm.css index d8a26d448f1a885448a1884681dab3a129bcbab9..c80e3217073e7cb205233717b6c63a8f41e41ddf 100644 --- a/src/components/PopupForm.css +++ b/src/components/PopupForm.css @@ -77,7 +77,8 @@ height: 100px; } -.field-error { +.field-error, +.checkout-field-error { color: var(--error-text-color); font-size: smaller; font-style: italic; diff --git a/src/components/ProtectedRoute.tsx b/src/components/ProtectedRoute.tsx new file mode 100644 index 0000000000000000000000000000000000000000..eb97b26efe03cf797f661af431adfbd601c174b8 --- /dev/null +++ b/src/components/ProtectedRoute.tsx @@ -0,0 +1,21 @@ +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; diff --git a/src/components/form/SelectInput.tsx b/src/components/form/SelectInput.tsx index 05df2c705d137533664eab9e1155ac9ed7b21900..479a49362336153425d2df9c9b99d9fa17ad1816 100644 --- a/src/components/form/SelectInput.tsx +++ b/src/components/form/SelectInput.tsx @@ -1,3 +1,4 @@ +// Update SelectInput component in `src/components/form/SelectInput.tsx` import {Field, ErrorMessage} from 'formik'; import React from 'react'; import {CollegeItem, MajorItem, ResearchLabItem} from "../../types.ts"; @@ -6,15 +7,16 @@ interface SelectInputProps { label: string; name: string; 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 ( <div> <label htmlFor={name}>{label}</label> <Field as="select" name={name}> {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> ))} @@ -22,4 +24,4 @@ export const SelectInput: React.FC<SelectInputProps> = ({label, name, options}) <ErrorMessage className="checkout-field-error" name={name} component="span"/> </div> ); -}; +}; \ No newline at end of file diff --git a/src/pages/OpeningsPage.tsx b/src/pages/OpeningsPage.tsx index 5db0c459e73959231e137624a5821924e82208d4..5f639db6b113e2c23622249e5705296be928991b 100644 --- a/src/pages/OpeningsPage.tsx +++ b/src/pages/OpeningsPage.tsx @@ -6,6 +6,7 @@ import axios from "axios"; import OpeningsPopupForm from "../components/OpeningsPopupForm.tsx"; import OpeningsList from "../components/OpeningsList.tsx"; import {useOpeningsContext} from "../contexts/OpeningsContext.tsx"; +import {ProfessorUser} from "../types.ts"; export default function OpeningsPage() { @@ -23,8 +24,10 @@ export default function OpeningsPage() { description: string; type: string; url: string; - labId: number; + labName: string; + professor: ProfessorUser; }) => { + console.log(formData); axios.post(`${openingsAPI}`, formData, { headers: { 'Authorization': `Bearer ${token}`, diff --git a/src/pages/ProfilePage.css b/src/pages/ProfilePage.css index b02640d28dcd715a371764a9bcd112ffd310289f..afbb90c8cdf84c2322525679c1abc97114b03c03 100644 --- a/src/pages/ProfilePage.css +++ b/src/pages/ProfilePage.css @@ -41,4 +41,70 @@ .profile-section ul li { margin: 5px 0; 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; +} diff --git a/src/pages/ProfilePage.tsx b/src/pages/ProfilePage.tsx index 02fe506c450cee317640b069a678f8321a08c216..6aed0f2104bdf8323fc25df01bf6ebdbad7e84b1 100644 --- a/src/pages/ProfilePage.tsx +++ b/src/pages/ProfilePage.tsx @@ -1,10 +1,10 @@ -import './ProfilePage.css' -import {useParams} from "react-router-dom"; -import {NO_TOKEN, PROFESSOR_ROLE, STUDENT_ROLE} from "../utils.ts"; -import {useUserContext} from "../contexts/UserContext.tsx"; -import {useEffect, useState} from "react"; -import {fetchProfessorByFullName, fetchStudentByFullName} from "../services.ts"; -import {MajorItem, ResearchLabItem} from "../types.ts"; +import './ProfilePage.css'; +import { useParams } from "react-router-dom"; +import { NO_TOKEN, PROFESSOR_ROLE, STUDENT_ROLE } from "../utils.ts"; +import { useUserContext } from "../contexts/UserContext.tsx"; +import { useEffect, useState } from "react"; +import { fetchProfessorByFullName, fetchStudentByFullName, updateProfessorProfile, updateStudentProfile } from "../services.ts"; +import { MajorItem, ResearchLabItem } from "../types.ts"; interface ProfileUser { id: number; @@ -37,32 +37,62 @@ const situationLabelMap: { [key: string]: string } = { }; export default function ProfilePage() { - const {role, fullName} = useParams(); - const {token} = useUserContext(); + const { role, fullName } = useParams(); + const { token } = useUserContext(); const [profileUser, setProfileUser] = useState<ProfileUser>(); + const [isEditing, setIsEditing] = useState(false); + const [editData, setEditData] = useState<Partial<ProfileUser>>({}); useEffect(() => { const fetchProfileDetails = async () => { - if (token !== NO_TOKEN) { - if (fullName) { - try { - if (role === PROFESSOR_ROLE) { - const result = await fetchProfessorByFullName(fullName, token); - setProfileUser(result); - } - if (role === STUDENT_ROLE) { - const result = await fetchStudentByFullName(fullName, token); - setProfileUser(result); - } - } catch (error) { - console.error("Failed to load user with name: " + fullName, error); + if (token !== NO_TOKEN && fullName) { + try { + if (role === PROFESSOR_ROLE) { + const result = await fetchProfessorByFullName(fullName, token); + setProfileUser(result); + } else if (role === STUDENT_ROLE) { + const result = await fetchStudentByFullName(fullName, token); + setProfileUser(result); } + } catch (error) { + console.error("Failed to load user with name: " + fullName, error); } } - } + }; fetchProfileDetails(); }, [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 aboutMeArray = profileUser?.aboutMe ? profileUser.aboutMe.split(', ') : []; const selectedLabels = aboutMeArray.map(value => situationLabelMap[value]); @@ -73,66 +103,132 @@ export default function ProfilePage() { <> <div className='profile-header'> <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 className='profile-section'> - <h2>Contact Information</h2> - <p><span className="attribute">Email:</span> {profileUser.email}</p> - </div> - {role === STUDENT_ROLE && ( - <> - <div className='profile-section'> - <h2>Academic Information</h2> - <p><span className="attribute"></span>{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 && ( + {isEditing ? ( + <div className='edit-section'> + {role === PROFESSOR_ROLE && ( + <> + <h2>Edit Lab Information</h2> + <label> + Lab Name: + <input + className="text-input" + type="text" + name="lab.name" + value={editData.lab?.name || profileUser.lab?.name || ''} + onChange={handleEditChange} + /> + </label> + <label> + URL: + <input + className="text-input" + type="text" + name="lab.url" + value={editData.lab?.url || profileUser.lab?.url || ''} + onChange={handleEditChange} + /> + </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'> - <h2>Subscriptions</h2> - <ul> - {profileUser.subscriptions.map((sub, index) => ( - <li key={index}>{sub}</li> - ))} - </ul> + <h2>Contact Information</h2> + <p><span className="attribute">Email:</span> {profileUser.email}</p> </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> )} </div> ); -} \ No newline at end of file +} diff --git a/src/services.ts b/src/services.ts index 4ae24cefb16bfc0ab6a82356ba5f50ab94fe64bf..0d4aac0c11d3898778be05cd246a8bf10c203a64 100644 --- a/src/services.ts +++ b/src/services.ts @@ -214,4 +214,24 @@ export async function signUp(signUpForm: SignupForm): Promise<SignUpResponse> { 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 diff --git a/src/types.ts b/src/types.ts index 738ec41f15ca57d497c79531b2c65cf247d0b00f..55992285d97174eaed1b1a1109127c284077d7c4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -34,7 +34,8 @@ export interface OpeningsItem { description: string; type: string; url: string; - labId: number; + labName: string; + professor: ProfessorUser; } export interface CollegeItem {