From d3b12bc0ac9ee312bb584e8e31474b7d7ee0cc14 Mon Sep 17 00:00:00 2001 From: Himir Desai <76738299+Himir-Desai@users.noreply.github.com> Date: Wed, 11 Feb 2026 10:49:20 -0800 Subject: [PATCH 01/24] Live Server Setup --- backend/package-lock.json | 38 ------------- frontend/components/Button.module.css | 21 +++++++ frontend/components/Button.tsx | 38 +++++++++++++ frontend/components/Modal.module.css | 32 +++++++++++ frontend/components/Modal.tsx | 15 +++++ frontend/src/api/user.ts | 73 +++++++++++++++++++++++++ frontend/src/app/(pages)/staff/page.tsx | 6 +- 7 files changed, 184 insertions(+), 39 deletions(-) create mode 100644 frontend/components/Button.module.css create mode 100644 frontend/components/Button.tsx create mode 100644 frontend/components/Modal.module.css create mode 100644 frontend/components/Modal.tsx create mode 100644 frontend/src/api/user.ts diff --git a/backend/package-lock.json b/backend/package-lock.json index 748c42f..37a4d28 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -7482,44 +7482,6 @@ "node": ">=12.0.0" } }, - "node_modules/proto3-json-serializer": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/proto3-json-serializer/-/proto3-json-serializer-2.0.2.tgz", - "integrity": "sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==", - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "protobufjs": "^7.2.5" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/protobufjs": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", - "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", - "hasInstallScript": true, - "license": "BSD-3-Clause", - "optional": true, - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/node": ">=13.7.0", - "long": "^5.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", diff --git a/frontend/components/Button.module.css b/frontend/components/Button.module.css new file mode 100644 index 0000000..e2809b5 --- /dev/null +++ b/frontend/components/Button.module.css @@ -0,0 +1,21 @@ +.button { + width: auto; + height: 2.25rem; + padding: 0 0.75rem; + border-radius: 0.5rem; + font: var(--font-label); + cursor: pointer; + border: 1px; + border-color: black; + border-style: solid; +} + +.primary { + background-color: #3f8d7c; + color: white; + border: none; +} + +.secondary { + border: 1px #cdcfd0 solid; +} diff --git a/frontend/components/Button.tsx b/frontend/components/Button.tsx new file mode 100644 index 0000000..04047cf --- /dev/null +++ b/frontend/components/Button.tsx @@ -0,0 +1,38 @@ +/** + * A reusable Button component with customizable label and style. + */ + +import React from "react"; + +import styles from "./Button.module.css"; + +export type ButtonProps = { + label: string; + kind: string; +} & React.ComponentProps<"button">; + +export const Button = function Button({ + ref, + label, + className, + kind = "primary", + ...props +}: ButtonProps & { ref?: React.RefObject }) { + let buttonClass = styles.button; + switch (kind) { + case "primary": + buttonClass += ` ${styles.primary}`; + break; + case "secondary": + buttonClass += ` ${styles.secondary}`; + break; + } + if (className) { + buttonClass += ` ${className}`; + } + return ( + + ); +}; diff --git a/frontend/components/Modal.module.css b/frontend/components/Modal.module.css new file mode 100644 index 0000000..18e650c --- /dev/null +++ b/frontend/components/Modal.module.css @@ -0,0 +1,32 @@ +.overlay { + position: fixed; + inset: 0; + z-index: 5; + + display: flex; + align-items: center; + justify-content: center; + + background: rgba(0, 0, 0, 0.25); +} + +.wrapper { + display: flex; + flex-direction: row; + + position: relative; /* sits inside overlay */ + width: 60%; + max-width: 700px; + + background: white; + border: 1px solid var(--grey-100); + border-radius: 12px; + padding: 24px; + + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.25); + + /* if the form content gets tall */ + max-height: 85vh; + overflow: visible; + justify-content: center; +} diff --git a/frontend/components/Modal.tsx b/frontend/components/Modal.tsx new file mode 100644 index 0000000..6321650 --- /dev/null +++ b/frontend/components/Modal.tsx @@ -0,0 +1,15 @@ +import type { ReactNode } from "react"; + +type ModalProps = { + child: ReactNode; + onExit: () => void; +}; +export const Modal = function Modal({ child, onExit }: ModalProps) { + return ( +
+
e.stopPropagation()}> + {child} +
+
+ ); +}; diff --git a/frontend/src/api/user.ts b/frontend/src/api/user.ts new file mode 100644 index 0000000..1dab706 --- /dev/null +++ b/frontend/src/api/user.ts @@ -0,0 +1,73 @@ +import { handleAPIError, post } from "./requests"; + +import type { APIResult } from "@/src/api/requests"; + +export type User = { + _id: string; + + // Core info + firstName: string; + lastName: string; + personalEmail: string; + meemliEmail: string; + admin: boolean; +}; + +export type UserJSON = { + _id: string; + + firstName: string; + lastName: string; + personalEmail: string; + meemliEmail: string; + admin: boolean; +}; + +const USER_ROUTE = "/api/user"; +const userByIdRoute = (id: string) => `${USER_ROUTE}/${id}`; + +function parseUser(user: UserJSON): User { + return { + _id: user._id, + + firstName: user.firstName, + lastName: user.lastName, + personalEmail: user.personalEmail, + meemliEmail: user.meemliEmail, + admin: user.admin, + }; +} + +type UserPayload = Omit; +export type CreateUserRequest = UserPayload; +export type UpdateUserRequest = User; + +export async function createUser(user: CreateUserRequest): Promise> { + try { + const response = await post(USER_ROUTE, user); + const json = (await response.json()) as UserJSON; + return { success: true, data: parseUser(json) }; + } catch (error) { + return handleAPIError(error); + } +} + +// export async function getStudent(id: string): Promise> { +// try { +// const response = await get(studentByIdRoute(id)); +// const json = (await response.json()) as StudentJSON; +// return { success: true, data: parseStudent(json) }; +// } catch (error) { +// return handleAPIError(error); +// } +// } + +// export async function updateStudent(student: UpdateStudentRequest): Promise> { +// try { +// const response = await put(studentByIdRoute(student._id), student); +// const json = (await response.json()) as StudentJSON; +// return { success: true, data: parseStudent(json) }; +// } catch (error) { +// return handleAPIError(error); +// } +// } diff --git a/frontend/src/app/(pages)/staff/page.tsx b/frontend/src/app/(pages)/staff/page.tsx index 7f1ab9b..cf12c5f 100644 --- a/frontend/src/app/(pages)/staff/page.tsx +++ b/frontend/src/app/(pages)/staff/page.tsx @@ -1,3 +1,7 @@ export default function Staff() { - return
This is Staff page
; + return ( +
+

Staff

+
+ ); } From 6d5a4736c9dc19a1a129cc623742817a3b26f94c Mon Sep 17 00:00:00 2001 From: OrigamiStarz <40199724+OrigamiStarz@users.noreply.github.com> Date: Wed, 11 Feb 2026 11:42:53 -0800 Subject: [PATCH 02/24] Pair programming stuff --- frontend/src/api/user.ts | 38 ++++++++--------- frontend/src/app/(pages)/staff/page.tsx | 26 +++++++++++- frontend/src/components/AddStaffForm.tsx | 34 +++++++++++++++ .../{ => src}/components/Button.module.css | 0 frontend/{ => src}/components/Button.tsx | 0 .../{ => src}/components/Modal.module.css | 0 frontend/{ => src}/components/Modal.tsx | 2 + .../src/components/ProgressBar.module.css | 17 ++++++++ frontend/src/components/ProgressBar.tsx | 18 ++++++++ frontend/src/components/TextField.module.css | 39 +++++++++++++++++ frontend/src/components/TextField.tsx | 42 +++++++++++++++++++ 11 files changed, 196 insertions(+), 20 deletions(-) create mode 100644 frontend/src/components/AddStaffForm.tsx rename frontend/{ => src}/components/Button.module.css (100%) rename frontend/{ => src}/components/Button.tsx (100%) rename frontend/{ => src}/components/Modal.module.css (100%) rename frontend/{ => src}/components/Modal.tsx (89%) create mode 100644 frontend/src/components/ProgressBar.module.css create mode 100644 frontend/src/components/ProgressBar.tsx create mode 100644 frontend/src/components/TextField.module.css create mode 100644 frontend/src/components/TextField.tsx diff --git a/frontend/src/api/user.ts b/frontend/src/api/user.ts index 1dab706..d0b2472 100644 --- a/frontend/src/api/user.ts +++ b/frontend/src/api/user.ts @@ -1,4 +1,4 @@ -import { handleAPIError, post } from "./requests"; +import { get, handleAPIError, post, put } from "./requests"; import type { APIResult } from "@/src/api/requests"; @@ -52,22 +52,22 @@ export async function createUser(user: CreateUserRequest): Promise> { -// try { -// const response = await get(studentByIdRoute(id)); -// const json = (await response.json()) as StudentJSON; -// return { success: true, data: parseStudent(json) }; -// } catch (error) { -// return handleAPIError(error); -// } -// } +export async function getUser(id: string): Promise> { + try { + const response = await get(userByIdRoute(id)); + const json = (await response.json()) as UserJSON; + return { success: true, data: parseUser(json) }; + } catch (error) { + return handleAPIError(error); + } +} -// export async function updateStudent(student: UpdateStudentRequest): Promise> { -// try { -// const response = await put(studentByIdRoute(student._id), student); -// const json = (await response.json()) as StudentJSON; -// return { success: true, data: parseStudent(json) }; -// } catch (error) { -// return handleAPIError(error); -// } -// } +export async function updateUser(user: UpdateUserRequest): Promise> { + try { + const response = await put(userByIdRoute(user._id), user); + const json = (await response.json()) as UserJSON; + return { success: true, data: parseUser(json) }; + } catch (error) { + return handleAPIError(error); + } +} diff --git a/frontend/src/app/(pages)/staff/page.tsx b/frontend/src/app/(pages)/staff/page.tsx index cf12c5f..1049952 100644 --- a/frontend/src/app/(pages)/staff/page.tsx +++ b/frontend/src/app/(pages)/staff/page.tsx @@ -1,7 +1,31 @@ +"use client"; + +import { useState } from "react"; + +import { AddStaffForm } from "@/src/components/AddStaffForm"; +import { Modal } from "@/src/components/Modal"; + +import { Button } from "@/src/components/Button"; + export default function Staff() { + const [addOpen, setAddOpen] = useState(false); + return (
-

Staff

+
+

Staff

+
); } diff --git a/frontend/src/components/AddStaffForm.tsx b/frontend/src/components/AddStaffForm.tsx new file mode 100644 index 0000000..70e6b56 --- /dev/null +++ b/frontend/src/components/AddStaffForm.tsx @@ -0,0 +1,34 @@ +// import styles from "./AddStaffForm.module.css"; + +import { TextField } from "./TextField"; +import { Button } from "./Button"; + + +// export type TextFieldProps = { +// label: string; +// error?: boolean; +// required?: boolean; +// } & Omit, "type">; + + +// type AddStaffFormProps = { + +// }; +// export const AddStaffForm = function AddStaffForm({ child, onExit }: ModalProps) { + + +// add the create user function +export const AddStaffForm = function AddStaffForm() { + return ( +
+

