Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update user profile #442

Draft
wants to merge 18 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions csm_web/frontend/src/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Policies from "./Policies";
import { EnrollmentMatcher } from "./enrollment_automation/EnrollmentMatcher";
import { Resources } from "./resource_aggregation/Resources";
import Section from "./section/Section";
import UserProfile from "./UserProfile";

import LogOutIcon from "../../static/frontend/img/log_out.svg";
import LogoNoText from "../../static/frontend/img/logo_no_text.svg";
Expand Down Expand Up @@ -39,6 +40,7 @@ const App = () => {
<Route path="resources/*" element={<Resources />} />
<Route path="matcher/*" element={<EnrollmentMatcher />} />
<Route path="policies/*" element={<Policies />} />
<Route path="profile/*" element={<UserProfile />} />
<Route path="*" element={<NotFound />} />
</Route>
</Routes>
Expand Down Expand Up @@ -85,6 +87,7 @@ function Header(): React.ReactElement {
if (
location.pathname.startsWith("/resources") ||
location.pathname.startsWith("/matcher") ||
location.pathname.startsWith("/profile") ||
location.pathname.startsWith("/policies")
) {
isActive = false;
Expand Down Expand Up @@ -135,6 +138,9 @@ function Header(): React.ReactElement {
) : null}
</div>
<div className="site-title-group">
<NavLink to="/profile" className={navlinkClassSubtitle}>
<h3 className="site-subtitle">Profile</h3>
</NavLink>
<NavLink to="/policies" className={navlinkClassSubtitle}>
<h3 className="site-subtitle">Policies</h3>
</NavLink>
Expand Down
239 changes: 239 additions & 0 deletions csm_web/frontend/src/components/UserProfile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
import { DateTime } from "luxon";
import React, { useState } from "react";
import { useUserInfo } from "../utils/queries/base";
import { useUserInfoUpdateMutation } from "../utils/queries/profiles";
import { RawUserInfo } from "../utils/types";

import "../css/profile.scss";

export const UserProfile = (): React.ReactElement => {
const { data: jsonUserInfo, isSuccess: userInfoLoaded } = useUserInfo();

return (
<React.Fragment>
<div>
{userInfoLoaded ? (
<DisplayUser userInfo={jsonUserInfo} priorityEnrollment={jsonUserInfo.priorityEnrollment} />
) : (
<></>
)}
</div>
</React.Fragment>
);
};

interface UserInfoProps {
userInfo: RawUserInfo;
priorityEnrollment?: string;
}

const DisplayUser = ({ userInfo, priorityEnrollment }: UserInfoProps) => {
/**
* Mutation to create a new section.
*/
const createSectionMutation = useUserInfoUpdateMutation(userInfo?.id);

const [editing, setEditing] = useState(false);
/**
* User First Name
*/
const userFirstName = userInfo.firstName;

/**
* User Last Name
*/

const userLastName = userInfo.lastName;
/**
* User email
*/
const userEmail = userInfo.email;
/**
* User Pronoun
*/
const [userPronoun, setUserPronoun] = useState<string>(userInfo.pronouns);
/**
* User Bio
*/
const [userBio, setUserBio] = useState<string>(userInfo.bio);
/**
* Pronunciation
*/
const [userPreferredName, setUserPreferredName] = useState<string>(userInfo.pronunciation);

const CHARACTER_LIMIT = 500;

let priority: DateTime | undefined;
if (priorityEnrollment) {
priority = DateTime.fromISO(priorityEnrollment);
}

const handleEditing = () => {
setEditing(true);
};

const handleCancel = () => {
// Reset form data if necessary
setEditing(false);
};

/**
* Handle save.
*/
const handleSave = (event: React.MouseEvent<HTMLButtonElement>): void => {
event.preventDefault();
const data = {
id: userInfo.id,
firstName: userFirstName,
lastName: userLastName,
email: userEmail,
isPrivate: userInfo.isPrivate,
bio: userBio,
priorityEnrollment: priority,
pronouns: userPronoun,
pronunciation: userPreferredName
};
console.log(data);
createSectionMutation.mutate(data, {
onSuccess: () => {
setEditing(false);
}
});
};

/**
* Handle the change of a form field.
*/
const handleChange = (name: string, value: string): void => {
switch (name) {
case "pronouns":
setUserPronoun(value);
break;
case "bio":
setUserBio(value);
break;
case "preferredName":
setUserPreferredName(value);
break;
default:
console.error("Unknown input name: " + name);
break;
}
};

// const handleBio = (value: string): void => {
// setUserBio()
// }

return (
<div>
{userInfo !== null ? (
<div className="formbold-main-wrapper">
<div className="formbold-form-wrapper">
<form action="" method="POST">
<div className="formbold-input-flex">
<div>
<input
type="text"
name="firstname"
id="firstname"
defaultValue={userInfo.firstName}
className="formbold-form-input"
disabled={true}
/>
<label className="formbold-form-label"> First name </label>
</div>
<div>
<input
type="text"
name="lastname"
id="lastname"
defaultValue={userInfo.lastName}
className="formbold-form-input"
disabled={true}
/>
<label className="formbold-form-label"> Last name </label>
</div>
</div>
<div className="formbold-textarea">
<textarea
name="bio"
id="bio"
placeholder="Write your bio..."
className="formbold-form-input-bio"
disabled={!editing}
defaultValue={userInfo.bio}
maxLength={500}
onChange={e => handleChange("bio", e.target.value)}
></textarea>
<label className="formbold-form-label"> Bio {`[${userBio.length} / ${CHARACTER_LIMIT}]`}</label>
</div>
<div className="formbold-input-flex">
<div>
<input
type="email"
name="email"
id="email"
defaultValue={userInfo.email}
className="formbold-form-input"
disabled={true}
/>
<label className="formbold-form-label"> Email </label>
</div>
<div>
<input
type="text"
name="pronouns"
id="pronouns"
placeholder=""
className="formbold-form-input"
defaultValue={userInfo.pronouns}
disabled={!editing}
maxLength={20}
onChange={e => handleChange("pronouns", e.target.value)}
/>
<label className="formbold-form-label"> Pronouns </label>
</div>
</div>
<div className="formbold-textarea">
<textarea
name="preferredName"
id="preferredName"
placeholder=""
className="formbold-form-input"
disabled={!editing}
defaultValue={userInfo.pronunciation}
maxLength={50}
onChange={e => handleChange("preferredName", e.target.value)}
></textarea>
<label className="formbold-form-label"> Preferred Name </label>
</div>
</form>
<div className="button-wrapper">
<>
{!editing ? (
<button className="formbold-btn" onClick={handleEditing}>
Edit
</button>
) : (
<>
<button className="formbold-btn" onClick={handleSave}>
Save
</button>
<button className="formbold-btn" onClick={handleCancel}>
Cancel
</button>
</>
)}
</>
</div>
</div>
</div>
) : (
""
)}
</div>
);
};

export default UserProfile;
16 changes: 15 additions & 1 deletion csm_web/frontend/src/components/section/MentorSectionInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import React, { useState } from "react";
import * as React from "react";
import { useState } from "react";

import { useSectionStudents } from "../../utils/queries/sections";
import { Mentor, Spacetime, Student } from "../../utils/types";
import LoadingSpinner from "../LoadingSpinner";
import { CoordinatorAddStudentModal } from "./CoordinatorAddStudentModal";
import MetaEditModal from "./MetaEditModal";
import ProfileModal from "./ProfileModal";
import { InfoCard, SectionSpacetime } from "./Section";
import SpacetimeDeleteModal from "./SpacetimeDeleteModal";
import SpacetimeEditModal from "./SpacetimeEditModal";
import StudentDropper from "./StudentDropper";

import EyeIcon from "../../../static/frontend/img/eye.svg";
import PencilIcon from "../../../static/frontend/img/pencil.svg";
import XIcon from "../../../static/frontend/img/x.svg";

Expand Down Expand Up @@ -90,6 +93,17 @@ export default function MentorSectionInfo({
/>
)}
<span className="student-info">{name || email}</span>
<button
className="secondary-link-btn info-card-edit-btn"
onClick={() => {
setShowModal(ModalStates.SPACETIME_EDIT);
}}
>
<EyeIcon className="icon" /> View
</button>
{showModal === ModalStates.SPACETIME_EDIT && (
<ProfileModal id={studentId} closeModal={closeModal} />
)}
</td>
</tr>
)
Expand Down
Loading