diff --git a/csm_web/frontend/src/components/App.tsx b/csm_web/frontend/src/components/App.tsx index 6799ce7c..6b4d2040 100644 --- a/csm_web/frontend/src/components/App.tsx +++ b/csm_web/frontend/src/components/App.tsx @@ -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"; @@ -39,6 +40,7 @@ const App = () => { } /> } /> } /> + } /> } /> @@ -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; @@ -135,6 +138,9 @@ function Header(): React.ReactElement { ) : null}
+ +

Profile

+

Policies

diff --git a/csm_web/frontend/src/components/UserProfile.tsx b/csm_web/frontend/src/components/UserProfile.tsx new file mode 100644 index 00000000..0b81d09c --- /dev/null +++ b/csm_web/frontend/src/components/UserProfile.tsx @@ -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 ( + +
+ {userInfoLoaded ? ( + + ) : ( + <> + )} +
+
+ ); +}; + +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(userInfo.pronouns); + /** + * User Bio + */ + const [userBio, setUserBio] = useState(userInfo.bio); + /** + * Pronunciation + */ + const [userPreferredName, setUserPreferredName] = useState(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): 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 ( +
+ {userInfo !== null ? ( +
+
+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ handleChange("pronouns", e.target.value)} + /> + +
+
+
+ + +
+
+
+ <> + {!editing ? ( + + ) : ( + <> + + + + )} + +
+
+
+ ) : ( + "" + )} +
+ ); +}; + +export default UserProfile; diff --git a/csm_web/frontend/src/components/section/MentorSectionInfo.tsx b/csm_web/frontend/src/components/section/MentorSectionInfo.tsx index e2e76b30..0cb6a073 100644 --- a/csm_web/frontend/src/components/section/MentorSectionInfo.tsx +++ b/csm_web/frontend/src/components/section/MentorSectionInfo.tsx @@ -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"; @@ -90,6 +93,17 @@ export default function MentorSectionInfo({ /> )} {name || email} + + {showModal === ModalStates.SPACETIME_EDIT && ( + + )} ) diff --git a/csm_web/frontend/src/components/section/ProfileModal.tsx b/csm_web/frontend/src/components/section/ProfileModal.tsx new file mode 100644 index 00000000..2fec53f5 --- /dev/null +++ b/csm_web/frontend/src/components/section/ProfileModal.tsx @@ -0,0 +1,125 @@ +import * as React from "react"; +import { useUserInfo } from "../../utils/queries/base"; +import { UseUserInfoWithId } from "../../utils/queries/profiles"; +import { useSectionStudents } from "../../utils/queries/sections"; +import { UserInfo } from "../../utils/types"; +import LoadingSpinner from "../LoadingSpinner"; +import Modal from "../Modal"; + +interface ProfileModalProps { + id: number; + closeModal: () => void; +} + +const ProfileModal = ({ id, closeModal }: ProfileModalProps): React.ReactElement => { + const { data: jsonUserInfo, isSuccess: userInfoLoaded } = useSectionStudents(id); + // console.log(id); + // console.log(); + // console.log(jsonUserInfo); + + let userInfo: UserInfo | null; + if (userInfoLoaded) { + let priorityEnrollment = undefined; + if (jsonUserInfo.priorityEnrollment) { + priorityEnrollment = new Date(Date.parse(jsonUserInfo.priorityEnrollment)); + } + const convertedUserInfo: UserInfo = { + ...jsonUserInfo, + priorityEnrollment + }; + userInfo = convertedUserInfo; + } else { + // not done loading yet + userInfo = null; + } + + return ( + +
+ {userInfo !== null ? ( +
+
+
+
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ +
+ + +
+
+ + +
+
+
+
+
+ ) : ( + "" + )} +
+
+ ); +}; + +export default ProfileModal; diff --git a/csm_web/frontend/src/components/section/StudentSection.tsx b/csm_web/frontend/src/components/section/StudentSection.tsx index 7ba08464..bd74b05c 100644 --- a/csm_web/frontend/src/components/section/StudentSection.tsx +++ b/csm_web/frontend/src/components/section/StudentSection.tsx @@ -1,12 +1,16 @@ import { DateTime } from "luxon"; -import React, { useEffect, useState } from "react"; -import { Navigate, Route, Routes } from "react-router-dom"; +import * as React from "react"; +import { useEffect, useState } from "react"; +import { Navigate, Route, Routes, useParams } from "react-router-dom"; +import { fetchNormalized, fetchWithMethod, HTTP_METHODS } from "../../utils/api"; import { DEFAULT_TIMEZONE } from "../../utils/datetime"; +import { useMentorProfile } from "../../utils/queries/profiles"; import { useDropUserMutation, useStudentAttendances, - useStudentSubmitWordOfTheDayMutation + useStudentSubmitWordOfTheDayMutation, + useSectionStudents } from "../../utils/queries/sections"; import { Mentor, Override, Role, Spacetime } from "../../utils/types"; import LoadingSpinner from "../LoadingSpinner"; @@ -39,6 +43,31 @@ export default function StudentSection({ override, associatedProfileId }: StudentSectionType) { + const { sectionId } = useParams(); + // console.log(id); + + // const mentorID = mentor.id; + // let mentorProfile; + // // console.log(mentor) + + // const fetchTemp = async () => { + // await fetchWithMethod(`/user/${mentorID}/fetch_user_id`, HTTP_METHODS.POST, {'type': "mentor"}).then(response => + // response.json().then(data => mentorProfile = data)) + + // // const response = await fetchWithMethod(`/user/${mentorID}/fetch_user_id`, HTTP_METHODS.POST, {'type': "mentor"}); + // // if (response.ok) { + // // console.log(await response.json()) + // // } else { + // // return + // // } + // } + + // console.log(useMentorProfile(mentorID)); + + // fetchTemp(); + + // console.log(mentor); + return ( @@ -54,6 +84,7 @@ export default function StudentSection({ path="attendance" element={} /> + } />
{mentor.name}
- {mentor.email} + {/* {mentor.email} */} + )} + {spacetimes.map(({ override, ...spacetime }, index) => ( 1} @@ -154,6 +187,90 @@ function DropSection({ profileId }: DropSectionProps) { } } +interface ProfileSectionProps { + profileId: number; +} + +enum ProfileSectionStage { + CLOSED = "CLOSED", + OPENED = "OPENED" +} + +function ProfileSection({ profileId }: ProfileSectionProps) { + // const studentDropMutation = useDropUserMutation(profileId); + const [stage, setStage] = useState(ProfileSectionStage.CLOSED); + const [mentorProfile, setMentorProfile] = useState(); + + useEffect(() => { + fetchData(); + }, []); + + const fetchData = async () => { + const result = fetchWithMethod(`/user/${profileId}/fetch_user_id`, HTTP_METHODS.POST, { type: "mentor" }).then( + response => + response.json().then(data => { + setMentorProfile(data); + console.log(data); + return data; + }) + ); + return result; + }; + + // current user's id from Students + + // console.log(profileId); + + // const { data: jsonUserInfo, isSuccess: userInfoLoaded } = useMentorProfile(profileId); + + // const response = async () => await fetchNormalized(`/user/${profileId}/profile`).then((data) => data.json()); + + // const mentorID = mentor.id; + // console.log(mentor) + + const fetchTemp = async () => { + // await fetchWithMethod(`/user/${profileId}/fetch_user_id`, HTTP_METHODS.POST, {'type': "mentor"}).then(response => + // response.json().then(data => setMentorProfile(data))) + // const response = await fetchWithMethod(`/user/${mentorID}/fetch_user_id`, HTTP_METHODS.POST, {'type': "mentor"}); + // if (response.ok) { + // console.log(await response.json()) + // } else { + // return + // } + }; + + // fetchTemp(); + + // console.log(mentorProfile); + + switch (stage) { + case ProfileSectionStage.CLOSED: + return ( + + ); + case ProfileSectionStage.OPENED: + return ( + setStage(ProfileSectionStage.CLOSED)}> +
+ {mentorProfile ? ( + <> +
{mentorProfile.firstName}
+
{mentorProfile.lastName}
+
{mentorProfile.bio}
+
{mentorProfile.pronouns}
+
{mentorProfile.email}
+ + ) : ( + "" + )} +
+
+ ); + } +} + interface StudentSectionAttendanceProps { associatedProfileId: number; id: number; @@ -357,3 +474,52 @@ function StudentSectionAttendance({ associatedProfileId, id }: StudentSectionAtt ); } + +interface StudentListProps { + associatedProfileId: number; + id: number; +} + +// Need permission for student to load student list, or new backend point? +function StudentList({ associatedProfileId, id }: StudentListProps) { + const { + data: studentList, + isSuccess: listLoaded, + isError: listLoadError, + refetch: refetchStudentList + } = useSectionStudents(id); + + return listLoaded ? ( + + + + + + + + + + {studentList + // convert to a table row + .map(({ name, email }) => { + // const [label, cssSuffix] = ATTENDANCE_LABELS[presence]; + // const attendanceColor = scssColors[`attendance-${cssSuffix}`]; + // const attendanceFgColor = scssColors[`attendance-${cssSuffix}-fg`]; + return ( + + + + + ); + })} + +
NameProfile
{name} +
{email}
+
+
+ ) : listLoadError ? ( +

Student List could not be loaded

+ ) : ( + + ); +} diff --git a/csm_web/frontend/src/css/base/autogrid.scss b/csm_web/frontend/src/css/base/autogrid.scss new file mode 100644 index 00000000..910ccd6e --- /dev/null +++ b/csm_web/frontend/src/css/base/autogrid.scss @@ -0,0 +1,5 @@ +@use "variables" as *; + +.auto-grid-item { + padding: 0 8px; +} diff --git a/csm_web/frontend/src/css/profile 2.scss b/csm_web/frontend/src/css/profile 2.scss new file mode 100644 index 00000000..eae077e0 --- /dev/null +++ b/csm_web/frontend/src/css/profile 2.scss @@ -0,0 +1,117 @@ +@use "base/variables" as *; +@use "base/button"; + +$default-font: "Montserrat", sans-serif; + +textarea { + height: "auto"; +} + +.formbold-main-wrapper { + display: flex; + align-items: center; + justify-content: center; + padding: 48px; + font-family: $default-font; +} + +.formbold-form-wrapper { + width: 100%; + max-width: 550px; + margin: 0 auto; + background: white; +} + +.formbold-input-flex { + display: flex; + gap: 20px; + margin-bottom: 22px; +} + +.formbold-input-flex > div { + display: flex; + flex-direction: column-reverse; + width: 50%; +} + +.formbold-textarea { + display: flex; + flex-direction: column-reverse; +} + +.formbold-form-input { + width: 100%; + padding-bottom: 10px; + font-family: $default-font; + font-size: 16px; + font-weight: 500; + color: #07074d; + resize: none; + background: #fff; + border: none; + border-bottom: 1px solid #dde3ec; + outline: none; +} + +.formbold-form-input-bio { + width: 100%; + height: auto; + min-height: 200px; + padding-bottom: 10px; + overflow: "hidden"; + font-family: $default-font; + font-size: 16px; + font-weight: 500; + color: #07074d; + resize: none; + background: #fff; + border: none; + border-bottom: 1px solid #dde3ec; + outline: none; +} + +.formbold-form-input::placeholder { + color: #536387; +} + +.formbold-form-input:focus { + border-color: #6a64f1; +} + +.formbold-form-label { + display: block; + margin-bottom: 18px; + font-size: 14px; + font-weight: 500; + line-height: 24px; + color: #07074d; +} + +.formbold-form-input:focus + .formbold-form-label { + color: #6a64f1; +} + +.button-wrapper { + display: flex; + flex-direction: row-reverse; + align-items: center; + justify-content: space-evenly; + padding: 5px; + margin: 10px; +} + +.formbold-btn { + padding: 12px 25px; + margin-top: 25px; + font-size: 16px; + font-weight: 500; + color: black; + cursor: pointer; + background-color: var(--csm-green); + border: none; + border-radius: 5px; +} + +.formbold-btn:hover { + box-shadow: 0 3px 8px rgb(0 0 0 / 5%); +} diff --git a/csm_web/frontend/src/css/profile.scss b/csm_web/frontend/src/css/profile.scss new file mode 100644 index 00000000..eae077e0 --- /dev/null +++ b/csm_web/frontend/src/css/profile.scss @@ -0,0 +1,117 @@ +@use "base/variables" as *; +@use "base/button"; + +$default-font: "Montserrat", sans-serif; + +textarea { + height: "auto"; +} + +.formbold-main-wrapper { + display: flex; + align-items: center; + justify-content: center; + padding: 48px; + font-family: $default-font; +} + +.formbold-form-wrapper { + width: 100%; + max-width: 550px; + margin: 0 auto; + background: white; +} + +.formbold-input-flex { + display: flex; + gap: 20px; + margin-bottom: 22px; +} + +.formbold-input-flex > div { + display: flex; + flex-direction: column-reverse; + width: 50%; +} + +.formbold-textarea { + display: flex; + flex-direction: column-reverse; +} + +.formbold-form-input { + width: 100%; + padding-bottom: 10px; + font-family: $default-font; + font-size: 16px; + font-weight: 500; + color: #07074d; + resize: none; + background: #fff; + border: none; + border-bottom: 1px solid #dde3ec; + outline: none; +} + +.formbold-form-input-bio { + width: 100%; + height: auto; + min-height: 200px; + padding-bottom: 10px; + overflow: "hidden"; + font-family: $default-font; + font-size: 16px; + font-weight: 500; + color: #07074d; + resize: none; + background: #fff; + border: none; + border-bottom: 1px solid #dde3ec; + outline: none; +} + +.formbold-form-input::placeholder { + color: #536387; +} + +.formbold-form-input:focus { + border-color: #6a64f1; +} + +.formbold-form-label { + display: block; + margin-bottom: 18px; + font-size: 14px; + font-weight: 500; + line-height: 24px; + color: #07074d; +} + +.formbold-form-input:focus + .formbold-form-label { + color: #6a64f1; +} + +.button-wrapper { + display: flex; + flex-direction: row-reverse; + align-items: center; + justify-content: space-evenly; + padding: 5px; + margin: 10px; +} + +.formbold-btn { + padding: 12px 25px; + margin-top: 25px; + font-size: 16px; + font-weight: 500; + color: black; + cursor: pointer; + background-color: var(--csm-green); + border: none; + border-radius: 5px; +} + +.formbold-btn:hover { + box-shadow: 0 3px 8px rgb(0 0 0 / 5%); +} diff --git a/csm_web/frontend/src/utils/queries/profiles.tsx b/csm_web/frontend/src/utils/queries/profiles.tsx new file mode 100644 index 00000000..ca34a0ad --- /dev/null +++ b/csm_web/frontend/src/utils/queries/profiles.tsx @@ -0,0 +1,127 @@ +/** + * Query hooks regarding sections. + */ + +import { useMutation, UseMutationResult, useQuery, useQueryClient, UseQueryResult } from "@tanstack/react-query"; +import { isNull } from "lodash"; +import { DateTime } from "luxon"; +import { fetchNormalized, fetchWithMethod, HTTP_METHODS } from "../api"; +import { RawUserInfo, Section } from "../types"; +import { handleError, handlePermissionsError, handleRetry, PermissionError, ServerError } from "./helpers"; + +/* ===== Mutation ===== */ +/** + * Hook to mutate user info + */ +export interface UpdateUserInfo { + id: number; + firstName: string; + lastName: string; + email: string; + priorityEnrollment?: DateTime; + isPrivate: boolean; + bio?: string; + pronouns?: string; + pronunciation?: string; +} + +/** + * Hook to get the user's info. + */ +export const useStudentsInfo = (): UseQueryResult => { + const queryResult = useQuery( + ["students"], + async () => { + const response = await fetchNormalized("/students"); + if (response.ok) { + return await response.json(); + } else { + handlePermissionsError(response.status); + throw new ServerError("Failed to fetch user info"); + } + }, + { retry: handleRetry } + ); + + handleError(queryResult); + return queryResult; +}; + +/** + * Hook to get a mentor profile within section + */ +export const useMentorProfile = (id: number): UseQueryResult => { + const queryResult = useQuery( + ["sections", id], + async () => { + if (isNaN(id)) { + throw new PermissionError("Invalid section id"); + } + const response = await fetchNormalized(`/sections/${id}`); + if (response.ok) { + const section = await response.json(); + const mentor = section.mentor; + return mentor; + } else { + handlePermissionsError(response.status); + throw new ServerError(`Failed to fetch section ${id}`); + } + }, + { retry: handleRetry } + ); + + handleError(queryResult); + return queryResult; +}; + +export const useUserInfoUpdateMutation = ( + userId: number | undefined +): UseMutationResult => { + const queryClient = useQueryClient(); + const mutationResult = useMutation( + async (body: UpdateUserInfo) => { + if (isNull(userId)) { + throw new PermissionError("Invalid user id"); + } + const response = await fetchWithMethod(`/user/${userId}/profile`, HTTP_METHODS.PUT, body); + if (response.ok) { + return; + } else { + handlePermissionsError(response.status); + throw new ServerError(`Failed to update user info`); + } + }, + { + onSuccess: () => { + // invalidate all queries for the section + queryClient.invalidateQueries(["userinfo"]); + }, + retry: handleRetry + } + ); + + handleError(mutationResult); + return mutationResult; +}; + +/** + * Hook to get a section with a given id. + */ +export const UseUserInfoWithId = (id: number): UseQueryResult => { + const queryResult = useQuery( + ["userinfo", id], + async () => { + const response = await fetchNormalized("/userinfo"); + if (response.ok) { + return await response.json(); + } else { + handlePermissionsError(response.status); + throw new ServerError("Failed to fetch user info"); + } + }, + { retry: handleRetry } + ); + + handleError(queryResult); + return queryResult; +}; diff --git a/csm_web/frontend/src/utils/queries/sections.tsx b/csm_web/frontend/src/utils/queries/sections.tsx index a4bbcf8c..75b5aaa9 100644 --- a/csm_web/frontend/src/utils/queries/sections.tsx +++ b/csm_web/frontend/src/utils/queries/sections.tsx @@ -4,7 +4,7 @@ import { useMutation, UseMutationResult, useQuery, useQueryClient, UseQueryResult } from "@tanstack/react-query"; import { fetchNormalized, fetchWithMethod, HTTP_METHODS } from "../api"; -import { Attendance, RawAttendance, Section, Spacetime, Student } from "../types"; +import { Attendance, RawAttendance, Section, Spacetime, Student, UserInfo } from "../types"; import { handleError, handlePermissionsError, handleRetry, PermissionError, ServerError } from "./helpers"; /* ===== Queries ===== */ @@ -112,6 +112,29 @@ export const useStudentAttendances = (studentId: number): UseQueryResult => { +// const queryResult = useQuery( +// ["students", studentId, "attendance"], +// async () => { +// if (isNaN(studentId)) { +// throw new PermissionError("Invalid student id"); +// } +// const response = await fetchNormalized(`/students/${studentId}/attendances`); +// if (response.ok) { +// return await response.json(); +// } else { +// handlePermissionsError(response.status); +// throw new ServerError(`Failed to fetch student ${studentId} attendances`); +// } +// }, +// { retry: handleRetry } +// ); +// +// handleError(queryResult); +// return queryResult; +// }; + interface WordOfTheDayResponse { id: number; // section occurrence id wordOfTheDay: string; diff --git a/csm_web/frontend/src/utils/types.tsx b/csm_web/frontend/src/utils/types.tsx index 78e9f215..e9d91466 100644 --- a/csm_web/frontend/src/utils/types.tsx +++ b/csm_web/frontend/src/utils/types.tsx @@ -36,6 +36,10 @@ export interface UserInfo { lastName: string; email: string; priorityEnrollment?: DateTime; + isPrivate: boolean; + bio: string; + pronouns: string; + pronunciation: string; } /** diff --git a/csm_web/scheduler/migrations/0033_user_bio_user_is_private_user_pronouns_and_more.py b/csm_web/scheduler/migrations/0033_user_bio_user_is_private_user_pronouns_and_more.py new file mode 100644 index 00000000..fa919a25 --- /dev/null +++ b/csm_web/scheduler/migrations/0033_user_bio_user_is_private_user_pronouns_and_more.py @@ -0,0 +1,40 @@ +# Generated by Django 4.2.1 on 2023-11-07 03:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("scheduler", "0032_word_of_the_day"), + ] + + operations = [ + migrations.AddField( + model_name="user", + name="bio", + field=models.CharField(default="", max_length=500), + ), + migrations.AddField( + model_name="user", + name="is_private", + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name="user", + name="pronouns", + field=models.CharField( + blank=True, + choices=[ + ("he/him", "He"), + ("she/her", "She"), + ("they/them", "They"), + ("", "None"), + ], + ), + ), + migrations.AddField( + model_name="user", + name="pronunciation", + field=models.CharField(default="", max_length=20), + ), + ] diff --git a/csm_web/scheduler/models.py b/csm_web/scheduler/models.py index 9e367ab6..8aa0f50d 100644 --- a/csm_web/scheduler/models.py +++ b/csm_web/scheduler/models.py @@ -51,6 +51,18 @@ def week_bounds(date): class User(AbstractUser): priority_enrollment = models.DateTimeField(null=True, blank=True) + class Pronouns(models.TextChoices): + HE = "he/him" + SHE = "she/her" + THEY = "they/them" + NONE = "" + + pronouns = models.CharField(choices=Pronouns.choices, blank=True) + is_private = models.BooleanField(default=False) + pronunciation = models.CharField(max_length=20, default="") + + bio = models.CharField(max_length=500, default="") + def can_enroll_in_course(self, course, bypass_enrollment_time=False): """Determine whether this user is allowed to enroll in the given course.""" # check restricted first @@ -260,19 +272,15 @@ def save(self, *args, **kwargs): ): if settings.DJANGO_ENV != settings.DEVELOPMENT: logger.info( - ( - " SO automatically created for student" - " %s in course %s for date %s" - ), + " SO automatically created for student" + " %s in course %s for date %s", self.user.email, course.name, now.date(), ) logger.info( - ( - " Attendance automatically created for student" - " %s in course %s for date %s" - ), + " Attendance automatically created for student" + " %s in course %s for date %s", self.user.email, course.name, now.date(), diff --git a/csm_web/scheduler/serializers.py b/csm_web/scheduler/serializers.py index cd31f741..72997289 100644 --- a/csm_web/scheduler/serializers.py +++ b/csm_web/scheduler/serializers.py @@ -167,7 +167,8 @@ class Meta: class UserSerializer(serializers.ModelSerializer): class Meta: model = User - fields = ("id", "email", "first_name", "last_name", "priority_enrollment") + fields = ("id", "email", "first_name", "last_name", "priority_enrollment", + "pronouns", "is_private", "pronunciation", "bio") class ProfileSerializer(serializers.Serializer): # pylint: disable=abstract-method diff --git a/csm_web/scheduler/urls.py b/csm_web/scheduler/urls.py index b997222f..a4a1036c 100644 --- a/csm_web/scheduler/urls.py +++ b/csm_web/scheduler/urls.py @@ -23,4 +23,6 @@ path("matcher//mentors/", views.matcher.mentors), path("matcher//configure/", views.matcher.configure), path("matcher//create/", views.matcher.create), + path("user//profile/", views.user.profile), + path("user//fetch_user_id/", views.user.fetch_user_pk), ] diff --git a/csm_web/scheduler/views/student.py b/csm_web/scheduler/views/student.py index fc3d29c8..3f2b696d 100644 --- a/csm_web/scheduler/views/student.py +++ b/csm_web/scheduler/views/student.py @@ -1,16 +1,14 @@ from django.core.exceptions import ObjectDoesNotExist from django.db.models import Q from django.utils import timezone -from scheduler.models import Attendance, SectionOccurrence from rest_framework import status, viewsets from rest_framework.decorators import action from rest_framework.exceptions import PermissionDenied from rest_framework.response import Response -import datetime -from .utils import log_str, logger, get_object_or_error from ..models import Student from ..serializers import AttendanceSerializer, StudentSerializer +from .utils import get_object_or_error, log_str, logger class StudentViewSet(viewsets.GenericViewSet): @@ -28,6 +26,10 @@ def get_queryset(self): @action(detail=True, methods=["patch"]) def drop(self, request, pk=None): + """ + Drops a student form a course. + PATCH: Drop a given student. Check for student ban if coordinator made request + """ student = get_object_or_error(self.get_queryset(), pk=pk) is_coordinator = student.course.coordinator_set.filter( user=request.user @@ -43,7 +45,10 @@ def drop(self, request, pk=None): student.course.whitelist.remove(student.user) student.save() logger.info( - f" User {log_str(request.user)} dropped Section {log_str(student.section)} for Student user {log_str(student.user)}" + " User %s dropped section %s for Student %s.", + log_str(request.user), + log_str(student.section), + log_str(student.user), ) # filter attendances and delete future attendances now = timezone.now().astimezone(timezone.get_default_timezone()) @@ -51,15 +56,25 @@ def drop(self, request, pk=None): Q( sectionOccurrence__date__gte=now.date(), sectionOccurrence__section=student.section, + presence="", ) ).delete() logger.info( - f" Deleted {num_deleted} attendances for user {log_str(student.user)} in Section {log_str(student.section)} after {now.date()}" + " Deleted %s attendances for user %s in Section %s after %s", + num_deleted, + log_str(student.user), + log_str(student.section), + now.date(), ) return Response(status=status.HTTP_204_NO_CONTENT) @action(detail=True, methods=["get", "put"]) def attendances(self, request, pk=None): + """ + Method for updating attendances. + GET: Gets the attendances for a student + PUT: Updates the attendances for a student + """ student = get_object_or_error(self.get_queryset(), pk=pk) if request.method == "GET": return Response( @@ -81,18 +96,26 @@ def attendances(self, request, pk=None): ) except ObjectDoesNotExist: logger.error( - f" Could not record attendance for User {log_str(request.user)}, used non-existent attendance id {request.data['id']}" + ( + " Could not record attendance for User %s, used" + " non-existent attendance ID %s" + ), + log_str(request.user), + request.data["id"], ) return Response(status=status.HTTP_400_BAD_REQUEST) if serializer.is_valid(): attendance = serializer.save() logger.info( - f" Attendance {log_str(attendance)} recorded for User {log_str(request.user)}" + " Attendance %s recorded for User %s", + log_str(attendance), + log_str(request.user), ) return Response(status=status.HTTP_204_NO_CONTENT) logger.error( - f" Could not record attendance for User {log_str(request.user)}, errors: {serializer.errors}" + " Could not record attendance for User %s, errors: %s", + log_str(request.user), + serializer.errors, ) return Response(serializer.errors, status=status.HTTP_422_UNPROCESSABLE_ENTITY) - diff --git a/csm_web/scheduler/views/user.py b/csm_web/scheduler/views/user.py index aeef9440..82774b98 100644 --- a/csm_web/scheduler/views/user.py +++ b/csm_web/scheduler/views/user.py @@ -1,11 +1,11 @@ -from rest_framework.exceptions import PermissionDenied -from rest_framework.response import Response from rest_framework import status from rest_framework.decorators import api_view +from rest_framework.exceptions import PermissionDenied +from rest_framework.response import Response +from scheduler.serializers import UserSerializer +from ..models import Coordinator, Mentor, Student, User from .utils import viewset_with -from ..models import Coordinator, User -from scheduler.serializers import UserSerializer class UserViewSet(*viewset_with("list")): @@ -13,6 +13,9 @@ class UserViewSet(*viewset_with("list")): queryset = User.objects.all() def list(self, request): + """ + Lists users. + """ if not ( request.user.is_superuser or Coordinator.objects.filter(user=request.user).exists() @@ -32,3 +35,75 @@ def userinfo(request): """ serializer = UserSerializer(request.user) return Response(serializer.data, status=status.HTTP_200_OK) + + +@api_view(["GET", "PUT"]) +def profile(request, pk=None): + """ + Function for handling user profile things + GET: Gets the information associated with the user profile. + PUT: Edit the profile of a user specified by the user id. + ANY coordinator for ANY course can edit ANY profile. + Request: {'user_id': int} + Response: status code + """ + queryset = User.objects.all() + coordinators = Coordinator.objects.all() + + if request.method == "GET": + serializer = UserSerializer(queryset.get(pk=pk)) + return Response(serializer.data, status=status.HTTP_200_OK) + + if not ( + request.user.is_superuser + or (queryset.filter(pk=pk).exists() and queryset.get(pk=pk) == request.user) + or coordinators.filter(user=request.user).exists() + ): + raise PermissionDenied("You're not allowed to edit that user's profile.") + user = queryset.get(pk=pk) + bio = request.data.get("bio") + pronunciation = request.data.get("pronunciation") + pronoun = request.data.get("pronouns") + private = request.data.get("is_private") + if bio is not None: + user.bio = bio + if pronunciation is not None: + user.pronunciation = pronunciation + if pronoun is not None: + user.pronouns = pronoun + if private is not None: + user.is_private = private + user.save() + serializer = UserSerializer(user) + return Response(serializer.data, status=status.HTTP_200_OK) + + +@api_view(["POST"]) +def fetch_user_pk(request, pk=None): + """ + Function for getting user IDs from profile IDs + GET: Gets the user serializer for a user given a mentor ID, student ID, or coordinator ID. + Request: {'type': string} + Response: status code, as well as user serializer + """ + profile_type = request.data.get("type") + queryset = None + if profile_type == "student": + queryset = Student.objects.all() + elif profile_type == "mentor": + queryset = Mentor.objects.all() + print(queryset) + elif profile_type == "coordinator": + queryset = Coordinator.objects.all() + else: + return Response(status=status.HTTP_400_BAD_REQUEST) + print(pk) + + person = queryset.get(pk=pk) + + print(person) + + serializer = UserSerializer(person.user) + + print(serializer.data) + return Response(serializer.data, status=status.HTTP_200_OK) diff --git a/package-lock.json b/package-lock.json index 6ce9e501..985f4398 100644 --- a/package-lock.json +++ b/package-lock.json @@ -54,7 +54,7 @@ "eslint-config-prettier": "^8.10.0", "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-cypress": "^2.12.1", - "eslint-plugin-import": "^2.28.1", + "eslint-plugin-import": "^2.29.1", "eslint-plugin-react": "^7.31.10", "eslint-plugin-react-hooks": "^4.6.0", "jest": "^29.6.2", @@ -88,9 +88,9 @@ } }, "node_modules/@adobe/css-tools": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.0.1.tgz", - "integrity": "sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.3.tgz", + "integrity": "sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==", "dev": true }, "node_modules/@ampproject/remapping": { @@ -107,12 +107,13 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", "dev": true, "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" @@ -185,13 +186,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.5.tgz", - "integrity": "sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", "dev": true, "dependencies": { - "@babel/types": "^7.20.5", + "@babel/types": "^7.23.6", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "engines": { @@ -310,9 +312,9 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, "engines": { "node": ">=6.9.0" @@ -331,25 +333,25 @@ } }, "node_modules/@babel/helper-function-name": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", - "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "dependencies": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -478,30 +480,30 @@ } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, "engines": { "node": ">=6.9.0" @@ -546,13 +548,13 @@ } }, "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -560,9 +562,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.10.tgz", - "integrity": "sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz", + "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -1875,34 +1877,34 @@ } }, "node_modules/@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.23.9.tgz", + "integrity": "sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.23.9", + "@babel/types": "^7.23.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.5.tgz", - "integrity": "sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.5", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.5", - "@babel/types": "^7.20.5", - "debug": "^4.1.0", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.9.tgz", + "integrity": "sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.9", + "@babel/types": "^7.23.9", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { @@ -1910,13 +1912,13 @@ } }, "node_modules/@babel/types": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz", - "integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz", + "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -4068,9 +4070,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -4194,9 +4196,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -4235,9 +4237,9 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -5932,7 +5934,7 @@ "node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, "node_modules/colord": { @@ -7500,9 +7502,9 @@ } }, "node_modules/eslint-plugin-import": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.0.tgz", - "integrity": "sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==", + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", + "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", "dev": true, "dependencies": { "array-includes": "^3.1.7", @@ -7521,7 +7523,7 @@ "object.groupby": "^1.0.1", "object.values": "^1.1.7", "semver": "^6.3.1", - "tsconfig-paths": "^3.14.2" + "tsconfig-paths": "^3.15.0" }, "engines": { "node": ">=4" @@ -8718,7 +8720,7 @@ "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "engines": { "node": ">=4" @@ -12277,9 +12279,9 @@ "dev": true }, "node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "dev": true, "funding": [ { @@ -12770,9 +12772,9 @@ } }, "node_modules/postcss": { - "version": "8.4.29", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz", - "integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==", + "version": "8.4.33", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", + "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==", "dev": true, "funding": [ { @@ -12789,7 +12791,7 @@ } ], "dependencies": { - "nanoid": "^3.3.6", + "nanoid": "^3.3.7", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, @@ -15678,9 +15680,9 @@ } }, "node_modules/tsconfig-paths": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", - "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", "dev": true, "dependencies": { "@types/json5": "^0.0.29", @@ -16352,9 +16354,9 @@ "dev": true }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -16577,9 +16579,9 @@ "dev": true }, "@adobe/css-tools": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.0.1.tgz", - "integrity": "sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.3.tgz", + "integrity": "sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==", "dev": true }, "@ampproject/remapping": { @@ -16593,12 +16595,13 @@ } }, "@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", "dev": true, "requires": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" } }, "@babel/compat-data": { @@ -16650,13 +16653,14 @@ } }, "@babel/generator": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.5.tgz", - "integrity": "sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", "dev": true, "requires": { - "@babel/types": "^7.20.5", + "@babel/types": "^7.23.6", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "dependencies": { @@ -16744,9 +16748,9 @@ } }, "@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true }, "@babel/helper-explode-assignable-expression": { @@ -16759,22 +16763,22 @@ } }, "@babel/helper-function-name": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", - "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "requires": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" } }, "@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-member-expression-to-functions": { @@ -16870,24 +16874,24 @@ } }, "@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", "dev": true }, "@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true }, "@babel/helper-validator-option": { @@ -16920,20 +16924,20 @@ } }, "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "dev": true, "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.10.tgz", - "integrity": "sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.9.tgz", + "integrity": "sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==", "dev": true }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { @@ -17805,42 +17809,42 @@ } }, "@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.23.9.tgz", + "integrity": "sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==", "dev": true, "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.23.9", + "@babel/types": "^7.23.9" } }, "@babel/traverse": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.5.tgz", - "integrity": "sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.5", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.5", - "@babel/types": "^7.20.5", - "debug": "^4.1.0", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.9.tgz", + "integrity": "sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.9", + "@babel/types": "^7.23.9", + "debug": "^4.3.1", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz", - "integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.9.tgz", + "integrity": "sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==", "dev": true, "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" } }, @@ -19443,9 +19447,9 @@ }, "dependencies": { "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -19509,9 +19513,9 @@ }, "dependencies": { "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -19536,9 +19540,9 @@ }, "dependencies": { "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -20864,7 +20868,7 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, "colord": { @@ -22191,9 +22195,9 @@ } }, "eslint-plugin-import": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.0.tgz", - "integrity": "sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==", + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", + "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", "dev": true, "requires": { "array-includes": "^3.1.7", @@ -22212,7 +22216,7 @@ "object.groupby": "^1.0.1", "object.values": "^1.1.7", "semver": "^6.3.1", - "tsconfig-paths": "^3.14.2" + "tsconfig-paths": "^3.15.0" }, "dependencies": { "debug": { @@ -22930,7 +22934,7 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true }, "has-property-descriptors": { @@ -25519,9 +25523,9 @@ "dev": true }, "nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "dev": true }, "natural-compare": { @@ -25879,12 +25883,12 @@ } }, "postcss": { - "version": "8.4.29", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.29.tgz", - "integrity": "sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==", + "version": "8.4.33", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", + "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==", "dev": true, "requires": { - "nanoid": "^3.3.6", + "nanoid": "^3.3.7", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } @@ -27889,9 +27893,9 @@ } }, "tsconfig-paths": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", - "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", "dev": true, "requires": { "@types/json5": "^0.0.29", @@ -28373,9 +28377,9 @@ "dev": true }, "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true }, "wrap-ansi": { diff --git a/package.json b/package.json index 0312eab6..49f5a41c 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "eslint-config-prettier": "^8.10.0", "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-cypress": "^2.12.1", - "eslint-plugin-import": "^2.28.1", + "eslint-plugin-import": "^2.29.1", "eslint-plugin-react": "^7.31.10", "eslint-plugin-react-hooks": "^4.6.0", "jest": "^29.6.2",