import { db } from "@/lib/db/client" export type MemberMutationResult = | { status: "updated"; memberId: string } | { status: "deleted"; memberId: string } | { status: "not_found" } | { status: "last_member" } export async function listInvitationMembers(invitationId: string) { return db .selectFrom("invitation_members") .selectAll() .where("invitation_id", "=", invitationId) .orderBy("is_primary_contact", "desc") .orderBy("created_at", "asc") .execute() } export async function updateInvitationMemberName({ invitationId, memberId, name, adminEmail, }: { invitationId: string memberId: string name: string adminEmail: string }): Promise { return db.transaction().execute(async (trx) => { const member = await trx .selectFrom("invitation_members") .innerJoin("invitations", "invitations.id", "invitation_members.invitation_id") .select(["invitation_members.id", "invitation_members.name", "invitations.display_name"]) .where("invitation_members.id", "=", memberId) .where("invitation_members.invitation_id", "=", invitationId) .executeTakeFirst() if (!member) return { status: "not_found" } const updated = await trx .updateTable("invitation_members") .set({ name, updated_at: new Date() }) .where("id", "=", memberId) .where("invitation_id", "=", invitationId) .returning("id") .executeTakeFirst() if (!updated) return { status: "not_found" } await trx .updateTable("invitations") .set({ updated_at: new Date() }) .where("id", "=", invitationId) .execute() await trx .insertInto("admin_audit_events") .values({ admin_email: adminEmail, action: "invitation_member.updated", target_type: "invitation_member", target_id: memberId, metadata: { invitationId, displayName: member.display_name, previousName: member.name, newName: name, }, }) .execute() return { status: "updated", memberId } }) } export async function deleteInvitationMember({ invitationId, memberId, adminEmail, }: { invitationId: string memberId: string adminEmail: string }): Promise { return db.transaction().execute(async (trx) => { const member = await trx .selectFrom("invitation_members") .innerJoin("invitations", "invitations.id", "invitation_members.invitation_id") .select([ "invitation_members.id", "invitation_members.name", "invitation_members.is_primary_contact", "invitations.display_name", ]) .where("invitation_members.id", "=", memberId) .where("invitation_members.invitation_id", "=", invitationId) .executeTakeFirst() if (!member) return { status: "not_found" } const count = await trx .selectFrom("invitation_members") .select((eb) => eb.fn.countAll().as("count")) .where("invitation_id", "=", invitationId) .executeTakeFirstOrThrow() const memberCount = Number(count.count) if (memberCount <= 1) return { status: "last_member" } let promotedMemberId: string | null = null if (member.is_primary_contact) { const replacement = await trx .selectFrom("invitation_members") .select(["id"]) .where("invitation_id", "=", invitationId) .where("id", "!=", memberId) .orderBy("created_at", "asc") .executeTakeFirst() if (replacement) { promotedMemberId = replacement.id await trx .updateTable("invitation_members") .set({ is_primary_contact: true, updated_at: new Date() }) .where("id", "=", replacement.id) .execute() } } const deleted = await trx .deleteFrom("invitation_members") .where("id", "=", memberId) .where("invitation_id", "=", invitationId) .returning("id") .executeTakeFirst() if (!deleted) return { status: "not_found" } await trx .updateTable("invitations") .set({ updated_at: new Date() }) .where("id", "=", invitationId) .execute() await trx .insertInto("admin_audit_events") .values({ admin_email: adminEmail, action: "invitation_member.deleted", target_type: "invitation_member", target_id: memberId, metadata: { invitationId, displayName: member.display_name, name: member.name, wasPrimary: member.is_primary_contact, promotedMemberId, remainingMembers: memberCount - 1, }, }) .execute() return { status: "deleted", memberId } }) }