Add Staff

+ + + + + +
+ ); +}; diff --git a/frontend/components/Button.module.css b/frontend/src/components/Button.module.css similarity index 100% rename from frontend/components/Button.module.css rename to frontend/src/components/Button.module.css diff --git a/frontend/components/Button.tsx b/frontend/src/components/Button.tsx similarity index 100% rename from frontend/components/Button.tsx rename to frontend/src/components/Button.tsx diff --git a/frontend/components/Modal.module.css b/frontend/src/components/Modal.module.css similarity index 100% rename from frontend/components/Modal.module.css rename to frontend/src/components/Modal.module.css diff --git a/frontend/components/Modal.tsx b/frontend/src/components/Modal.tsx similarity index 89% rename from frontend/components/Modal.tsx rename to frontend/src/components/Modal.tsx index 6321650..0494058 100644 --- a/frontend/components/Modal.tsx +++ b/frontend/src/components/Modal.tsx @@ -1,3 +1,5 @@ +import styles from "./Modal.module.css"; + import type { ReactNode } from "react"; type ModalProps = { diff --git a/frontend/src/components/ProgressBar.module.css b/frontend/src/components/ProgressBar.module.css new file mode 100644 index 0000000..6073089 --- /dev/null +++ b/frontend/src/components/ProgressBar.module.css @@ -0,0 +1,17 @@ +/* Track (background) */ +.progress-track { + width: 100%; + height: 10px; + background: #e3e2e2; /* light gray */ + border-radius: 999px; + overflow: hidden; /* keeps fill rounded */ + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.08); +} + +/* Fill (the moving part) */ +.progress-bar { + height: 100%; + background: #3f8d7c; + border-radius: 4px; + transition: width 200ms ease; /* smooth animation */ +} \ No newline at end of file diff --git a/frontend/src/components/ProgressBar.tsx b/frontend/src/components/ProgressBar.tsx new file mode 100644 index 0000000..ee416e0 --- /dev/null +++ b/frontend/src/components/ProgressBar.tsx @@ -0,0 +1,18 @@ +/** + * ProgressBar component to display the progress of a multi-step form. + */ +import styles from "./ProgressBar.module.css"; +export const ProgressBar = function ProgressBar({ + currentStep, + totalSteps, +}: { + currentStep: number; + totalSteps: number; +}) { + const progressPercentage = ((currentStep + 1) / totalSteps) * 100; + return ( +
+
; +
+ ); +}; \ No newline at end of file diff --git a/frontend/src/components/TextField.module.css b/frontend/src/components/TextField.module.css new file mode 100644 index 0000000..2a66c19 --- /dev/null +++ b/frontend/src/components/TextField.module.css @@ -0,0 +1,39 @@ +.required { + margin-left: 4px; + color: red; +} + +.field { + display: flex; + flex-direction: column; + width: 100%; + height: 68; + gap: 4px; +} + +.label { + flex-direction: row; + font-size: 14px; + font-weight: 500; +} + +.inline { + flex-direction: row; +} + +.input { + border: 1px solid #cdcfd0; + width: 100%; + height: 44px; + border-radius: 8px; + padding: 10px; +} +.input:focus { + border: 2px solid #3f8d7c; + outline: none; +} + +.error { + border-width: 2px; + border-color: red; +} \ No newline at end of file diff --git a/frontend/src/components/TextField.tsx b/frontend/src/components/TextField.tsx new file mode 100644 index 0000000..3aa26c2 --- /dev/null +++ b/frontend/src/components/TextField.tsx @@ -0,0 +1,42 @@ +/** + * A text input field with a label. + */ + +import React from "react"; + +import styles from "./TextField.module.css"; + +export type TextFieldProps = { + label: string; + error?: boolean; + required?: boolean; +} & Omit, "type">; + +export const TextField = function TextField({ + ref, + label, + error = false, + required = false, + className, + ...props +}: TextFieldProps & { ref?: React.RefObject }) { + let wrapperClass = styles.field; + if (className) { + wrapperClass += ` ${className}`; + } + let inputClass = styles.input; + if (error) { + inputClass += ` ${styles.error}`; + } + return ( +
+ + +
+ ); +}; \ No newline at end of file From a6501cd741d9c7309d4e40b0feb7163f1aa67c51 Mon Sep 17 00:00:00 2001 From: OrigamiStarz <40199724+OrigamiStarz@users.noreply.github.com> Date: Thu, 12 Feb 2026 02:20:37 -0800 Subject: [PATCH 03/24] Add styling for Staff Add Modal --- .../src/app/(pages)/staff/page.module.css | 0 frontend/src/app/(pages)/staff/page.tsx | 34 ++-- .../AddStaff/AddStaffForm.module.css | 149 ++++++++++++++++++ .../src/components/AddStaff/AddStaffForm.tsx | 76 +++++++++ frontend/src/components/AddStaffForm.tsx | 34 ---- frontend/src/components/Page.module.css | 0 frontend/src/components/Page.tsx | 17 ++ 7 files changed, 260 insertions(+), 50 deletions(-) create mode 100644 frontend/src/app/(pages)/staff/page.module.css create mode 100644 frontend/src/components/AddStaff/AddStaffForm.module.css create mode 100644 frontend/src/components/AddStaff/AddStaffForm.tsx delete mode 100644 frontend/src/components/AddStaffForm.tsx create mode 100644 frontend/src/components/Page.module.css create mode 100644 frontend/src/components/Page.tsx diff --git a/frontend/src/app/(pages)/staff/page.module.css b/frontend/src/app/(pages)/staff/page.module.css new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/app/(pages)/staff/page.tsx b/frontend/src/app/(pages)/staff/page.tsx index 1049952..8046f5e 100644 --- a/frontend/src/app/(pages)/staff/page.tsx +++ b/frontend/src/app/(pages)/staff/page.tsx @@ -2,30 +2,32 @@ import { useState } from "react"; -import { AddStaffForm } from "@/src/components/AddStaffForm"; +import { AddStaffForm } from "@/src/components/AddStaff/AddStaffForm"; +import { Button } from "@/src/components/Button"; import { Modal } from "@/src/components/Modal"; +import { Page } from "@/src/components/Page"; -import { Button } from "@/src/components/Button"; +import styles from "./page.module.css"; export default function Staff() { const [addOpen, setAddOpen] = useState(false); - + return ( -
+

Staff

-
-
+ ); } diff --git a/frontend/src/components/AddStaff/AddStaffForm.module.css b/frontend/src/components/AddStaff/AddStaffForm.module.css new file mode 100644 index 0000000..4bbff0d --- /dev/null +++ b/frontend/src/components/AddStaff/AddStaffForm.module.css @@ -0,0 +1,149 @@ +.form { + background-color: white; + display: flex; + flex-direction: column; + + font-family: "Montserrat", sans-serif; + color: black; + + width: 100%; + max-width: 750px; + padding: 0px 48px; + max-height: 614px; + border-radius: 12px; +} +.reused { + display: flex; + flex-direction: column; +} +.headerSegment { + display: flex; + flex-direction: column; + gap: 1vw; + padding: 24px 0 12px 0; +} +.pageTitle { + font-size: 30px; + font-weight: 600; +} + +.formPage { + height: 100%; + display: flex; + flex-direction: column; + flex-grow: 1; +} + +.formRow { + display: flex; + flex-direction: row; + width: 100%; + height: 68px; + gap: 2vw; + margin: 10px 0; +} + +.selectField { + display: flex; + flex-direction: column; + gap: 4px; +} + +.halfField { + flex: 1; + min-width: 0; +} + +.selectLabel { + font-size: 14px; +} + +.required { + color: #dc2626; + margin-left: 4px; +} + +.charCount { + font-size: 0.8rem; + color: #475467; + align-self: flex-start; + margin-top: 4px; +} + +.stateSelect { + width: 100%; + border: none; +} + + +.form :global(.stateSelect__control) { + min-height: 44px !important; + border-radius: 8px !important; + background: #fff; + box-shadow: none; +} + +/* +This format is needed to style the default react components correctly +*/ +.form :global(.stateSelect__control--is-focused):hover { + border: 2px solid var(--primary-500); +} + + +.form :global(.stateSelect__control--is-focused) { + border: 2px solid var(--primary-500); +} + +.form :global(.stateSelect__value-container) { + padding: 0 0.75rem; +} + +.form :global(.stateSelect__indicator-separator) { + display: none; +} + +.form :global(.stateSelect__menu) { + z-index: 999; +} + +.buttonRow { + display: flex; + flex-direction: row; + margin-top: 32px; + margin-left: auto; + margin-bottom: 32px; + gap: 24px; +} + +.editTabs { + display: flex; + flex-direction: row; + gap: 10px; + margin: 12px 0 4px; +} + +.editTabs button { + flex: 1; +} + +.subheading { + font-size: 16px; + font-weight: 600; +} + +.infoBar { + display: flex; + flex-direction: column; + height: auto; +} + +.form, +.formRow, +.formPage { + overflow: visible; +} + +.error { + color: red; +} \ No newline at end of file diff --git a/frontend/src/components/AddStaff/AddStaffForm.tsx b/frontend/src/components/AddStaff/AddStaffForm.tsx new file mode 100644 index 0000000..633177e --- /dev/null +++ b/frontend/src/components/AddStaff/AddStaffForm.tsx @@ -0,0 +1,76 @@ + +import { useEffect, useState } from "react"; + +import { Button } from "../Button"; +import { TextField } from "../TextField"; + +import styles from "./AddStaffForm.module.css"; + +type AddStaffFormProps = { + onExit: () => void; +}; + +export const AddStaffForm = function AddStaffForm( {onExit}: AddStaffFormProps) { + // const [isLoading, setLoading] = useState(false); + + // useEffect(() => { + // setLoading(true); + // // API Logic here, similar to: + // // getAllStudents() + // // .then((result) => { + // // if (result.success) { + // // setStudents(result.data); + // // setSelectedId(result.data[0]?._id ?? null); + // // } else { + // // setErrorMessage(result.error); + // // } + // // }) + // // .catch((error) => setErrorMessage(error instanceof Error ? error.message : String(error))) + // // .finally(() => setLoading(false)); + // setLoading(false); + // }, []); + + // TODO: do smth like: + // handleDraftChange(e.target.value, "parentFirstName")} + // required={true} + // error={Boolean(errors.parentFirstName)} + // /> + + // TODO: add validators + + return ( +
+ {/* header */} +
+
+

Add Staff

+
+
+ + {/* content */} +
+ + +
+
+ +
+
+ +
+
+ +
+ {/* Cancel / Submit */} +
+
+
+ ); +}; diff --git a/frontend/src/components/AddStaffForm.tsx b/frontend/src/components/AddStaffForm.tsx deleted file mode 100644 index 70e6b56..0000000 --- a/frontend/src/components/AddStaffForm.tsx +++ /dev/null @@ -1,34 +0,0 @@ -// import styles from "./AddStaffForm.module.css"; - -import { TextField } from "./TextField"; -import { Button } from "./Button"; - - -// export type TextFieldProps = { -// label: string; -// error?: boolean; -// required?: boolean; -// } & Omit, "type">; - - -// type AddStaffFormProps = { - -// }; -// export const AddStaffForm = function AddStaffForm({ child, onExit }: ModalProps) { - - -// add the create user function -export const AddStaffForm = function AddStaffForm() { - return ( -
-

