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 ? (
+
+
+
+
+ <>
+ {!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 ? (
+
+
+
+
+ Name |
+ Profile |
+
+
+
+ {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 (
+
+ {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",