Skip to content

Commit

Permalink
Delete senior folder from drive (#176)
Browse files Browse the repository at this point in the history
* Delete senior folder from drive

* Return delete id
  • Loading branch information
nickbar01234 authored Apr 29, 2024
1 parent 5c6fd77 commit bd280a5
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 47 deletions.
2 changes: 1 addition & 1 deletion src/app/api/senior/[id]/route.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
import { seniorSchema } from "@server/model";

export const seniorDeleteResponse = z.discriminatedUnion("code", [
z.object({ code: z.literal("SUCCESS") }),
z.object({ code: z.literal("SUCCESS"), seniorId: z.string() }),
z.object({
code: z.literal("NOT_FOUND"),
message: z.string(),
Expand Down
50 changes: 32 additions & 18 deletions src/app/api/senior/[id]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ import {
patchSeniorSchema,
} from "./route.schema";
import { prisma } from "@server/db/client";
import { driveV3 } from "@server/service";
import { Senior } from "@prisma/client";

/**
* @TODO - Delete folder belonging to the senior
*/
export const DELETE = withSessionAndRole(
["CHAPTER_LEADER"],
async ({ session, params }) => {
Expand Down Expand Up @@ -39,23 +38,26 @@ export const DELETE = withSessionAndRole(
);
}

const disconnectSenior = await prisma.senior.update({
where: {
id: seniorId,
},
data: {
Students: {
set: [],
await driveV3.files.delete({ fileId: maybeSenior.folder });
await prisma.$transaction([
prisma.senior.update({
where: {
id: seniorId,
},
},
});
const deleteSenior = await prisma.senior.delete({
where: {
id: seniorId,
},
});
data: {
Students: {
set: [],
},
},
}),
prisma.senior.delete({
where: {
id: seniorId,
},
}),
]);

return NextResponse.json({ code: "SUCCESS" });
return NextResponse.json({ code: "SUCCESS", seniorId: seniorId });
}
);

Expand Down Expand Up @@ -118,6 +120,18 @@ export const PATCH = withSessionAndRole(
},
});

// TODO(nickbar01234) - Refactor for to sync with POST /senior
const toFolderName = (senior: Pick<Senior, "firstname" | "lastname">) =>
`${senior.firstname}_${senior.lastname}-${seniorId}`;

if (toFolderName(seniorBody) != toFolderName(maybeSenior)) {
const params = {
fileId: maybeSenior.folder,
resource: { name: toFolderName(seniorBody) },
};
await driveV3.files.update(params);
}

return NextResponse.json(
seniorPatchResponse.parse({
code: "SUCCESS",
Expand Down
2 changes: 1 addition & 1 deletion src/app/api/senior/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export const POST = withSessionAndRole(

const baseFolder = chapter.chapterFolder; // TODO: make env variable
const fileMetadata = {
name: [`${seniorBody.firstname}_${seniorBody.lastname}_${senior.id}`],
name: [`${seniorBody.firstname}_${seniorBody.lastname}-${senior.id}`],
mimeType: "application/vnd.google-apps.folder",
parents: [baseFolder],
};
Expand Down
40 changes: 28 additions & 12 deletions src/components/AdminHomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import { useRouter } from "next/navigation";
import SearchableContainer from "./SearchableContainer";
import ChapterRequest from "./ChapterRequest";
import DropDownContainer from "./container/DropDownContainer";
import { useApiThrottle } from "@hooks";
import React from "react";
import { Spinner } from "./skeleton";

type ChapterWithUserAndChapterRequest = Prisma.ChapterGetPayload<{
include: { students: true; chapterRequest: true };
Expand All @@ -23,6 +26,15 @@ type AdminHomePageProps = {
const AdminHomePage = ({ chapters }: AdminHomePageProps) => {
const router = useRouter();

const [deleteChapterId, setDeleteChapterId] = React.useState("");
const { fetching, fn: throttleDeleteChapter } = useApiThrottle({
fn: deleteChapter,
callback: () => {
setDeleteChapterId("");
router.refresh();
},
});

return (
<SearchableContainer
elements={chapters}
Expand All @@ -37,9 +49,9 @@ const AdminHomePage = ({ chapters }: AdminHomePageProps) => {

options.push({
name: "Remove Chapter",
onClick: async () => {
const response = await deleteChapter(chapter.id);
router.refresh();
onClick: () => {
setDeleteChapterId(chapter.id);
throttleDeleteChapter(chapter.id);
},
color: "#ef6767",
icon: <FontAwesomeIcon icon={faTrashCan} />,
Expand Down Expand Up @@ -69,15 +81,19 @@ const AdminHomePage = ({ chapters }: AdminHomePageProps) => {
},
]}
topRightButton={
<TileEdit
options={options}
editIconProps={
<FontAwesomeIcon
className="fa-lg cursor-pointer"
icon={faEllipsis}
/>
}
/>
fetching && chapter.id === deleteChapterId ? (
<Spinner width={8} height={8} />
) : !fetching ? (
<TileEdit
options={options}
editIconProps={
<FontAwesomeIcon
className="fa-lg cursor-pointer"
icon={faEllipsis}
/>
}
/>
) : null
}
moreInformation={
<DropDownContainer defaultExpand={false}>
Expand Down
2 changes: 1 addition & 1 deletion src/components/ChapterRequest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ const ChapterRequest = (props: ChapterRequestProps) => {
</div>
) : (
<div className="flex justify-center p-2">
<Spinner height={16} width={16} />
<Spinner height={20} width={16} />
</div>
)}
</div>
Expand Down
48 changes: 34 additions & 14 deletions src/components/SeniorView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@ import { Senior, User } from "@prisma/client";
import SearchableContainer from "./SearchableContainer";
import { UserTile, TileEdit } from "./TileGrid";
import AddSenior from "./AddSenior";
import { useState } from "react";
import React, { useState } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faPencil, faTrashCan } from "@fortawesome/free-solid-svg-icons";
import { useApiThrottle } from "@hooks";
import { deleteSenior } from "@api/senior/[id]/route.client";
import { Spinner } from "./skeleton";
import { seniorFullName } from "@utils";

type SeniorViewProps = {
seniors: Senior[];
Expand All @@ -26,9 +30,26 @@ export const SeniorView = ({ seniors, students }: SeniorViewProps) => {

const [yearsClicked, setYearsClicked] = useState<number[]>([]);

const [deleteSeniorId, setDeleteSeniorId] = React.useState("");

const { fn: throttleDeleteSenior, fetching } = useApiThrottle({
fn: deleteSenior,
callback: (res) => {
setDeleteSeniorId("");
if (res.code === "SUCCESS") {
setSeniorsState((seniors) =>
seniors.filter((senior) => senior.id !== res.seniorId)
);
} else {
// Caught by error.tsx
throw new Error("Fail to delete senior");
}
},
});

return (
<>
<div className="mb-6 flex flex-row items-center justify-between ">
<div className="mb-6 flex flex-row items-center justify-between">
<div className="text-2xl">Seniors {`(${seniors.length})`}</div>
<AddSenior
key="add-senior"
Expand Down Expand Up @@ -64,14 +85,9 @@ export const SeniorView = ({ seniors, students }: SeniorViewProps) => {

options.push({
name: "Delete",
onClick: (e) => {
e.stopPropagation();
e.preventDefault();
fetch(`/api/senior/${senior.id}`, {
method: "DELETE",
}).then(() => {
window.location.reload();
});
onClick: () => {
setDeleteSeniorId(senior.id);
throttleDeleteSenior({ seniorId: senior.id });
},
color: "#EF6767",
icon: <FontAwesomeIcon icon={faTrashCan} />,
Expand All @@ -82,14 +98,18 @@ export const SeniorView = ({ seniors, students }: SeniorViewProps) => {
senior={senior}
link={`/private/chapter-leader/seniors/${senior.id}`}
key={senior.id}
dropdownComponent={<TileEdit options={options} />}
dropdownComponent={
fetching && senior.id === deleteSeniorId ? (
<Spinner width={12} height={12} />
) : !fetching ? (
<TileEdit options={options} />
) : null
}
/>
);
}}
search={(senior, key) =>
(senior.firstname + " " + senior.lastname)
.toLowerCase()
.includes(key.toLowerCase())
seniorFullName(senior).toLowerCase().includes(key.toLowerCase())
}
filterField={
<div className="mb-6 flex flex-row gap-4">
Expand Down

0 comments on commit bd280a5

Please sign in to comment.