diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 93130938..32cb82d3 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -87,6 +87,8 @@ enum Role { model Senior { id String @id @default(auto()) @map("_id") @db.ObjectId name String @default("") + firstname String + lastname String location String description String StudentIDs String[] @db.ObjectId diff --git a/src/app/api/senior/[id]/route.schema.ts b/src/app/api/senior/[id]/route.schema.ts index 66418971..3f00b0ea 100644 --- a/src/app/api/senior/[id]/route.schema.ts +++ b/src/app/api/senior/[id]/route.schema.ts @@ -15,11 +15,13 @@ export const seniorDeleteResponse = z.discriminatedUnion("code", [ unauthorizedErrorSchema, ]); -export const patchSeniorSchema = z.object({ - name: z.string(), - location: z.string(), - description: z.string(), - StudentIDs: z.array(z.string()), +export const patchSeniorSchema = seniorSchema.pick({ + firstname: true, + lastname: true, + name: true, // TODO(nickbar01234) - Remove name + location: true, + StudentIDs: true, + description: true, }); export type ISeniorSchema = z.infer; diff --git a/src/app/api/senior/[id]/route.ts b/src/app/api/senior/[id]/route.ts index 6a619581..4a3115fa 100644 --- a/src/app/api/senior/[id]/route.ts +++ b/src/app/api/senior/[id]/route.ts @@ -6,7 +6,6 @@ import { patchSeniorSchema, } from "./route.schema"; import { prisma } from "@server/db/client"; -import { seniorSchema } from "@server/model"; /** * @TODO - Delete folder belonging to the senior diff --git a/src/app/api/senior/route.schema.ts b/src/app/api/senior/route.schema.ts index d525e3b5..fb9c03ce 100644 --- a/src/app/api/senior/route.schema.ts +++ b/src/app/api/senior/route.schema.ts @@ -2,11 +2,13 @@ import { z } from "zod"; import { unauthorizedErrorSchema, unknownErrorSchema } from "../route.schema"; import { seniorSchema } from "@server/model"; -export const postSeniorSchema = z.object({ - name: z.string(), - location: z.string(), - description: z.string(), - StudentIDs: z.array(z.string()), +export const postSeniorSchema = seniorSchema.pick({ + firstname: true, + lastname: true, + name: true, // TODO(nickbar01234) - Remove name + location: true, + StudentIDs: true, + description: true, }); export type IPostSeniorRequestSchema = z.infer; diff --git a/src/app/api/senior/route.ts b/src/app/api/senior/route.ts index c104fa5d..a4f6385b 100644 --- a/src/app/api/senior/route.ts +++ b/src/app/api/senior/route.ts @@ -22,6 +22,7 @@ export const POST = withSessionAndRole( { status: 400 } ); } else { + const seniorBody = newSenior.data; const user = await prisma.user.findFirst({ where: { id: session.user.id, @@ -48,7 +49,9 @@ export const POST = withSessionAndRole( } const baseFolder = "1MVyWBeKCd1erNe9gkwBf7yz3wGa40g9a"; // TODO: make env variable const fileMetadata = { - name: [`${body.name}-${randomUUID()}`], + name: [ + `${seniorBody.firstname}_${seniorBody.lastname}-${randomUUID()}`, + ], mimeType: "application/vnd.google-apps.folder", parents: [baseFolder], }; @@ -82,11 +85,13 @@ export const POST = withSessionAndRole( const senior = await prisma.senior.create({ data: { - name: body.name, - location: body.location, - description: body.description, + name: seniorBody.name, + firstname: seniorBody.firstname, + lastname: seniorBody.lastname, + location: seniorBody.location, + description: seniorBody.description, ChapterID: session.user.ChapterID, - StudentIDs: body.StudentIDs, + StudentIDs: seniorBody.StudentIDs, folder: googleFolderId, }, }); diff --git a/src/app/private/[uid]/admin/home/chapters/[chapterId]/users/[userId]/seniors/[seniorId]/page.tsx b/src/app/private/[uid]/admin/home/chapters/[chapterId]/users/[userId]/seniors/[seniorId]/page.tsx index f5289eba..ea81c976 100644 --- a/src/app/private/[uid]/admin/home/chapters/[chapterId]/users/[userId]/seniors/[seniorId]/page.tsx +++ b/src/app/private/[uid]/admin/home/chapters/[chapterId]/users/[userId]/seniors/[seniorId]/page.tsx @@ -41,14 +41,14 @@ const SeniorPage = async ({ params }: Params) => { { display: "Chapters", url: "chapters" }, { display: user.Chapter?.chapterName ?? "", url: chapterId }, { display: user.name ?? "", url: userId }, - { display: senior.name, url: seniorId }, + { display: `${senior.firstname} ${senior.lastname}`, url: seniorId }, ]} /> -

{senior.name}

+

{`${senior.firstname} ${senior.lastname}`}

{senior.description.length > 0 && (

{senior.description}

)} diff --git a/src/app/private/[uid]/chapter-leader/layout.tsx b/src/app/private/[uid]/chapter-leader/layout.tsx index 1c70bd35..abd0fe60 100644 --- a/src/app/private/[uid]/chapter-leader/layout.tsx +++ b/src/app/private/[uid]/chapter-leader/layout.tsx @@ -29,6 +29,12 @@ const UserLayout = ({ children }: IUserLayout) => { link: `/private/${user.id}/chapter-leader/users`, icon: faUserGroup, }, + // @TODO(nickbar01234) - Fix icon + { + name: "Seniors", + link: `/private/${user.id}/chapter-leader/seniors`, + icon: faUserGroup, + }, { name: "Pending", link: `/private/${user.id}/chapter-leader/pending`, diff --git a/src/app/private/[uid]/chapter-leader/seniors/page.tsx b/src/app/private/[uid]/chapter-leader/seniors/page.tsx new file mode 100644 index 00000000..b5e2b745 --- /dev/null +++ b/src/app/private/[uid]/chapter-leader/seniors/page.tsx @@ -0,0 +1,43 @@ +import React from "react"; +import { prisma } from "@server/db/client"; +import { faUsers } from "@fortawesome/free-solid-svg-icons"; +import { HeaderContainer } from "@components/container"; +import { SeniorView } from "@components/SeniorView"; + +const UserSeniorsPage = async ({ params }: { params: { uid: string } }) => { + const userUid = params.uid; + const user = await prisma.user.findUnique({ + where: { + id: userUid, + }, + }); + if (!user) { + return
User not found
; + } + + // Fetch the seniors too + const chapter = await prisma.chapter.findFirst({ + where: { + id: user.ChapterID ?? undefined, + }, + include: { + seniors: {}, + students: {}, + }, + }); + const seniors = chapter?.seniors ? chapter.seniors : []; + const students = chapter?.students ? chapter.students : []; + + return ( + +
Seniors {`(${seniors.length})`}
+ +
+ ); +}; + +export default UserSeniorsPage; diff --git a/src/app/private/[uid]/user/seniors/SeniorsHomePage.tsx b/src/app/private/[uid]/user/seniors/SeniorsHomePage.tsx index 2775d84e..e1b10c9c 100644 --- a/src/app/private/[uid]/user/seniors/SeniorsHomePage.tsx +++ b/src/app/private/[uid]/user/seniors/SeniorsHomePage.tsx @@ -12,7 +12,7 @@ type SeniorsHomePageProps = { const SeniorsHomePage = ({ seniors, user }: SeniorsHomePageProps) => { const displaySeniors = (elem: Senior, index: number) => ( diff --git a/src/app/private/[uid]/user/seniors/[seniorId]/page.tsx b/src/app/private/[uid]/user/seniors/[seniorId]/page.tsx index a704539d..b642a83d 100644 --- a/src/app/private/[uid]/user/seniors/[seniorId]/page.tsx +++ b/src/app/private/[uid]/user/seniors/[seniorId]/page.tsx @@ -33,7 +33,7 @@ const Page = async ({ params }: PageProps) => { pathInfo={[ { display: "Seniors", url: `/private/${params.uid}/user/seniors` }, { - display: senior.name, + display: `${senior.firstname} ${senior.lastname}`, url: `/private/${params.uid}/seniors/${senior.id}`, }, ]} diff --git a/src/components/AddSenior.tsx b/src/components/AddSenior.tsx index b5603b4a..adbcf34a 100644 --- a/src/components/AddSenior.tsx +++ b/src/components/AddSenior.tsx @@ -5,6 +5,9 @@ import FilterDropdown from "@components/FilterDropdown"; import { Senior, User } from "@prisma/client"; import ImageIcon from "../../public/icons/icon_add_photo.png"; import { patchSenior } from "src/app/api/senior/[id]/route.client"; +import { postSenior } from "src/app/api/senior/route.client"; +import z from "zod/lib"; +import { seniorSchema } from "@server/model"; type AddSeniorProps = { seniors: Senior[]; @@ -83,11 +86,15 @@ const StudentSelector = ({ ); }; -type SeniorData = { - name: string; - location: string; - description: string; -}; +// type SeniorData = Omit< +// Extract, { code: "SUCCESS" }>["data"], +// "StudentIDs" +// >; + +type SeniorData = Pick< + z.infer, + "firstname" | "lastname" | "name" | "location" | "description" +>; const AddSenior = ({ seniors, @@ -99,6 +106,8 @@ const AddSenior = ({ setSeniorPatch, }: AddSeniorProps) => { const emptySenior: SeniorData = { + firstname: "", + lastname: "", name: "", location: "", description: "", @@ -157,36 +166,23 @@ const AddSenior = ({ // put accumulated students into senior model data const seniorModel = { ...seniorData, - StudentIDs: selectedStudents.map((usr) => { - console.log(usr.id); - return usr.id; - }), + name: `${seniorData.firstname} ${seniorData.lastname}`, + StudentIDs: selectedStudents.map((usr) => usr.id), }; // POST new senior model to database - const currRes = await fetch("/api/seniors/add", { - method: "POST", - body: JSON.stringify(seniorModel), + postSenior({ body: seniorModel }).then((res) => { + if (res.code === "SUCCESS") { + // PATCH students models previously and newly associated with senior model + setConfirm(true); + setSeniors([...seniors, res.data]); + } else { + setError(true); + } + setSeniorData(emptySenior); + setSelectedStudents([]); + return null; }); - const newSeniorObj = await currRes.json(); - - if (currRes.status === 200) { - // PATCH students models previously and newly associated with senior model - setConfirm(true); - setSeniors([...seniors, newSeniorObj]); - } - // check after both API calls - if (currRes.status != 200) { - console.log( - currRes.text().then((text) => { - console.log(text); - }) - ); - setError(true); - } - - setSeniorData(emptySenior); - setSelectedStudents([]); }; const handleImageReplace = (event: React.ChangeEvent) => { @@ -257,7 +253,7 @@ const AddSenior = ({ onChange={(e: React.ChangeEvent) => setSeniorData({ ...seniorData, - name: e.target.value, + firstname: e.target.value, }) } /> @@ -271,10 +267,10 @@ const AddSenior = ({ ) => + onChange={(e: React.ChangeEvent) => setSeniorData((seniorData) => ({ ...seniorData, - name: seniorData.name + " " + e.target.value, + lastname: e.target.value, })) } /> diff --git a/src/components/SeniorView.tsx b/src/components/SeniorView.tsx new file mode 100644 index 00000000..195d3466 --- /dev/null +++ b/src/components/SeniorView.tsx @@ -0,0 +1,45 @@ +"use client"; + +import { Senior, User } from "@prisma/client"; +import SearchableContainer from "./SearchableContainer"; +import { UserTile } from "./TileGrid"; +import AddSenior from "./AddSenior"; +import { useState } from "react"; + +type SeniorViewProps = { + seniors: Senior[]; + students: User[]; +}; + +export const SeniorView = ({ seniors, students }: SeniorViewProps) => { + const [seniorsState, setSeniorsState] = useState(seniors); + const [showAddSeniorPopUp, setShowAddSeniorPopUp] = useState(false); + const [seniorPatch, setSeniorPatch] = useState(""); + + return ( + + addElementComponent={ + + } + elements={seniorsState ? seniorsState : []} + display={(senior, index) => ( + // TODO(nickbar01234) - Fix link + + )} + search={(senior, key) => + (senior.firstname + " " + senior.lastname) + .toLowerCase() + .includes(key.toLowerCase()) + } + /> + ); +}; diff --git a/src/components/TileGrid/SeniorTile.tsx b/src/components/TileGrid/SeniorTile.tsx index 6bcdcd38..0fd0d445 100644 --- a/src/components/TileGrid/SeniorTile.tsx +++ b/src/components/TileGrid/SeniorTile.tsx @@ -59,7 +59,11 @@ export function SeniorTile({ }, ]; - console.log(senior.name + "'s Students: " + senior.StudentIDs.toString()); + console.log( + `${senior.firstname} ${senior.lastname}` + + "'s Students: " + + senior.StudentIDs.toString() + ); return (
@@ -77,7 +81,7 @@ export function SeniorTile({
{" "} - {senior.name}{" "} + {`${senior.firstname} ${senior.lastname}`}{" "}

{senior.location} diff --git a/src/components/TileGrid/UserTile.tsx b/src/components/TileGrid/UserTile.tsx index f0616bc4..63c4503c 100644 --- a/src/components/TileGrid/UserTile.tsx +++ b/src/components/TileGrid/UserTile.tsx @@ -54,8 +54,8 @@ export function UserTile({

{student && student.name ? student.name + (student.admin ? " (Admin)" : "") - : senior - ? senior.name + : senior && `${senior.firstname} ${senior.lastname}` + ? `${senior.firstname} ${senior.lastname}` : null}

{/* @TODO: Add pronouns once we add to student field */} diff --git a/src/components/senior/DisplaySenior.tsx b/src/components/senior/DisplaySenior.tsx index 7a48ca5a..9fa621cd 100644 --- a/src/components/senior/DisplaySenior.tsx +++ b/src/components/senior/DisplaySenior.tsx @@ -52,7 +52,7 @@ const DisplaySenior = (props: DisplayProps) => { return (
{/* @TODO - Firstname + lastname */} -

{senior.name}

+

{`${senior.firstname} ${senior.lastname}`}

{senior.description}

{ onSave={async () => { await patchSenior({ body: { + firstname: senior.firstname, + lastname: senior.lastname, name: senior.name, location: senior.location, description: senior.description, diff --git a/src/pages/admin-home.tsx b/src/pages/admin-home.tsx index d502d983..6a61bcd6 100644 --- a/src/pages/admin-home.tsx +++ b/src/pages/admin-home.tsx @@ -261,7 +261,7 @@ function SeniorBody({ ); }} search={(senior: Senior, filter: string) => - !!senior.name?.includes(filter) + !!`${senior.firstname} ${senior.lastname}`?.includes(filter) } elements={seniors} /> diff --git a/src/server/model/index.ts b/src/server/model/index.ts index a3b46594..d768b5b6 100644 --- a/src/server/model/index.ts +++ b/src/server/model/index.ts @@ -3,6 +3,8 @@ import { z } from "zod"; export const seniorSchema = z.object({ id: z.string(), + firstname: z.string(), + lastname: z.string(), name: z.string(), location: z.string(), description: z.string(),