Skip to content

Commit

Permalink
Add assign president popup (#142)
Browse files Browse the repository at this point in the history
  • Loading branch information
nickbar01234 authored Apr 7, 2024
1 parent 208a778 commit 18e65e3
Show file tree
Hide file tree
Showing 13 changed files with 135 additions and 269 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ import { z } from "zod";
import { EditRoleRequest, EditRoleResponse } from "./route.schema";

export const editRole = async (
request: TypedRequest<z.infer<typeof EditRoleRequest>>,
uid: string
request: TypedRequest<z.infer<typeof EditRoleRequest>>
) => {
const { body, ...options } = request;
const response = await fetch(`/api/user/${uid}/edit-role`, {
const response = await fetch(`/api/admin/edit-role`, {
method: "PATCH",
body: JSON.stringify(body),
...options,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import { roleSchema } from "@server/schema";
import { z } from "zod";

export const EditRoleRequest = z.object({
role: roleSchema.refine(
(value): value is "CHAPTER_LEADER" | "USER" =>
value === "CHAPTER_LEADER" || value === "USER"
),
chapterLeaders: z.array(z.string()),
users: z.array(z.string()),
});

export const EditRoleResponse = z.discriminatedUnion("code", [
Expand Down
52 changes: 52 additions & 0 deletions src/app/api/admin/edit-role/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { withRole, withSession } from "@server/decorator";
import { EditRoleRequest, EditRoleResponse } from "./route.schema";
import { NextResponse } from "next/server";
import { prisma } from "@server/db/client";
import { Role } from "@prisma/client";

/**
* Update multiple non-admin user's role.
*/
export const PATCH = withSession(
withRole(["ADMIN"], async ({ req, session }) => {
const maybeBody = EditRoleRequest.safeParse(await req.json());

if (!maybeBody.success || session.user.role !== "ADMIN") {
return NextResponse.json(
EditRoleResponse.parse({
code: "INVALID_REQUEST",
message: "Invalid request",
}),
{ status: 400 }
);
}

const updateManyStudents = (ids: string[], role: Role) => {
if (ids.length > 0) {
return prisma.user.updateMany({
where: {
id: {
in: ids,
},
},
data: {
role: role,
},
});
}
return Promise.resolve();
};

await Promise.allSettled([
updateManyStudents(maybeBody.data.chapterLeaders, "CHAPTER_LEADER"),
updateManyStudents(maybeBody.data.users, "USER"),
]);

return NextResponse.json(
EditRoleResponse.parse({
code: "SUCCESS",
message: "Updated user role",
})
);
})
);
50 changes: 0 additions & 50 deletions src/app/api/user/[uid]/edit-role/route.ts

This file was deleted.

12 changes: 2 additions & 10 deletions src/app/private/[uid]/chapter-leader/users/MembersHomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,22 +73,14 @@ const MembersHomePage = ({ members }: MembersHomePageProps) => {
{`Members (${members.length})`}
</h1>
{uidToEdit != null && (
<Popup>
<Popup onClick={(e) => e.stopPropagation()}>
<div className="text-3xl font-bold text-white">Assign to E-board</div>
<Dropdown
header="Select position"
elements={EBOARD_POSITIONS}
display={(element) => <>{element.position}</>}
selected={selectedPosition}
setSelected={(element) => {
if (selectedPosition.some((other) => element.id === other.id)) {
setSelectedPosition((prev) =>
prev.filter((other) => element.id !== other.id)
);
} else {
setSelectedPosition([element]);
}
}}
setSelected={setSelectedPosition}
onSave={async () => {
await editPosition(
{
Expand Down
71 changes: 64 additions & 7 deletions src/components/DisplayChapterInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import { Prisma, Resource, User, UserRequest } from "@prisma/client";
import { CardGrid } from "./container";
import { Prisma, Resource } from "@prisma/client";
import { CardGrid, Popup } from "./container";
import { UserTile } from "./TileGrid";
import DisplayResources from "./DisplayResources";
import React from "react";
Expand All @@ -10,6 +10,9 @@ import PendingCard from "@components/PendingCard";
import { fullName } from "@utils";
import { RoleToUrlSegment } from "@constants/RoleAlias";
import { sortedStudents } from "@utils";
import { Dropdown } from "./selector";
import { editRole } from "@api/admin/edit-role/route.client";
import { useRouter } from "next/navigation";

type ChapterWithUser = Prisma.ChapterGetPayload<{
include: { students: true };
Expand All @@ -32,6 +35,7 @@ const DisplayChapterInfo = ({
}: DisplayChapterInfoParams) => {
const userContext = React.useContext(UserContext);
const { user } = userContext;
const router = useRouter();

const students =
user.role === "ADMIN"
Expand All @@ -40,8 +44,48 @@ const DisplayChapterInfo = ({
(user) => user.role === "CHAPTER_LEADER" || user.position !== ""
);

const currentPresidents = chapter.students.filter(
(user) => user.role === "CHAPTER_LEADER"
);
const [displayAssignPresident, setDisplayAssignPresident] =
React.useState(false);
const [assignedPresidents, setAssignedPresidents] =
React.useState(currentPresidents);

const onSaveNewPresidents = async () => {
const previousPresidents = currentPresidents.filter(
(student) =>
assignedPresidents.find((other) => student.id === other.id) == undefined
);
await editRole({
body: {
chapterLeaders: assignedPresidents.map((student) => student.id),
users: previousPresidents.map((student) => student.id),
},
});
router.refresh();
};

const resetAssignment = () => {
setDisplayAssignPresident(false);
setAssignedPresidents(currentPresidents);
};

return (
<div className="w-full">
<div className="w-full" onClick={resetAssignment}>
{displayAssignPresident && (
<Popup onClick={(e) => e.stopPropagation()}>
<div className="text-3xl font-bold text-white">Assign President</div>
<Dropdown
header="Select student"
elements={chapter.students}
display={(student) => fullName(student)}
selected={assignedPresidents}
setSelected={setAssignedPresidents}
onSave={onSaveNewPresidents}
/>
</Popup>
)}
<div className="font-merriweather mb-4 text-2xl font-bold text-[#000022]">
{chapter.chapterName}
</div>
Expand Down Expand Up @@ -86,10 +130,23 @@ const DisplayChapterInfo = ({
<div className="mb-12">
<CardGrid
title={
<div className="text-xl text-[#000022]">
{user.role === "ADMIN"
? `Members (${chapter.students.length})`
: "Executive Board"}
<div className="flex justify-between">
<span className="text-xl text-[#000022]">
{user.role === "ADMIN"
? `Members (${chapter.students.length})`
: "Executive Board"}
</span>
{user.role === "ADMIN" && (
<div
className="cursor-pointer rounded-lg bg-dark-teal px-4 py-3 text-white"
onClick={(e) => {
e.stopPropagation();
setDisplayAssignPresident(true);
}}
>
Assign President
</div>
)}
</div>
}
tiles={sortedStudents(students).map((student) => {
Expand Down
Loading

0 comments on commit 18e65e3

Please sign in to comment.