From 26952eab9a2b0b57904cec01a9bb36bb172eca04 Mon Sep 17 00:00:00 2001 From: Sean Date: Fri, 29 Mar 2024 14:30:43 -0700 Subject: [PATCH 01/34] feedback API --- src/lib/api/FeedbackAPI.ts | 82 ++++++++++++++++++++++++++++++++++++++ src/lib/api/index.ts | 1 + src/lib/config.ts | 1 + 3 files changed, 84 insertions(+) create mode 100644 src/lib/api/FeedbackAPI.ts diff --git a/src/lib/api/FeedbackAPI.ts b/src/lib/api/FeedbackAPI.ts new file mode 100644 index 00000000..088adefd --- /dev/null +++ b/src/lib/api/FeedbackAPI.ts @@ -0,0 +1,82 @@ +import config from '@/lib/config'; +import { SubmitFeedbackRequest, UpdateFeedbackStatusRequest } from '@/lib/types/apiRequests'; +import { + GetFeedbackResponse, + PublicFeedback, + SubmitFeedbackResponse, + UpdateFeedbackStatusResponse, +} from '@/lib/types/apiResponses'; +import { FeedbackStatus, FeedbackType } from '@/lib/types/enums'; +import axios from 'axios'; + +/** + * Submit feedback + * @param token Bearer token. Authenticated user must strictly be in the + * `ACTIVE` state (not `PASSWORD_RESET`) + * @param title Title of feedback + * @param description Description of feedback. Must be at least 100 characters + * long. + * @param type Type of ACM offering that the feedback is addressed to. + * @returns The submitted feedback + */ +export const addFeedback = async ( + token: string, + title: string, + description: string, + type: FeedbackType +): Promise => { + const requestUrl = `${config.api.baseUrl}${config.api.endpoints.feedback}`; + + const requestBody: SubmitFeedbackRequest = { feedback: { title, description, type } }; + + const response = await axios.post(requestUrl, requestBody, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + + return response.data.feedback; +}; + +/** + * Get all feedback submitted by user, or by all users if current user is an + * admin. + * @param token Bearer token + * @returns List of submitted feedback + */ +export const getFeedback = async (token: string): Promise => { + const requestUrl = `${config.api.baseUrl}${config.api.endpoints.feedback}`; + + const response = await axios.get(requestUrl, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + + return response.data.feedback; +}; + +/** + * Set status (e.g. acknowledged, ignored) of feedback + * @param token Bearer token + * @param uuid Feedback ID + * @param status Status to set feedback to + * @returns The updated feedback object + */ +export const respondToFeedback = async ( + token: string, + uuid: string, + status: FeedbackStatus +): Promise => { + const requestUrl = `${config.api.baseUrl}${config.api.endpoints.feedback}/${uuid}`; + + const requestBody: UpdateFeedbackStatusRequest = { status }; + + const response = await axios.post(requestUrl, requestBody, { + headers: { + Authorization: `Bearer ${token}`, + }, + }); + + return response.data.feedback; +}; diff --git a/src/lib/api/index.ts b/src/lib/api/index.ts index d0d44c46..91b6d28e 100644 --- a/src/lib/api/index.ts +++ b/src/lib/api/index.ts @@ -1,5 +1,6 @@ export * as AuthAPI from './AuthAPI'; export * as EventAPI from './EventAPI'; +export * as FeedbackAPI from './FeedbackAPI'; export * as KlefkiAPI from './KlefkiAPI'; export * as LeaderboardAPI from './LeaderboardAPI'; export * as ResumeAPI from './ResumeAPI'; diff --git a/src/lib/config.ts b/src/lib/config.ts index eb8031c9..71a5249d 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -37,6 +37,7 @@ const config = { attendance: '/attendance', forUserByUUID: '/attendance/user', }, + feedback: '/feedback', leaderboard: '/leaderboard', store: { collection: '/merch/collection', From fe1ffcb06538b5f728812f9f1b89a5d005531cce Mon Sep 17 00:00:00 2001 From: Sean Date: Fri, 29 Mar 2024 21:28:57 -0700 Subject: [PATCH 02/34] Feedback form --- src/components/events/FeedbackForm/index.tsx | 72 +++++++++++++++++++ .../events/FeedbackForm/style.module.scss | 20 ++++++ .../FeedbackForm/style.module.scss.d.ts | 10 +++ src/lib/utils.ts | 21 ++++-- 4 files changed, 118 insertions(+), 5 deletions(-) create mode 100644 src/components/events/FeedbackForm/index.tsx create mode 100644 src/components/events/FeedbackForm/style.module.scss create mode 100644 src/components/events/FeedbackForm/style.module.scss.d.ts diff --git a/src/components/events/FeedbackForm/index.tsx b/src/components/events/FeedbackForm/index.tsx new file mode 100644 index 00000000..9cdaa46b --- /dev/null +++ b/src/components/events/FeedbackForm/index.tsx @@ -0,0 +1,72 @@ +import { Dropdown, Typography } from '@/components/common'; +import { FeedbackAPI } from '@/lib/api'; +import { FeedbackType } from '@/lib/types/enums'; +import { isEnum } from '@/lib/utils'; +import { useState } from 'react'; +import styles from './style.module.scss'; + +const feedbackTypeNames: Record = { + GENERAL: 'ACM', + MERCH_STORE: 'Store', + BIT_BYTE: 'Bit-Byte Program', + AI: 'ACM AI', + CYBER: 'ACM Cyber', + DESIGN: 'ACM Design', + HACK: 'ACM Hack', + INNOVATE: 'ACM Innovate', +}; + +interface FeedbackFormProps { + authToken: string; +} + +const FeedbackForm = ({ authToken }: FeedbackFormProps) => { + const [title, setTitle] = useState(''); + const [description, setDescription] = useState(''); + const [type, setType] = useState(FeedbackType.GENERAL); + + return ( +
{ + e.preventDefault(); + FeedbackAPI.addFeedback(authToken, title, description, FeedbackType.GENERAL); + }} + > + Feedback +

+ Feel free to give event suggestions, friendly words, constructive crisitism, or just say + what’s on your mind! +

+ setTitle(e.currentTarget.value)} + className={styles.field} + /> +