Add Staff

- - - - - -
- ); -}; diff --git a/frontend/src/components/Page.module.css b/frontend/src/components/Page.module.css new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/components/Page.tsx b/frontend/src/components/Page.tsx new file mode 100644 index 0000000..fa30417 --- /dev/null +++ b/frontend/src/components/Page.tsx @@ -0,0 +1,17 @@ +import React from "react"; + +import styles from "./Page.module.css"; + +export type PageProps = { + children: React.ReactNode; +}; + +export const Page = ({ children }: PageProps) => { + return ( +
+
+
{children}
+
+
+ ); +}; \ No newline at end of file From b6dfe07a0aeb868bc107fb6be4f5299664ca0347 Mon Sep 17 00:00:00 2001 From: OrigamiStarz <40199724+OrigamiStarz@users.noreply.github.com> Date: Fri, 13 Feb 2026 02:39:36 -0800 Subject: [PATCH 04/24] Add dropdowns for add staff modal --- .../AddStaff/AddStaffForm.module.css | 18 +- .../src/components/AddStaff/AddStaffForm.tsx | 77 +- package-lock.json | 791 ++++++++++++++++++ package.json | 3 + 4 files changed, 869 insertions(+), 20 deletions(-) diff --git a/frontend/src/components/AddStaff/AddStaffForm.module.css b/frontend/src/components/AddStaff/AddStaffForm.module.css index 4bbff0d..edbe8a9 100644 --- a/frontend/src/components/AddStaff/AddStaffForm.module.css +++ b/frontend/src/components/AddStaff/AddStaffForm.module.css @@ -47,6 +47,7 @@ display: flex; flex-direction: column; gap: 4px; + margin: 10px 0; } .halfField { @@ -55,7 +56,9 @@ } .selectLabel { + flex-direction: row; font-size: 14px; + font-weight: 500; } .required { @@ -70,13 +73,12 @@ margin-top: 4px; } -.stateSelect { +.select { width: 100%; border: none; } - -.form :global(.stateSelect__control) { +.form :global(.select__control) { min-height: 44px !important; border-radius: 8px !important; background: #fff; @@ -86,24 +88,24 @@ /* This format is needed to style the default react components correctly */ -.form :global(.stateSelect__control--is-focused):hover { +.form :global(.select__control--is-focused):hover { border: 2px solid var(--primary-500); } -.form :global(.stateSelect__control--is-focused) { +.form :global(.select__control--is-focused) { border: 2px solid var(--primary-500); } -.form :global(.stateSelect__value-container) { +.form :global(.select__value-container) { padding: 0 0.75rem; } -.form :global(.stateSelect__indicator-separator) { +.form :global(.select__indicator-separator) { display: none; } -.form :global(.stateSelect__menu) { +.form :global(.select__menu) { z-index: 999; } diff --git a/frontend/src/components/AddStaff/AddStaffForm.tsx b/frontend/src/components/AddStaff/AddStaffForm.tsx index 633177e..381f6fc 100644 --- a/frontend/src/components/AddStaff/AddStaffForm.tsx +++ b/frontend/src/components/AddStaff/AddStaffForm.tsx @@ -1,7 +1,8 @@ - import { useEffect, useState } from "react"; +import Select from "react-select"; import { Button } from "../Button"; +import { MultiSelectDropdown } from "../studentform/MultiSelectDropdown"; import { TextField } from "../TextField"; import styles from "./AddStaffForm.module.css"; @@ -10,7 +11,25 @@ type AddStaffFormProps = { onExit: () => void; }; -export const AddStaffForm = function AddStaffForm( {onExit}: AddStaffFormProps) { +type Option = { value: string; label: string }; + +export const AddStaffForm = function AddStaffForm({ onExit }: AddStaffFormProps) { + const [role, setRole] = useState