Skip to content

Commit

Permalink
Add teams to web app
Browse files Browse the repository at this point in the history
  • Loading branch information
stevenewald committed Sep 24, 2024
1 parent 829305a commit 8693a31
Show file tree
Hide file tree
Showing 9 changed files with 226 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ export async function GenerateApplicationEmail(user: User, profile: Profile) {

const mailOptions = {
from: "[email protected]",
to: "[email protected]",
// to: "[email protected]",
to: "[email protected]",
subject: `[ACTION REQUIRED] NUTC Application Submitted`,
text: `<ul>
<li>First Name: ${profile.firstName}</li>
Expand Down
31 changes: 31 additions & 0 deletions web/app/api/protected/db/user/getCurrentTeam/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { getSession } from "@auth0/nextjs-auth0";
import { NextResponse } from "next/server";
import prisma from "@/prisma/prismaClient";

export async function GET() {
try {
const session = await getSession();

if (!session?.user?.sub) {
return NextResponse.json({ message: "Not logged in" }, { status: 401 });
}

const user = (await prisma.user.findUnique({
where: { uid: session.user.sub },
include: { team: { include: { members: { include: { profile: true } } } } },
}));
const teamName = user?.teamName;
const teamMembers = user?.team?.members;

var teamMemberNames: String[] = [];
for (const teamMember of teamMembers || []) {
if (teamMember.uid == user?.uid) continue;
teamMemberNames.push(`${teamMember.profile?.firstName} ${teamMember.profile?.lastName}`);
}

return NextResponse.json({ teamName, teamMemberNames, status: 200 });
} catch (error: any) {
console.error(error);
return NextResponse.json({ message: error.message }, { status: 500 });
}
}
28 changes: 28 additions & 0 deletions web/app/api/protected/db/user/setTeam/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { getSession } from "@auth0/nextjs-auth0";
import { NextResponse } from "next/server";
import prisma from "@/prisma/prismaClient";

export async function POST(req: Request) {
try {
const session = await getSession();

if (!session?.user?.sub) {
return NextResponse.json({ message: "Not logged in" }, { status: 401 });
}

const { newTeam } = await req.json();

console.log(`Updated team to be ${newTeam}`);
await prisma.team.upsert({ where: { name: newTeam }, update: {}, create: { name: newTeam } });

await prisma.user.update({
where: { uid: session.user.sub },
data: { teamName: newTeam }
});

return NextResponse.json({ message: "success", status: 200 });
} catch (error: any) {
console.error(error);
return NextResponse.json({ message: error.message }, { status: 500 });
}
}
2 changes: 1 addition & 1 deletion web/app/contact/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export default function Contact() {
<dd>
<a
className="hover:text-white"
href="mailto:[email protected]">
href="mailto:[email protected]">
[email protected]
</a>
</dd>
Expand Down
13 changes: 7 additions & 6 deletions web/app/dash/dash-nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
ArrowDownOnSquareIcon,
ArrowTrendingUpIcon,
ArrowUpTrayIcon,
UserPlusIcon,
Bars3Icon,
QuestionMarkCircleIcon,
XMarkIcon,
Expand Down Expand Up @@ -47,12 +48,12 @@ const navigation = [
icon: ArrowUpTrayIcon,
activeName: "/dash/submit",
},
// {
// name: "Partner Settings",
// href: "/dash/group",
// icon: UserPlusIcon,
// activeName: "/dash/group",
// },
{
name: "Partner Settings",
href: "/dash/group",
icon: UserPlusIcon,
activeName: "/dash/group",
},
];

function classNames(...classes: any) {
Expand Down
117 changes: 117 additions & 0 deletions web/app/dash/group/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
'use client'
import { UsersIcon } from '@heroicons/react/20/solid'
import { useState, useEffect, useRef } from "react"
import Swal from 'sweetalert2'

const stats = [
{ name: 'Total Subscribers', stat: '71,897' },
{ name: 'Avg. Open Rate', stat: '58.16%' },
{ name: 'Avg. Click Rate', stat: '24.57%' },
]

export default function Groups() {
const teamNameRef = useRef<HTMLInputElement>(null);

const [team, setTeam] = useState("");
const [teamMembers, setTeamMembers] = useState([]);

useEffect(() => {
const getTeam = async () => {
const currentTeam = await fetch("/api/protected/db/user/getCurrentTeam");
const { teamName, teamMemberNames } = await currentTeam.json();
console.log(JSON.stringify(teamMemberNames));
setTeam(teamName);
setTeamMembers(teamMemberNames);
};
getTeam();
}, []);

async function handleSetTeamHandler() {
if (teamNameRef?.current?.value == null) { return }
const newTeamName = teamNameRef.current.value;
if (newTeamName == team) return;
const response = await fetch("/api/protected/db/user/setTeam", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ newTeam: newTeamName }),
});

if (response.ok) {
await Swal.fire({ icon: 'success', title: 'Team Updated', text: `Team is now ${newTeamName}` });
} else {
await Swal.fire({ icon: 'error', title: 'Setting team failed', text: 'Please contact [email protected]' });
}

setTeam(teamNameRef.current.value);
}

return (
<div className="relative min-h-screen flex items-center justify-center">
<div className="flex items-center justify-center p-4 text-center sm:p-0">
<div className="relative transform overflow-hidden rounded-lg bg-white px-4 pb-4 pt-5 text-left shadow-xl sm:my-8 sm:w-full sm:max-w-lg sm:p-6">
<div>
<div className="mt-1 text-center">
<h1 className="text-xl font-semibold leading-6 text-gray-900">
Team Registration
</h1>
<div className="mt-2">
<p className="text-base text-gray-500">
To form a team, create a team name and have your teammates enter the same on their computers.
</p>
</div>
</div>
</div>
<div>
<label htmlFor="email" className="block text-sm font-medium leading-6 text-gray-900 mt-4">
Team Name
</label>
<div className="mt-2 flex rounded-md shadow-sm">
<div className="relative flex flex-grow items-stretch focus-within:z-10">
<div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<UsersIcon aria-hidden="true" className="h-5 w-5 text-gray-400" />
</div>
<input
id="team"
name="team"
type="team"
defaultValue={team}
ref={teamNameRef}
className="block w-full rounded-none rounded-l-md border-0 py-1.5 pl-10 text-gray-900 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
/>
</div>
</div>
</div>
<div className="mt-5 sm:mt-6">
<button
type="button"
className="inline-flex w-full justify-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
onClick={handleSetTeamHandler}
>
Save Team
</button>
</div>
{teamMembers.length > 0 && <div><div className="relative mt-5">
<div aria-hidden="true" className="absolute inset-0 flex items-center">
<div className="w-full border-t border-gray-300" />
</div>
<div className="relative flex justify-center">
<span className="bg-white px-2 text-sm text-gray-500">Team Members</span>
</div>
</div>
<dl className="mt-5 grid gap-5 grid-cols-3 text-center">
{teamMembers.map((item) => (
<div key={item} className="overflow-hidden rounded-lg bg-white px-4 py-5 shadow sm:p-6 col-span-1">
<dd className="mt-1 text-lg font-semibold tracking-tight text-gray-900">{item}</dd>
</div>
))}
</dl></div>}
</div>


</div>
</div >

);
}
16 changes: 16 additions & 0 deletions web/prisma/migrations/20240922195531_/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
-- AlterTable
ALTER TABLE "users" ADD COLUMN "teamId" INTEGER;

-- CreateTable
CREATE TABLE "Team" (
"id" SERIAL NOT NULL,
"name" TEXT NOT NULL,

CONSTRAINT "Team_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "Team_name_key" ON "Team"("name");

-- AddForeignKey
ALTER TABLE "users" ADD CONSTRAINT "users_teamId_fkey" FOREIGN KEY ("teamId") REFERENCES "Team"("id") ON DELETE SET NULL ON UPDATE CASCADE;
15 changes: 15 additions & 0 deletions web/prisma/migrations/20240922195636_/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
Warnings:
- You are about to drop the column `teamId` on the `users` table. All the data in the column will be lost.
- You are about to drop the `Team` table. If the table is not empty, all the data it contains will be lost.
*/
-- DropForeignKey
ALTER TABLE "users" DROP CONSTRAINT "users_teamId_fkey";

-- AlterTable
ALTER TABLE "users" DROP COLUMN "teamId";

-- DropTable
DROP TABLE "Team";
9 changes: 9 additions & 0 deletions web/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,19 @@ model User {
Algo Algo[]
profile Profile?
AlgoFile AlgoFile[]
team Team? @relation(fields: [teamName], references: [name])
teamName String?
@@map("users")
}

model Team {
name String @id @unique
members User[]
@@map("teams")
}

model Algo {
algoFileS3Key String @id
name String @unique
Expand Down

0 comments on commit 8693a31

Please sign in to comment.