diff --git a/packages/components/src/admin/AdminDashboard.tsx b/packages/components/src/admin/AdminDashboard.tsx
index 254e9fe..9331a39 100644
--- a/packages/components/src/admin/AdminDashboard.tsx
+++ b/packages/components/src/admin/AdminDashboard.tsx
@@ -20,7 +20,6 @@ export default function AdminDashboard() {
const userData = data.users;
const groupData = data.groups;
- const groupInvitesData = data.groupInvites;
return (
@@ -65,19 +64,6 @@ export default function AdminDashboard() {
-
-
-
- Invites
-
- Manage pending invitations.
-
-
-
-
-
-
-
diff --git a/packages/components/src/admin/DataTable.tsx b/packages/components/src/admin/DataTable.tsx
index 1cb6a52..27529e2 100644
--- a/packages/components/src/admin/DataTable.tsx
+++ b/packages/components/src/admin/DataTable.tsx
@@ -42,17 +42,6 @@ const columns: { [T in keyof AdminDataTypes]: DataColumn[] } = {
{ accessorKey: "createdAt", header: "Date of Creation" },
{ accessorKey: "updatedAt", header: "Date Last Updated" },
],
- groupInvites: [
- { accessorKey: "email", header: "Email" },
- { accessorKey: "firstName", header: "First Name" },
- { accessorKey: "lastName", header: "Last Name" },
- { accessorKey: "stageName", header: "Stage Name" },
- { accessorKey: "role", header: "Role" },
- { accessorKey: "isSongWriter", header: "Songwriter?" },
- { accessorKey: "isAscapAffiliated", header: "ASCAP Affiliated?" },
- { accessorKey: "isBmiAffiliated", header: "BMI Affiliated?" },
- { accessorKey: "createdAt", header: "Date of Creation" },
- ],
};
interface DataTableProps {
diff --git a/packages/db/prisma/migrations/20250125001713_refactor_groups/migration.sql b/packages/db/prisma/migrations/20250125001713_refactor_groups/migration.sql
new file mode 100644
index 0000000..49b0b99
--- /dev/null
+++ b/packages/db/prisma/migrations/20250125001713_refactor_groups/migration.sql
@@ -0,0 +1,62 @@
+/*
+ Warnings:
+
+ - You are about to drop the `Group` table. If the table is not empty, all the data it contains will be lost.
+ - You are about to drop the `GroupInvite` table. If the table is not empty, all the data it contains will be lost.
+ - You are about to drop the `_GroupToUser` table. If the table is not empty, all the data it contains will be lost.
+
+*/
+-- DropForeignKey
+ALTER TABLE "GroupInvite" DROP CONSTRAINT "GroupInvite_groupId_fkey";
+
+-- DropForeignKey
+ALTER TABLE "GroupInvite" DROP CONSTRAINT "GroupInvite_initiatorId_fkey";
+
+-- DropForeignKey
+ALTER TABLE "_GroupToUser" DROP CONSTRAINT "_GroupToUser_A_fkey";
+
+-- DropForeignKey
+ALTER TABLE "_GroupToUser" DROP CONSTRAINT "_GroupToUser_B_fkey";
+
+-- DropTable
+DROP TABLE "Group";
+
+-- DropTable
+DROP TABLE "GroupInvite";
+
+-- DropTable
+DROP TABLE "_GroupToUser";
+
+-- CreateTable
+CREATE TABLE "MusicianGroup" (
+ "groupId" TEXT NOT NULL,
+ "organizerId" TEXT NOT NULL,
+ "name" TEXT NOT NULL,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL,
+
+ CONSTRAINT "MusicianGroup_pkey" PRIMARY KEY ("groupId")
+);
+
+-- CreateTable
+CREATE TABLE "MusicianGroupMember" (
+ "groupId" TEXT NOT NULL,
+ "firstName" TEXT NOT NULL,
+ "lastName" TEXT NOT NULL,
+ "stageName" TEXT,
+ "email" TEXT NOT NULL,
+ "isSongWriter" BOOLEAN NOT NULL DEFAULT false,
+ "isAscapAffiliated" BOOLEAN NOT NULL DEFAULT false,
+ "isBmiAffiliated" BOOLEAN NOT NULL DEFAULT false,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL
+);
+
+-- CreateIndex
+CREATE UNIQUE INDEX "MusicianGroupMember_groupId_email_key" ON "MusicianGroupMember"("groupId", "email");
+
+-- AddForeignKey
+ALTER TABLE "MusicianGroup" ADD CONSTRAINT "MusicianGroup_organizerId_fkey" FOREIGN KEY ("organizerId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "MusicianGroupMember" ADD CONSTRAINT "MusicianGroupMember_groupId_fkey" FOREIGN KEY ("groupId") REFERENCES "MusicianGroup"("groupId") ON DELETE CASCADE ON UPDATE CASCADE;
diff --git a/packages/db/prisma/schema.prisma b/packages/db/prisma/schema.prisma
index 1d39213..05652d1 100644
--- a/packages/db/prisma/schema.prisma
+++ b/packages/db/prisma/schema.prisma
@@ -33,10 +33,9 @@ model User {
isSongWriter Boolean @default(false)
isAscapAffiliated Boolean @default(false)
isBmiAffiliated Boolean @default(false)
- groups Group[]
+ musicianGroups MusicianGroup[]
passwordResetReq PasswordResetReq?
sessions Session[]
- sentInvites GroupInvite[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
@@ -49,34 +48,6 @@ model Session {
user User @relation(fields: [userId], references: [userId], onDelete: Cascade)
}
-model Group {
- groupId String @id @default(uuid())
- name String
- users User[]
- invites GroupInvite[]
- createdAt DateTime @default(now())
- updatedAt DateTime @updatedAt
-}
-
-model GroupInvite {
- inviteId String @default(cuid()) @map("id")
- groupId String
- initiatorId String
- email String
- firstName String
- lastName String
- stageName String?
- role Role
- isSongWriter Boolean @default(false)
- isAscapAffiliated Boolean @default(false)
- isBmiAffiliated Boolean @default(false)
- group Group @relation(fields: [groupId], references: [groupId], onDelete: Cascade)
- intitiator User @relation(fields: [initiatorId], references: [userId], onDelete: Cascade)
- createdAt DateTime @default(now())
-
- @@unique([groupId, email])
-}
-
model EmailVerificationCode {
email String @id
code String
@@ -92,3 +63,29 @@ model PasswordResetReq {
createdAt DateTime @default(now())
expiresAt DateTime
}
+
+model MusicianGroup {
+ groupId String @id @default(uuid())
+ organizerId String // The user that created the group
+ organizer User @relation(fields: [organizerId], references: [userId], onDelete: Cascade)
+ name String
+ groupMembers MusicianGroupMember[] // Does not contain the user that created the group
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+}
+
+model MusicianGroupMember {
+ groupId String
+ group MusicianGroup @relation(fields: [groupId], references: [groupId], onDelete: Cascade)
+ firstName String
+ lastName String
+ stageName String?
+ email String
+ isSongWriter Boolean @default(false)
+ isAscapAffiliated Boolean @default(false)
+ isBmiAffiliated Boolean @default(false)
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+
+ @@unique([groupId, email])
+}
diff --git a/packages/trpc/src/procedures/admin-view.ts b/packages/trpc/src/procedures/admin-view.ts
index 1f85851..beae395 100644
--- a/packages/trpc/src/procedures/admin-view.ts
+++ b/packages/trpc/src/procedures/admin-view.ts
@@ -2,11 +2,15 @@ import { adminAuthenticatedProcedureBuilder } from "../internal/init";
export const getAdminViewProcedure = adminAuthenticatedProcedureBuilder.query(
async ({ ctx }) => {
- const [users, groups, groupInvites] = await Promise.all([
+ const [users, groups] = await Promise.all([
ctx.prisma.user.findMany({ omit: { hashedPassword: true } }),
- ctx.prisma.group.findMany(),
- ctx.prisma.groupInvite.findMany(),
+ ctx.prisma.musicianGroup.findMany({
+ include: {
+ organizer: true,
+ groupMembers: true,
+ },
+ }),
]);
- return { users, groups, groupInvites };
+ return { users, groups };
},
);
diff --git a/packages/trpc/src/procedures/onboarding.ts b/packages/trpc/src/procedures/onboarding.ts
index bbd3bdd..640a46a 100644
--- a/packages/trpc/src/procedures/onboarding.ts
+++ b/packages/trpc/src/procedures/onboarding.ts
@@ -60,7 +60,6 @@ export const onboardingProcedure = authenticatedProcedureBuilder
},
});
} else {
- // TODO: Actually send the group invites
await ctx.prisma.user.update({
data: {
role: "MUSICIAN",
@@ -70,14 +69,13 @@ export const onboardingProcedure = authenticatedProcedureBuilder
isSongWriter: input.isSongWriter,
isAscapAffiliated: input.isAscapAffiliated,
isBmiAffiliated: input.isBmiAffiliated,
- groups: {
+ musicianGroups: {
create: {
name: input.groupName,
- invites: {
+ groupMembers: {
createMany: {
data:
input.groupMembers?.map((member) => ({
- initiatorId: ctx.session.userId,
email: member.email,
firstName: member.firstName,
lastName: member.lastName,
@@ -85,7 +83,6 @@ export const onboardingProcedure = authenticatedProcedureBuilder
isSongWriter: member.isSongWriter,
isAscapAffiliated: member.isAscapAffiliated,
isBmiAffiliated: member.isBmiAffiliated,
- role: "MUSICIAN",
})) ?? [],
},
},
diff --git a/tests/api/onboarding.test.ts b/tests/api/onboarding.test.ts
index 86b695f..84bf2de 100644
--- a/tests/api/onboarding.test.ts
+++ b/tests/api/onboarding.test.ts
@@ -90,7 +90,7 @@ describe("get user", () => {
},
},
}),
- prisma.group.deleteMany({
+ prisma.musicianGroup.deleteMany({
where: {
name: "Owen's Group",
},
@@ -136,18 +136,18 @@ describe("get user", () => {
expect(response.message).toEqual("Successfully onboarded");
expect(mockCache.revalidatePath).toHaveBeenCalledWith("/onboarding");
- const group = await prisma.group.findFirst({
+ const group = await prisma.musicianGroup.findFirst({
where: {
name: "Owen's Group",
},
include: {
- invites: true,
+ groupMembers: true,
},
});
expect(group).not.toBeNull();
- expect(group?.invites).toHaveLength(1);
- expect(group?.invites[0]?.email).toEqual("damian@test.org");
+ expect(group?.groupMembers).toHaveLength(1);
+ expect(group?.groupMembers[0]?.email).toEqual("damian@test.org");
});
test("Onboards media maker", async () => {