Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 18 additions & 11 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,17 @@ model VolunteerDetails {
}

model VolunteerSession {
id String @id @default(auto()) @map("_id") @db.ObjectId
user User @relation(fields: [userId], references: [id])
userId String @db.ObjectId
id String @id @default(auto()) @map("_id") @db.ObjectId
user User @relation(fields: [userId], references: [id])
userId String @db.ObjectId
organizationId String? @db.ObjectId
organization Organization? @relation(fields: [organizationId], references: [id])
checkInTime DateTime
checkOutTime DateTime?
durationHours Float?
dateWorked DateTime
timeSlot TimeSlot? @relation(fields: [timeSlotId], references: [id])
timeSlotId String? @db.ObjectId @unique
timeSlot TimeSlot? @relation(fields: [timeSlotId], references: [id])
timeSlotId String? @db.ObjectId @unique
}

model Code {
Expand All @@ -95,23 +97,28 @@ model Organization {
name String @unique
normalizedName String @unique
users User[]
timeSlots TimeSlot[]
volunteerSessions VolunteerSession[]
}

model TimeSlot {
id String @id @default(auto()) @map("_id") @db.ObjectId
userId String @db.ObjectId
user User @relation(fields: [userId], references: [id])
id String @id @default(auto()) @map("_id") @db.ObjectId
userId String @db.ObjectId
user User @relation(fields: [userId], references: [id])
organizationId String? @db.ObjectId
organization Organization? @relation(fields: [organizationId], references: [id])
startTime DateTime
endTime DateTime
durationHours Float
date DateTime
approved Boolean @default(true)
status TimeSlotStatus @default(AVAILABLE)
approved Boolean @default(true)
status TimeSlotStatus @default(AVAILABLE)
volunteerSession VolunteerSession?
numVolunteers Int @default(1)
}

model CustomDay {
id String @id @default(auto()) @map("_id") @db.ObjectId
id String @id @default(auto()) @map("_id") @db.ObjectId
date DateTime
startTime DateTime
endTime DateTime
Expand Down
3 changes: 3 additions & 0 deletions src/app/api/auth/[...nextauth]/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export const getAuthOptions = (dynamicMaxAge?: number): NextAuthOptions => {
email: user.email,
volunteerDetails,
rememberMe: rememberMe,
organizationId: user.organizationId,
};
},
}),
Expand All @@ -74,6 +75,7 @@ export const getAuthOptions = (dynamicMaxAge?: number): NextAuthOptions => {
token.firstName = user.firstName;
token.lastName = user.lastName;
token.rememberMe = user.rememberMe;
token.organizationId = user.organizationId;

if (user.role !== "ADMIN") {
token.volunteerDetails = user.volunteerDetails || null;
Expand Down Expand Up @@ -114,6 +116,7 @@ export const getAuthOptions = (dynamicMaxAge?: number): NextAuthOptions => {
session.user.firstName = token.firstName;
session.user.lastName = token.lastName;
session.user.volunteerDetails = token.volunteerDetails || null;
session.user.organizationId = token.organizationId;
session.expires = new Date(token.exp * 1000).toISOString();
return session;
},
Expand Down
5 changes: 5 additions & 0 deletions src/app/api/organization/route.client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ export const addOrganization = async (
return fetchApi("/api/organization", "POST", { userId, organizationName });
};

export const getOrganization = async (organizationId: string) => {
const url = `/api/organization?id=${organizationId}`;
return fetchApi(url, "GET");
};

export const getOrganizations = async () => {
const url = `/api/organization`;
return fetchApi(url, "GET");
Expand Down
42 changes: 40 additions & 2 deletions src/app/api/organization/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,47 @@ export const POST = async (request: NextRequest) => {
}
};

export const GET = async () => {
export const GET = async (request: NextRequest) => {
const { searchParams } = new URL(request.url);

const id: string | undefined = searchParams.get("id") || undefined;

try {
const fetchedOrganizations = await prisma.organization.findMany();
if (id) {
const fetchedOrganization = await prisma.organization.findUnique({
where: { id },
include: { volunteerSessions: true, users: true },
});

if (!fetchedOrganization) {
return NextResponse.json(
{
code: "NOT_FOUND",
message: "Organization not found",
},
{ status: 404 }
);
}

return NextResponse.json(
{
code: "SUCCESS",
data: fetchedOrganization,
},
{ status: 200 }
);
}

const fetchedOrganizations = await prisma.organization.findMany({
include: {
users: true,
volunteerSessions: {
select: {
durationHours: true,
},
},
},
});

return NextResponse.json(
{
Expand Down
15 changes: 13 additions & 2 deletions src/app/api/timeSlot/route.client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,19 @@ export const fetchApi = async (
return responseData;
};

export const addTimeSlot = async (timeSlot: CreateTimeSlotInput) =>
fetchApi("/api/timeSlot", "POST", { timeSlot });
export const addTimeSlot = async (
timeSlot: CreateTimeSlotInput,
groupSignupInfo?: {
eventTitle: string;
date: string;
startTime: string;
endTime: string;
groupName: string;
groupDescription?: string;
groupReason?: string;
groupCapacity: number;
}
) => fetchApi("/api/timeSlot", "POST", { timeSlot, groupSignupInfo });

export const getTimeSlotsByDate = async (userId: string, date: Date) => {
const isoDate = date.toISOString().split("T")[0];
Expand Down
8 changes: 7 additions & 1 deletion src/app/api/timeSlot/route.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { PrismaClient } from "@prisma/client";
import { NextRequest, NextResponse } from "next/server";
import { sendGroupSignupMail } from "../../../lib/groupSignupMail";

const prisma = new PrismaClient();

export const POST = async (request: NextRequest) => {
try {
const { timeSlot } = await request.json();
const { timeSlot, groupSignupInfo } = await request.json();

const newTimeSlot = await prisma.timeSlot.create({
data: {
userId: timeSlot.userId,
organizationId: timeSlot.organizationId ?? null,
startTime: new Date(timeSlot.startTime),
endTime: new Date(timeSlot.endTime),
durationHours: timeSlot.durationHours,
Expand All @@ -18,6 +20,10 @@ export const POST = async (request: NextRequest) => {
},
});

if (timeSlot.organizationId && groupSignupInfo) {
await sendGroupSignupMail(groupSignupInfo);
}

return NextResponse.json(
{
code: "SUCCESS",
Expand Down
5 changes: 4 additions & 1 deletion src/app/api/user/route.client.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { Role, User, VolunteerDetails } from "@prisma/client";

type CreateUserInput = Omit<User, "id" | "events" | "eventIds" | "organizationId">;
type CreateUserInput = Omit<
User,
"id" | "events" | "eventIds" | "organizationId"
>;
type CreateVolunteerDetailsInput = Omit<
VolunteerDetails,
"id" | "user" | "userId"
Expand Down
69 changes: 38 additions & 31 deletions src/app/api/user/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,10 @@ export const GET = async (request: NextRequest) => {
try {
const users = await prisma.user.findMany({
where: { role: role === "ADMIN" ? "ADMIN" : "VOLUNTEER" },
include: { volunteerDetails: role === "VOLUNTEER" },
include: {
volunteerDetails: role === "VOLUNTEER",
volunteerSessions: role === "VOLUNTEER",
},
});

if (!users || users.length === 0) {
Expand Down Expand Up @@ -215,6 +218,7 @@ export const GET = async (request: NextRequest) => {
},
},
volunteerDetails: true,
organization: true,
},
});

Expand All @@ -240,7 +244,7 @@ export const GET = async (request: NextRequest) => {
try {
const fetchedUser = await prisma.user.findUnique({
where: id ? { id } : { email },
include: { volunteerDetails: true },
include: { volunteerDetails: true, volunteerSessions: true },
});

if (!fetchedUser) {
Expand All @@ -253,13 +257,10 @@ export const GET = async (request: NextRequest) => {
);
}

// Do not include volunteerDetails in user we return
const { volunteerDetails, ...user } = fetchedUser;

return NextResponse.json(
{
code: "SUCCESS",
data: { user, volunteerDetails },
data: fetchedUser,
},
{ status: 200 }
);
Expand All @@ -283,40 +284,46 @@ export const GET = async (request: NextRequest) => {

export const PATCH = async (request: NextRequest) => {
try {
/* @TODO: Add auth */
const { user, volunteerDetails } = await request.json();

const {
id,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
volunteerDetails: _,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
volunteerSessions: __,
...userWithoutIdAndRelations
} = user;

const updatedUser = await prisma.user.update({
where: {
id: user.id,
},
where: { id },
data: {
...user,
id: undefined,
...userWithoutIdAndRelations,
volunteerDetails: volunteerDetails
? {
update: {
ageOver14: volunteerDetails.ageOver14,
firstTime: volunteerDetails.firstTime,
country: volunteerDetails.country,
address: volunteerDetails.address,
city: volunteerDetails.city,
state: volunteerDetails.state,
zipCode: volunteerDetails.zipCode,
hasLicense: volunteerDetails.hasLicense,
speaksEsp: volunteerDetails.speaksEsp,
whyJoin: volunteerDetails.whyJoin,
comments: volunteerDetails.comments,
},
}
: undefined,
},
});

let updatedVD = undefined;

if (volunteerDetails) {
updatedVD = await prisma.volunteerDetails.update({
where: {
id: volunteerDetails.id,
},
data: {
...volunteerDetails,
id: undefined,
},
});
}

return NextResponse.json(
{
code: "SUCCESS",
message: `User update with email: ${updatedUser.email}`,
data: updatedVD
? { user: updatedUser, volunteerDetails: updatedVD }
: { user: updatedUser },
message: `User updated with email: ${updatedUser.email}`,
data: updatedUser,
},
{ status: 200 }
);
Expand All @@ -325,7 +332,7 @@ export const PATCH = async (request: NextRequest) => {
return NextResponse.json(
{
code: "ERROR",
message: error,
message: error instanceof Error ? error.message : "Unknown error",
},
{ status: 500 }
);
Expand Down
Loading