- {club.userMetadataToClubs.map((officer) => (
-
+ {club.officers.map((officer) => (
+
- {officer.userMetadata.firstName +
- ' ' +
- officer.userMetadata.lastName}
+ {officer.name}
- Officer {/* TODO: link to officers table */}
+ {officer.position}
diff --git a/src/server/api/routers/club.ts b/src/server/api/routers/club.ts
index 07cd3887..36f6dafc 100644
--- a/src/server/api/routers/club.ts
+++ b/src/server/api/routers/club.ts
@@ -1,14 +1,4 @@
-import {
- eq,
- ilike,
- sql,
- and,
- notInArray,
- inArray,
- or,
- lt,
- gt,
-} from 'drizzle-orm';
+import { eq, ilike, sql, and, notInArray, inArray, lt, gt } from 'drizzle-orm';
import { createTRPCRouter, protectedProcedure, publicProcedure } from '../trpc';
import { z } from 'zod';
import { clubEditRouter } from './clubEdit';
@@ -16,6 +6,7 @@ import { userMetadataToClubs } from '@src/server/db/schema/users';
import { club, usedTags } from '@src/server/db/schema/club';
import { contacts } from '@src/server/db/schema/contacts';
import { carousel } from '@src/server/db/schema/admin';
+import { officers as officersTable } from '@src/server/db/schema/officers';
import { createClubSchema as baseClubSchema } from '@src/utils/formSchemas';
const byNameSchema = z.object({
name: z.string().default(''),
@@ -252,6 +243,14 @@ export const clubRouter = createTRPCRouter({
});
return officers;
}),
+ getListedOfficers: protectedProcedure
+ .input(byIdSchema)
+ .query(async ({ input, ctx }) => {
+ const officers = await ctx.db.query.officers.findMany({
+ where: eq(officersTable.clubId, input.id),
+ });
+ return officers;
+ }),
isActive: publicProcedure.input(byIdSchema).query(async ({ input, ctx }) => {
const hasPresident = await ctx.db.query.userMetadataToClubs.findFirst({
where: and(
@@ -278,16 +277,7 @@ export const clubRouter = createTRPCRouter({
where: (club) => eq(club.id, id),
with: {
contacts: true,
- userMetadataToClubs: {
- where: (row) =>
- or(
- eq(row.memberType, 'President'),
- eq(row.memberType, 'Officer'),
- ),
- with: {
- userMetadata: { columns: { firstName: true, lastName: true } },
- },
- },
+ officers: true,
},
});
return byId;
diff --git a/src/server/api/routers/clubEdit.ts b/src/server/api/routers/clubEdit.ts
index 30ca549c..3dfbe4de 100644
--- a/src/server/api/routers/clubEdit.ts
+++ b/src/server/api/routers/clubEdit.ts
@@ -8,6 +8,7 @@ import { selectContact } from '@src/server/db/models';
import { club } from '@src/server/db/schema/club';
import { contacts } from '@src/server/db/schema/contacts';
import { userMetadataToClubs } from '@src/server/db/schema/users';
+import { officers } from '@src/server/db/schema/officers';
async function isUserOfficer(userId: string, clubId: string) {
const officer = await db.query.userMetadataToClubs.findFirst({
@@ -36,7 +37,7 @@ const editContactSchema = z.object({
modified: selectContact.array(),
created: selectContact.omit({ clubId: true }).array(),
});
-const editOfficerSchema = z.object({
+const editCollaboratorSchema = z.object({
clubId: z.string(),
deleted: z.string().array(),
modified: z
@@ -53,6 +54,24 @@ const editOfficerSchema = z.object({
})
.array(),
});
+
+const editOfficerSchema = z.object({
+ clubId: z.string(),
+ deleted: z.string().array(),
+ modified: z
+ .object({
+ id: z.string(),
+ name: z.string(),
+ position: z.string(),
+ })
+ .array(),
+ created: z
+ .object({
+ name: z.string(),
+ position: z.string(),
+ })
+ .array(),
+});
const deleteSchema = z.object({ clubId: z.string() });
export const clubEditRouter = createTRPCRouter({
@@ -117,7 +136,7 @@ export const clubEditRouter = createTRPCRouter({
.onConflictDoNothing();
}),
officers: protectedProcedure
- .input(editOfficerSchema)
+ .input(editCollaboratorSchema)
.mutation(async ({ input, ctx }) => {
const isOfficer = await isUserOfficer(ctx.session.user.id, input.clubId);
if (!isOfficer) {
@@ -169,6 +188,47 @@ export const clubEditRouter = createTRPCRouter({
where: eq(userMetadataToClubs.memberType, 'Member'),
});
}),
+ listedOfficers: protectedProcedure
+ .input(editOfficerSchema)
+ .mutation(async ({ input, ctx }) => {
+ const isOfficer = await isUserOfficer(ctx.session.user.id, input.clubId);
+ if (!isOfficer) {
+ throw new TRPCError({
+ message: 'must be an officer to modify this club',
+ code: 'UNAUTHORIZED',
+ });
+ }
+ if (input.deleted.length > 0) {
+ await ctx.db
+ .delete(userMetadataToClubs)
+ .where(
+ and(
+ eq(userMetadataToClubs.clubId, input.clubId),
+ inArray(userMetadataToClubs.userId, input.deleted),
+ ),
+ );
+ }
+ const promises: Promise
[] = [];
+ for (const modded of input.modified) {
+ const prom = ctx.db
+ .update(officers)
+ .set({ position: modded.position })
+ .where(
+ and(eq(officers.id, modded.id), eq(officers.clubId, input.clubId)),
+ );
+ promises.push(prom);
+ }
+ await Promise.allSettled(promises);
+ if (input.created.length === 0) return;
+
+ await ctx.db.insert(officers).values(
+ input.created.map((officer) => ({
+ clubId: input.clubId,
+ name: officer.name,
+ position: officer.position,
+ })),
+ );
+ }),
delete: protectedProcedure
.input(deleteSchema)
.mutation(async ({ input, ctx }) => {
diff --git a/src/server/db/index.ts b/src/server/db/index.ts
index cc1185ab..59295784 100644
--- a/src/server/db/index.ts
+++ b/src/server/db/index.ts
@@ -7,6 +7,7 @@ import * as events from './schema/events';
import * as users from './schema/users';
import * as forms from './schema/forms';
import * as admin from './schema/admin';
+import * as officers from './schema/officers';
import { neon } from '@neondatabase/serverless';
const schema = {
@@ -16,6 +17,7 @@ const schema = {
...users,
...forms,
...admin,
+ ...officers,
};
const neon_client = neon(env.DATABASE_URL);
diff --git a/src/utils/formSchemas.ts b/src/utils/formSchemas.ts
index 2cc635bb..77dc0bf7 100644
--- a/src/utils/formSchemas.ts
+++ b/src/utils/formSchemas.ts
@@ -36,6 +36,15 @@ export const editOfficerSchema = z.object({
})
.array(),
});
+export const editListedOfficerSchema = z.object({
+ officers: z
+ .object({
+ id: z.string().optional(),
+ name: z.string(),
+ position: z.string().min(1),
+ })
+ .array(),
+});
export const createEventSchema = z.object({
clubId: z.string(),
From 59ba703c66a8e9327763cf600c78e11e362f02e5 Mon Sep 17 00:00:00 2001
From: Ethan Bickel
Date: Tue, 5 Nov 2024 17:33:05 -0600
Subject: [PATCH 09/10] fix redirect
---
src/components/admin/ClubTable.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/admin/ClubTable.tsx b/src/components/admin/ClubTable.tsx
index 8123e162..51eeb228 100644
--- a/src/components/admin/ClubTable.tsx
+++ b/src/components/admin/ClubTable.tsx
@@ -32,7 +32,7 @@ export default function ClubTable({ clubs }: { clubs: Club[] }) {
id: 'view',
cell: ({ row }) => (
From 439237b82e3c20a10c7fa2c5ea0c4bfede287715 Mon Sep 17 00:00:00 2001
From: Ethan Bickel
Date: Tue, 5 Nov 2024 19:06:44 -0600
Subject: [PATCH 10/10] private -> public
---
src/server/api/routers/club.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/server/api/routers/club.ts b/src/server/api/routers/club.ts
index 36f6dafc..20b693bd 100644
--- a/src/server/api/routers/club.ts
+++ b/src/server/api/routers/club.ts
@@ -243,7 +243,7 @@ export const clubRouter = createTRPCRouter({
});
return officers;
}),
- getListedOfficers: protectedProcedure
+ getListedOfficers: publicProcedure
.input(byIdSchema)
.query(async ({ input, ctx }) => {
const officers = await ctx.db.query.officers.findMany({