Skip to content

Commit

Permalink
Add file edit options on frontend (#164)
Browse files Browse the repository at this point in the history
* Add file edit options on frontend

* Add duplicate check for file creation

* Update logic to check duplicates

* Fix padding

* Fix query logic
  • Loading branch information
nickbar01234 authored Apr 15, 2024
1 parent 2290c7c commit 5aa5bc7
Show file tree
Hide file tree
Showing 14 changed files with 231 additions and 183 deletions.
4 changes: 4 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,11 @@ model File {
id String @id @default(auto()) @map("_id") @db.ObjectId
date DateTime // will zero out the hours
filetype String
// Use with Google API
fileId String
url String
seniorId String @db.ObjectId
senior Senior @relation(fields: [seniorId], references: [id], onDelete: Cascade)
Tags String[]
Expand Down
6 changes: 0 additions & 6 deletions src/app/api/file/[fileId]/route.client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,8 @@ import { z } from "zod";
import { FileResponse } from "./route.schema";
import { File } from "@server/model";

/**
* Describe the interface of SignInRequest.
*/
type IFile = z.infer<typeof File>;

/**
* Extends the parameters of fetch() function to give types to the RequestBody.
*/
interface IUpdateRequest extends Omit<RequestInit, "body"> {
fileId: string;
body: IFile;
Expand Down
150 changes: 35 additions & 115 deletions src/app/api/file/[fileId]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,13 @@ import { withSession } from "@server/decorator";
import { driveV3 } from "@server/service";
import moment from "moment";

export const PATCH = withSession(async (request) => {
const body = await request.req.json();
const nextParams: { fileId: string } = request.params.params;
export const PATCH = withSession(async ({ params, session, req }) => {
const body = await req.json();
const nextParams: { fileId: string } = params.params;
const { fileId } = nextParams;
const fileRequest = File.safeParse(body);

if (!fileRequest.success) {
console.log(fileRequest.error);
return NextResponse.json(
FileResponse.parse({
code: "INVALID_REQUEST",
Expand All @@ -26,57 +25,31 @@ export const PATCH = withSession(async (request) => {
);
} else {
const fileData = fileRequest.data;

// Check that user has this senior assigned to them
const user = await prisma.user.findFirst({
const maybeFile = await prisma.file.findFirst({
where: {
id: request.session.user.id,
id: fileId,
},
include: { senior: true },
});

if (user === null || user.SeniorIDs === null) {
return NextResponse.json(
FileResponse.parse({
code: "INVALID_REQUEST",
message: "Not a valid request",
}),
{ status: 400 }
);
}
const otherFiles = await prisma.file.findMany({
where: {
date: fileData.date,
seniorId: fileData.seniorId,
id: {
not: fileId,
},
},
});

if (
!user.SeniorIDs.some((seniorId: string) => seniorId === fileData.seniorId)
otherFiles.length > 0 ||
maybeFile == null ||
!maybeFile.senior.StudentIDs.includes(session.user.id)
) {
return NextResponse.json(
FileResponse.parse({
code: "INVALID_REQUEST",
message: "Not a valid request",
}),
{ status: 400 }
);
}

// Get senior from database
const foundSenior = await prisma.senior.findUnique({
where: { id: fileData.seniorId },
});

if (foundSenior === null) {
return NextResponse.json(
FileResponse.parse({
code: "INVALID_REQUEST",
message: "Not a valid request",
}),
{ status: 400 }
);
}

// query that date doesn't already exist
const foundFile = await prisma.file.findFirst({
where: { date: fileData.date, seniorId: fileData.seniorId },
});

if (foundFile !== null) {
// If there are other files with the same date, then the user can't take it
// If file is not found then file is deleted
// If senior doesn't include current user then they have been deselected
return NextResponse.json(
FileResponse.parse({
code: "INVALID_REQUEST",
Expand All @@ -91,30 +64,13 @@ export const PATCH = withSession(async (request) => {
const body = { name: formatted_date };

const fileUpdateData = {
fileId: fileId,
fileId: maybeFile.fileId,
resource: body,
};

await driveV3.files.update(fileUpdateData);

const file = await prisma.file.findFirst({
where: {
url: fileData.url,
},
});

if (file === null) {
return NextResponse.json(
FileResponse.parse({
code: "INVALID_REQUEST",
message: "Not a valid request",
}),
{ status: 400 }
);
}

await prisma.file.update({
where: { id: file.id },
where: { id: maybeFile.id },
data: { date: fileData.date, Tags: fileData.Tags },
});

Expand All @@ -128,68 +84,32 @@ export const PATCH = withSession(async (request) => {
}
});

export const DELETE = withSession(async (request) => {
const nextParams: { fileId: string } = request.params.params;
export const DELETE = withSession(async ({ params, session }) => {
const nextParams: { fileId: string } = params.params;
const { fileId } = nextParams;

// Check that user has this senior assigned to them
const user = await prisma.user.findFirst({
where: {
id: request.session.user.id,
},
});

if (user === null || user.SeniorIDs === null) {
return NextResponse.json(
FileResponse.parse({
code: "INVALID_REQUEST",
message: "Not a valid request",
}),
{ status: 400 }
);
}
const file = await prisma.file.findFirst({
where: {
url: `https://docs.google.com/document/d/${fileId}`,
id: fileId,
},
include: {
senior: true,
},
});

if (file === null) {
if (file == null || !file.senior.StudentIDs.includes(session.user.id)) {
// File has been deleted or student has been deselected
return NextResponse.json(
FileResponse.parse({
code: "INVALID_REQUEST",
message: "Not a valid request",
}),
{ status: 400 }
);
}

if (!user.SeniorIDs.some((seniorId: string) => seniorId === file.seniorId)) {
return NextResponse.json(
FileResponse.parse({
code: "NOT_AUTHORIZED",
message: "Senior not assigned to user",
}),
{ status: 404 }
);
}

// Get senior from database
const foundSenior = await prisma.senior.findUnique({
where: { id: file.seniorId },
});
if (foundSenior === null) {
return NextResponse.json(
FileResponse.parse({
code: "INVALID_REQUEST",
message: "Not a valid request",
code: "SUCCESS_DELETE",
message: "File successfully deleted",
}),
{ status: 400 }
{ status: 200 }
);
}

await driveV3.files.delete({
fileId: fileId,
fileId: file.fileId,
});

await prisma.file.delete({
Expand Down
26 changes: 13 additions & 13 deletions src/app/api/file/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,19 @@ export const POST = withSession(async (request) => {
id: request.session.user.id,
},
});
const otherFiles = await prisma.file.findMany({
where: {
date: fileData.date,
seniorId: fileData.seniorId,
},
});

if (user === null || user.SeniorIDs === null) {
if (
user === null ||
user.SeniorIDs === null ||
!user.SeniorIDs.some((seniorId) => seniorId === fileData.seniorId) ||
otherFiles.length > 0
) {
return NextResponse.json(
FileResponse.parse({
code: "INVALID_REQUEST",
Expand All @@ -38,18 +49,6 @@ export const POST = withSession(async (request) => {
);
}

if (
!user.SeniorIDs.some((seniorId: string) => seniorId === fileData.seniorId)
) {
return NextResponse.json(
FileResponse.parse({
code: "NOT_AUTHORIZED",
message: "Senior not assigned to user",
}),
{ status: 404 }
);
}

// Get senior from database
const foundSenior = await prisma.senior.findUnique({
where: { id: fileData.seniorId },
Expand Down Expand Up @@ -88,6 +87,7 @@ export const POST = withSession(async (request) => {
data: {
date: fileData.date,
filetype: fileData.filetype,
fileId: googleFileId ?? "",
url: `https://docs.google.com/document/d/${googleFileId}`,
seniorId: fileData.seniorId,
Tags: fileData.Tags,
Expand Down
7 changes: 4 additions & 3 deletions src/app/api/senior/[id]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,14 @@ export const PATCH = withSessionAndRole(
);
const studentIDsToAdd = seniorBody.StudentIDs;

const { StudentIDs: _, ...other } = seniorBody;
const senior = await prisma.senior.update({
where: { id: seniorId },
data: {
...seniorBody,
...other,
Students: {
connect: studentIDsToAdd.map((id) => ({ id })),
disconnect: studentIDsToRemove.map((id) => ({ id })),
connect: studentIDsToAdd.map((id) => ({ id: id })),
disconnect: studentIDsToRemove.map((id) => ({ id: id })),
},
},
});
Expand Down
4 changes: 2 additions & 2 deletions src/app/api/user/[uid]/edit-seniors/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ export const PATCH = withSession(
where: { id: uid },
data: {
Seniors: {
connect: seniorIDsToAdd.map((id) => ({ id })),
disconnect: seniorIDsToRemove.map((id) => ({ id })),
connect: seniorIDsToAdd.map((id) => ({ id: id })),
disconnect: seniorIDsToRemove.map((id) => ({ id: id })),
},
},
});
Expand Down
8 changes: 7 additions & 1 deletion src/app/private/chapter-leader/seniors/[seniorId]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import PathNav from "@components/PathNav";
import { DisplaySenior } from "@components/senior";
import { prisma } from "@server/db/client";
import { getServerSessionOrRedirect } from "@server/utils";
import { seniorFullName } from "@utils";

interface LayoutProps {
Expand All @@ -10,6 +11,7 @@ interface LayoutProps {
}

const Page = async ({ params }: LayoutProps) => {
const session = await getServerSessionOrRedirect();
const senior = await prisma.senior.findFirstOrThrow({
where: { id: params.seniorId },
include: {
Expand All @@ -33,7 +35,11 @@ const Page = async ({ params }: LayoutProps) => {
},
]}
/>
<DisplaySenior editable canAddFile={false} senior={senior} />
<DisplaySenior
editable
canAddFile={senior.StudentIDs.includes(session.user?.id ?? "")}
senior={senior}
/>
</div>
);
};
Expand Down
2 changes: 1 addition & 1 deletion src/components/AddSenior.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const StudentSelector = ({
return (
<div>
<div className="text-neutral-600 h-[34px] w-full text-lg">
Assign students
Assign members
</div>
<FilterDropdown<User>
items={students}
Expand Down
2 changes: 1 addition & 1 deletion src/components/container/HeaderContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const HeaderContainer = (props: IHeaderContainer) => {
{showHorizontalLine && (
<hr className="mt-6 w-full border-t border-black" />
)}
<div className="mt-6">{children}</div>
<div className="py-6">{children}</div>
</div>
);
};
Expand Down
Loading

0 comments on commit 5aa5bc7

Please sign in to comment.