From e74d7c2cae63827c1df6bb59a0ee8ef0e54bfaef Mon Sep 17 00:00:00 2001 From: Rohan Ramakrishnan <65002749+14r14@users.noreply.github.com> Date: Thu, 7 Nov 2024 21:33:40 -0800 Subject: [PATCH] Hacker Profile (#533) * made project * test commit + push * fixing merge * modal mostly done * implement update email functionality * implement update email functionality * edit password + other text-based fields * remove test file * test commit + push * errors with components * Finish prelim profile-pic upload * Finish prelim profile-pic upload * Finish prelim profile-pic upload * modal done * actually finished modal * Upload resume to supabase storage * refactored code into component * adding package.json * basically done with profile * done with profile frontend except for spacing * bug fixes * updates according to figma * inter * stashing yarn.lock * done with hacker profile * Update layout and fix profile pic api * Update inner layout padding * Add loading placeholder, fix sidebar link --------- Co-authored-by: saniarashidd Co-authored-by: Jiong Yan Yap --- apps/dashboard/common/constants.ts | 3 +- .../components/battlepass/battlepass-page.tsx | 2 +- .../hacker-profile/edit-profile-card.tsx | 473 ++++++++++++++++++ apps/dashboard/components/svg/star-4.tsx | 22 + apps/dashboard/components/svg/star-5.tsx | 22 + apps/dashboard/layouts/themeless-layout.tsx | 6 +- apps/dashboard/pages/_app.tsx | 1 + .../hacker-profile/change-password/index.ts | 26 + .../pages/api/hacker-profile/index.ts | 61 +++ .../profile-picture/get-url/[userId].ts | 31 ++ .../profile-picture/upload/[userId].ts | 101 ++++ .../resume/get-resume/[userId].ts | 32 ++ .../resume/upload/[userId].ts} | 41 +- .../hacker-profile/update-profile/index.ts | 106 ++++ apps/dashboard/pages/hacker-profile/index.tsx | 349 +++++++++++++ .../attendee-details-scan/index.tsx | 2 +- .../attendee-details/index.tsx | 2 +- .../attendee-event-scan/index.tsx | 2 +- .../identity-portal/event-checkin/index.tsx | 2 +- apps/dashboard/tsconfig.json | 3 +- libs/hackform-client/src/index.ts | 1 + libs/hackform-client/src/lib/pfp-client.ts | 81 +++ libs/hackform-client/src/lib/resume-client.ts | 110 ++-- package.json | 3 +- yarn.lock | 36 +- 25 files changed, 1440 insertions(+), 78 deletions(-) create mode 100644 apps/dashboard/components/hacker-profile/edit-profile-card.tsx create mode 100644 apps/dashboard/components/svg/star-4.tsx create mode 100644 apps/dashboard/components/svg/star-5.tsx create mode 100644 apps/dashboard/pages/api/hacker-profile/change-password/index.ts create mode 100644 apps/dashboard/pages/api/hacker-profile/index.ts create mode 100644 apps/dashboard/pages/api/hacker-profile/profile-picture/get-url/[userId].ts create mode 100644 apps/dashboard/pages/api/hacker-profile/profile-picture/upload/[userId].ts create mode 100644 apps/dashboard/pages/api/hacker-profile/resume/get-resume/[userId].ts rename apps/dashboard/pages/api/{resume.ts => hacker-profile/resume/upload/[userId].ts} (72%) create mode 100644 apps/dashboard/pages/api/hacker-profile/update-profile/index.ts create mode 100644 apps/dashboard/pages/hacker-profile/index.tsx create mode 100644 libs/hackform-client/src/lib/pfp-client.ts diff --git a/apps/dashboard/common/constants.ts b/apps/dashboard/common/constants.ts index 2c1ab153..b903d810 100644 --- a/apps/dashboard/common/constants.ts +++ b/apps/dashboard/common/constants.ts @@ -1,7 +1,8 @@ import { Option } from '@hibiscus/types'; import { ApplicationStatus } from '@hibiscus/types'; -export const ALLOWED_RESUME_FORMATS = ['pdf', 'docx', 'txt', 'odt']; +export const ALLOWED_RESUME_FORMATS = ['pdf']; +export const ALLOWED_PFP_FORMATS = ['jpeg']; export const DEFAULT_OTHERS_FIELD_LABEL = 'Others'; export const hackformLinks = { diff --git a/apps/dashboard/components/battlepass/battlepass-page.tsx b/apps/dashboard/components/battlepass/battlepass-page.tsx index 16507eeb..8f3f8696 100644 --- a/apps/dashboard/components/battlepass/battlepass-page.tsx +++ b/apps/dashboard/components/battlepass/battlepass-page.tsx @@ -49,7 +49,7 @@ function BattlepassPage() { }, []); return ( -
+
{/* content */}
{/* left column */} diff --git a/apps/dashboard/components/hacker-profile/edit-profile-card.tsx b/apps/dashboard/components/hacker-profile/edit-profile-card.tsx new file mode 100644 index 00000000..3b6b5cba --- /dev/null +++ b/apps/dashboard/components/hacker-profile/edit-profile-card.tsx @@ -0,0 +1,473 @@ +import styled from 'styled-components'; +import Select, { StylesConfig } from 'react-select'; +import { IoMdClose } from 'react-icons/io'; + +import { SCHOOLS } from '../../common/schools'; + +import * as Yup from 'yup'; +import { useFormik } from 'formik'; +import { useEffect, useState } from 'react'; + +const EditProfileCard = ({ onClose }) => { + const options = [ + { value: 'fall 2024', label: 'Fall 2024' }, + { value: 'spring 2025', label: 'Spring 2025' }, + { value: 'fall 2025', label: 'Fall 2025' }, + { value: 'spring 2026', label: 'Spring 2026' }, + { value: 'fall 2026', label: 'Fall 2026' }, + { value: 'spring 2027', label: 'Spring 2027' }, + { value: 'fall 2027', label: 'Fall 2027' }, + { value: 'spring 2028', label: 'Spring 2028' }, + { value: 'fall 2028', label: 'Fall 2028' }, + { value: 'spring 2029', label: 'Spring 2029' }, + { value: 'fall 2029', label: 'Fall 2029' }, + { value: 'spring 2030', label: 'Spring 2030' }, + ]; + + const schoolOptions = SCHOOLS.map((school) => ({ + value: school, + label: school, + })); + + const [name, setName] = useState(''); + const [username, setUsername] = useState(''); + const [fieldOfStudy, setFieldOfStudy] = useState(''); + const [profileBio, setProfileBio] = useState(''); + const [school, setSchool] = useState(''); + const [graduationYear, setGraduationYear] = useState(''); + + useEffect(() => { + const fetchData = async () => { + const response = await fetch('/api/hacker-profile'); + let data = await response.json(); + data = data.data; + setName(data.first_name + ' ' + data.last_name); + setUsername(data.username || ''); + setFieldOfStudy(data.major || ''); + setProfileBio(data.bio || ''); + setSchool(data.school || ''); + setGraduationYear(data.graduation_year || ''); + formik.setFieldValue('name', data.first_name + ' ' + data.last_name); + formik.setFieldValue('username', data.username); + formik.setFieldValue('fieldOfStudy', data.major); + formik.setFieldValue('profileBio', data.bio); + formik.setFieldValue('school', data.school); + formik.setFieldValue('graduationYear', data.graduation_year); + }; + + fetchData(); + }, []); + + const updatePasswordHandler = (password) => { + fetch('/api/hacker-profile/change-password', { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ password: password }), + }) + .then((res) => res.json()) + .then((data) => { + if (data.message === 'Success') { + onClose(); + } else { + alert('There was an error updating your password'); + } + }); + }; + + const formik = useFormik({ + initialValues: { + name: '', + username: '', + fieldOfStudy: '', + password: '', + profileBio: '', + school: '', + graduationYear: '', + }, + onSubmit: async (values, formikHelpers) => { + formikHelpers.setSubmitting(true); + const update = {}; + + if (values.fieldOfStudy === null || values.fieldOfStudy == undefined) + values.fieldOfStudy = ''; + if (values.profileBio === null || values.fieldOfStudy == undefined) + values.profileBio = ''; + if (values.username === null || values.username == undefined) + values.username = ''; + if (values.graduationYear === null || values.graduationYear == undefined) + values.graduationYear = ''; + if (values.school === null || values.school == undefined) + values.school = ''; + + if (values.name !== name) { + update['newName'] = values.name; + } + if (values.fieldOfStudy != fieldOfStudy) { + update['newFieldOfStudy'] = values.fieldOfStudy; + } + if (values.profileBio != profileBio) { + update['newBio'] = values.profileBio; + } + if (values.username != username) { + update['newUsername'] = values.username; + } + if (values.graduationYear != graduationYear) { + update['newGradYear'] = values.graduationYear; + } + if (values.school != school) { + update['newSchool'] = values.school; + } + + if (values.password !== '') { + updatePasswordHandler(values.password); + } + + if (Object.keys(update).length > 0) { + fetch('/api/hacker-profile/update-profile', { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(update), + }) + .then((res) => res.json()) + .then((data) => { + if (data.message === 'Success') { + onClose(); + } else { + alert('There was an error updating your profile'); + } + }); + } + formikHelpers.setSubmitting(false); + window.location.reload(); + }, + }); + + return ( + + + {/*close x button + */} + + { + e.preventDefault(); + formik.handleSubmit(); + }} + type="submit" + > + Save + + + + Name + + + + + Username + + + + + + + Field of Study + + + + + Profile Bio + + + {/* + Password + + */} + + + + + School + { + formik.setFieldValue('graduationYear', e.value); + }} + menuPlacement="auto" + menuPortalTarget={document.body} + menuPosition={'fixed'} + menuShouldScrollIntoView={true} + styles={{ + control: (baseStyles, state) => ({ + ...baseStyles, + + backgroundColor: '#f5f5f5', + borderBottom: 'solid #7E7E7E 2px', + borderTop: 'none', + borderLeft: 'none', + borderRight: 'none', + width: '28vh', + borderRadius: '8px', + }), + menuPortal: (base) => ({ ...base, zIndex: 9999 }), + + menu: (baseStyles, state) => ({ + ...baseStyles, + color: 'black', + borderRadius: '8px', + zIndex: 9999, + }), + menuList: (baseStyles, state) => ({ + ...baseStyles, + maxHeight: '115px', + overflowY: 'auto', + backgroundColor: '#f5f5f5', + borderRadius: '8px', + zIndex: 9999, + }), + option: (baseStyles, state) => ({ + ...baseStyles, + backgroundColor: state.isFocused ? '#FF9966' : '#f5f5f5', // Change hover color + color: 'black', + cursor: 'pointer', + }), + }} + /> + + + + + {/* + Graduation Year + + + + + + {name} + + {!isEditing && ( + setIsEditing(true)}>Edit + )} + + + + + + {school} + + + + {fieldOfStudy} + + + + + {graduationYear && + graduationYear[0].toUpperCase() + graduationYear.slice(1)} + + + + + +
+ + About Me + {profileBio} + + Resume + {resume && ( + + View Resume + + )} + document.getElementById('resumeUpload').click()} + > + {' '} + Upload Resume + + + + + + + + ); +} + +export default Index; + +const ProfileImageContainer = styled.div` + width: 150px; + height: 150px; + border-radius: 50%; + overflow: hidden; + border: 1px solid black; + background-color: #d9d9d9; + position: absolute; + left: 50px; + margin-right: 50px; +`; + +const ProfileImage = styled.img` + width: 100%; + height: 100%; + object-fit: cover; +`; + +const EditIconButton = styled.button` + position: absolute; + bottom: 10px; + right: 10px; + background-color: white; + border-radius: 50%; + width: 40px; + height: 40px; + display: flex; + justify-content: center; + align-items: center; + border: 1px solid black; + cursor: pointer; + + label { + cursor: pointer; + } +`; + +const BasicInfoContainer = styled.div``; + +const UserName = styled.h2` + font-size: 38px; + margin-bottom: 30px; + color: black; + font-weight: bold; + margin-left: 0; + padding-right: 30px; + text-wrap: nowrap; +`; + +const InfoRow = styled.div` + display: flex; + justify-content: space-between; + width: 100%; + padding-bottom: 30px; +`; + +const InfoItem = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; + margin-right: 40px; +`; + +const Label = styled.span` + color: #fe6447; + font-weight: bold; +`; + +const InfoText = styled.span` + color: black; +`; + +const DividerLine = styled.hr` + width: 85%; + border: 10; + height: 10px; + color: #989898; + margin: 20px 0; + z-index: 10; +`; + +const AboutContainer = styled.div` + margin-bottom: 20px; +`; + +const TextBox = styled.div``; + +const Star4Container = styled.div` + position: absolute; + bottom: 0; + right: 0px; + width: 100%; + height: 250px; + overflow: hidden; + display: flex; + justify-content: flex-end; + z-index: 0; +`; + +const Header3 = styled.h3` + font-size: 1.5rem; + margin-bottom: 5px; +`; + +const Body = styled.div` + width: 100%; + display: flex; + justify-content: center; + align-items: flex-start; + flex-direction: column; + background-color: #f5f5f5; + + padding-left: 250px; +`; + +const LayoutContainer = styled.div` + width: 100%; + position: relative; + display: flex; + justify-content: flex-start; + align-items: stretch; + height: 100%; + background-color: #f5f5f5; + flex-direction: column; + padding: 40px; +`; + +const ProfileBox = styled.div` + display: flex; + + justify-content: space-between; + align-items: center; + + background-color: #f5f5f5; + border-radius: 10px; + width: 50%; +`; + +const ProfileText = styled.h2` + margin-right: 20px; + font-weight: bold; +`; + +const EditButton = styled.button` + padding: 10px 25px; + margin-top: 0; + background-color: transparent; + color: black; + border: 1px solid black; + border-radius: 10px; + z-index: 2; + cursor: pointer; + float: right; + margin-bottom: 13px; +`; + +const ResumeButton = styled.button` + padding: 10px 20px; + margin-top: 10px; + background-color: #ff6347; + color: black; + border: 1px solid black; + border-radius: 10px; + cursor: pointer; + z-index: 1; +`; diff --git a/apps/dashboard/pages/identity-portal/attendee-details-scan/index.tsx b/apps/dashboard/pages/identity-portal/attendee-details-scan/index.tsx index 325bbb94..3a724c32 100644 --- a/apps/dashboard/pages/identity-portal/attendee-details-scan/index.tsx +++ b/apps/dashboard/pages/identity-portal/attendee-details-scan/index.tsx @@ -23,7 +23,7 @@ export function Index() { } return ( -
+

Search for hackers!

diff --git a/apps/dashboard/pages/identity-portal/attendee-details/index.tsx b/apps/dashboard/pages/identity-portal/attendee-details/index.tsx index 3f07900b..cda09bef 100644 --- a/apps/dashboard/pages/identity-portal/attendee-details/index.tsx +++ b/apps/dashboard/pages/identity-portal/attendee-details/index.tsx @@ -173,7 +173,7 @@ export function Index() { return ( <> -
+

{user.first_name} {user.last_name}

diff --git a/apps/dashboard/pages/identity-portal/attendee-event-scan/index.tsx b/apps/dashboard/pages/identity-portal/attendee-event-scan/index.tsx index b19e7313..c31d2ddb 100644 --- a/apps/dashboard/pages/identity-portal/attendee-event-scan/index.tsx +++ b/apps/dashboard/pages/identity-portal/attendee-event-scan/index.tsx @@ -234,7 +234,7 @@ export function Index() { return ( <> -
+

Scan attendee in for {eventName}

diff --git a/apps/dashboard/pages/identity-portal/event-checkin/index.tsx b/apps/dashboard/pages/identity-portal/event-checkin/index.tsx index 625db32a..20089acf 100644 --- a/apps/dashboard/pages/identity-portal/event-checkin/index.tsx +++ b/apps/dashboard/pages/identity-portal/event-checkin/index.tsx @@ -41,7 +41,7 @@ export function Index() { return ( <> -
+

Select an event!

diff --git a/apps/dashboard/tsconfig.json b/apps/dashboard/tsconfig.json index c03f05f4..0724c105 100644 --- a/apps/dashboard/tsconfig.json +++ b/apps/dashboard/tsconfig.json @@ -11,6 +11,7 @@ "resolveJsonModule": true, "isolatedModules": true, "incremental": true, + "types": ["jest", "node"] }, "include": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "next-env.d.ts"], @@ -24,7 +25,7 @@ ], "references": [ { - "path": "./.storybook/tsconfig.json" + "path": "./.storybook/tsconfig.json", } ] } diff --git a/libs/hackform-client/src/index.ts b/libs/hackform-client/src/index.ts index 2649dd05..e5a66aa6 100644 --- a/libs/hackform-client/src/index.ts +++ b/libs/hackform-client/src/index.ts @@ -1,2 +1,3 @@ export * from './lib/data-client'; export * from './lib/resume-client'; +export * from './lib/pfp-client'; \ No newline at end of file diff --git a/libs/hackform-client/src/lib/pfp-client.ts b/libs/hackform-client/src/lib/pfp-client.ts new file mode 100644 index 00000000..db86cc55 --- /dev/null +++ b/libs/hackform-client/src/lib/pfp-client.ts @@ -0,0 +1,81 @@ +import { injectable, container } from 'tsyringe'; +import { HibiscusSupabaseClient } from '@hibiscus/hibiscus-supabase-client'; +import { StorageError } from '@supabase/storage-js'; + +interface ProfilePictureResponse { + path: string; + success: boolean; +} + +@injectable() +export class HackFromProfilePictureClient { + private readonly supabaseClient: HibiscusSupabaseClient; + private readonly bucket: string = 'profile-pictures'; + + constructor() { + this.supabaseClient = container.resolve(HibiscusSupabaseClient); + } + + public async uploadPfp( + userId: string, + file: Buffer, + mimeType: string + ): Promise { + const blob = new Blob([file], { type: mimeType }); + const { data, error } = await this.supabaseClient + .getClient() + .storage.from(this.bucket) + .upload(`${userId}/pfp.${mimeType.split("/")[1]}`, blob); + if (error) { + throw new StorageError(error.message); + } + + return { + path: data.path, + success: true, + }; + } + + public async updatePfp( + userId: string, + file: Buffer, + mimeType: string + ): Promise { + const blob = new Blob([file], { type: mimeType }); + const { data, error } = await this.supabaseClient + .getClient() + .storage.from(this.bucket) + .update(`${userId}/pfp.${mimeType.split("/")[1]}`, blob); + if (error) { + throw new StorageError(error.message); + } + + return { + path: data.path, + success: true, + }; + } + + public async pfpExists(userId: string): Promise { + const { data, error } = await this.supabaseClient + .getClient() + .storage.from(this.bucket) + .list(`${userId}/`); + + if (error) { + console.log(error); + throw new StorageError(error.message); + } + + return data.length > 0; + } + + public async getPfpUrl(userId: string): Promise { + const { data } = await this.supabaseClient + .getClient() + .storage.from(this.bucket) + .getPublicUrl(`${userId}/pfp.jpeg`); + + return data.publicUrl; + } +} diff --git a/libs/hackform-client/src/lib/resume-client.ts b/libs/hackform-client/src/lib/resume-client.ts index 380a1e63..02f7cd98 100644 --- a/libs/hackform-client/src/lib/resume-client.ts +++ b/libs/hackform-client/src/lib/resume-client.ts @@ -1,59 +1,89 @@ -import { - GetObjectCommand, - PutObjectCommand, - S3Client, -} from '@aws-sdk/client-s3'; -import { injectable } from 'tsyringe'; -import { credentials, region } from './aws'; +import { injectable, container } from 'tsyringe'; +import { HibiscusSupabaseClient } from '@hibiscus/hibiscus-supabase-client'; +import { StorageError } from '@supabase/storage-js'; + +interface ResumeResponse { + path: string; + success: boolean; +} @injectable() export class HackformResumeUploadClient { - private readonly s3: S3Client; - private readonly bucket = '2023-hacker-resumes'; + private readonly supabaseClient: HibiscusSupabaseClient; + private readonly bucket: string = 'resume'; constructor() { - this.s3 = new S3Client({ - region, - credentials, - }); + this.supabaseClient = container.resolve(HibiscusSupabaseClient); } /** * Upload hacker resume file with given key * @param buf bytes Buffer object data representing the hacker resume's upload - * @param key identifier for this resume file (must be unique) - * @returns status code from the operation, attempts taken, and request ID - * to the ext service + * @param userId userId of user that is uploading the resume + * @returns ResumeResponse object containing the path of the uploaded resume and a boolean indicating success + */ + async uploadResume(buf: Buffer, userId: string): Promise { + const blob = new Blob([buf], { type: 'application/pdf' }); + const { data, error } = await this.supabaseClient + .getClient() + .storage.from(this.bucket) + .upload(`${userId}/resume.pdf`, blob); + if (error) { + throw new StorageError(error.message); + } + + return { + path: data.path, + success: true, + }; + } + + /** + * Update hacker resume file with given key + * @param buf bytes Buffer object data representing the hacker resume's upload + * @param userId userId of user that is uploading the resume + * @returns ResumeResponse object containing the path of the uploaded resume and a boolean indicating success */ - async uploadResume( - buf: Buffer, - key: string - ): Promise<{ httpStatusCode: number; attempts: number; requestId: string }> { - const { $metadata } = await this.s3.send( - new PutObjectCommand({ - Bucket: this.bucket, - Key: key, - Body: buf, - }) - ); + async updateResume(buf: Buffer, userId: string): Promise { + const blob = new Blob([buf], { type: 'application/pdf' }); + const { data, error } = await this.supabaseClient + .getClient() + .storage.from(this.bucket) + .update(`${userId}/resume.pdf`, blob); + if (error) { + throw new StorageError(error.message); + } + return { - httpStatusCode: $metadata.httpStatusCode, - attempts: $metadata.attempts, - requestId: $metadata.requestId, + path: data.path, + success: true, }; } - async getResumeMetadata(key: string) { - const result = await this.s3.send( - new GetObjectCommand({ - Bucket: this.bucket, - Key: key, - }) - ); - return result.$metadata; + /** + * Check if a resume exists for a given user + * @param userId userId of user to check for resume + * @returns boolean indicating if a resume exists for the given user + */ + async resumeExists(userId: string): Promise { + const { data, error } = await this.supabaseClient + .getClient() + .storage.from(this.bucket) + .list(`${userId}/`); + + if (error) { + throw new StorageError(error.message); + } + + return data.length > 0; } - async getResumePresignedUrl(key: string): Promise { - return ''; + async getResumeUrl(userId: string): Promise { + const { data } = await this.supabaseClient + .getClient() + .storage.from(this.bucket) + .getPublicUrl(`${userId}/resume.pdf`); + + return data.publicUrl; } } diff --git a/package.json b/package.json index f385d244..8b9abb64 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,8 @@ "react-redux": "^8.0.5", "react-resize-detector": "^8.0.2", "react-responsive": "^9.0.2", - "react-select": "^5.8.2", + "react-select": "^5.8.1", + "react-sortable-hoc": "^2.0.0", "react-spinners": "^0.13.4", "react-transition-group": "^4.4.5", "reflect-metadata": "^0.1.13", diff --git a/yarn.lock b/yarn.lock index a0fa9b80..4609e7df 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3248,6 +3248,13 @@ dependencies: regenerator-runtime "^0.14.0" +"@babel/runtime@^7.2.0": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.25.9.tgz#65884fd6dc255a775402cc1d9811082918f4bf00" + integrity sha512-4zpTHZ9Cm6L9L+uIqghQX8ZXg8HKFcjYO3qHoO8zTmRm6HQUJ8SSJ+KRvbMBZn0EGVlT4DRYeQ/6hjlyXBh+Kg== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/runtime@^7.8.7": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.15.tgz#38f46494ccf6cf020bd4eed7124b425e83e523b8" @@ -10867,7 +10874,7 @@ babel-plugin-macros@^2.8.0: babel-plugin-macros@^3.0.1, babel-plugin-macros@^3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1" integrity sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg== dependencies: "@babel/runtime" "^7.12.5" @@ -12534,7 +12541,7 @@ content-type@~1.0.4: convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.9.0" - resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== convert-source-map@^2.0.0: @@ -17280,9 +17287,9 @@ into-stream@^3.1.0: from2 "^2.1.1" p-is-promise "^1.1.0" -invariant@2.2.4: +invariant@2.2.4, invariant@^2.2.4: version "2.2.4" - resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== dependencies: loose-envify "^1.0.0" @@ -23068,7 +23075,7 @@ prompts@^2.0.1, prompts@^2.4.0: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.0.0, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: +prop-types@^15.0.0, prop-types@^15.5.7, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -23522,10 +23529,10 @@ react-responsive@^9.0.2: prop-types "^15.6.1" shallow-equal "^1.2.1" -react-select@^5.8.2: - version "5.8.2" - resolved "https://registry.yarnpkg.com/react-select/-/react-select-5.8.2.tgz#0d7ccb1895d61aafcd090fbf65aa9e506225a854" - integrity sha512-a/LkOckoI62710gGPQSQqUp7A10fGbH/ya3/IR49qaq3XoBvwymgD5mJgtiHxBDsutyEQfdKNycWVh8Cg8UCjw== +react-select@^5.8.1: + version "5.8.1" + resolved "https://registry.yarnpkg.com/react-select/-/react-select-5.8.1.tgz#3284a93b7633b5e893306b2a8007ea0f793e62b9" + integrity sha512-RT1CJmuc+ejqm5MPgzyZujqDskdvB9a9ZqrdnVLsvAHjJ3Tj0hELnLeVPQlmYdVKCdCpxanepl6z7R5KhXhWzg== dependencies: "@babel/runtime" "^7.12.0" "@emotion/cache" "^11.4.0" @@ -23545,6 +23552,15 @@ react-shallow-renderer@^16.15.0: object-assign "^4.1.1" react-is "^16.12.0 || ^17.0.0 || ^18.0.0" +react-sortable-hoc@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/react-sortable-hoc/-/react-sortable-hoc-2.0.0.tgz#f6780d8aa4b922a21f3e754af542f032677078b7" + integrity sha512-JZUw7hBsAHXK7PTyErJyI7SopSBFRcFHDjWW5SWjcugY0i6iH7f+eJkY8cJmGMlZ1C9xz1J3Vjz0plFpavVeRg== + dependencies: + "@babel/runtime" "^7.2.0" + invariant "^2.2.4" + prop-types "^15.5.7" + react-spinners@^0.13.4: version "0.13.8" resolved "https://registry.npmjs.org/react-spinners/-/react-spinners-0.13.8.tgz" @@ -25380,7 +25396,7 @@ source-map@0.7.3: source-map@^0.5.0, source-map@^0.5.6, source-map@^0.5.7: version "0.5.7" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== source-map@^0.7.3, source-map@^0.7.4: