Files
Raul Lugo 0ff76deb3a
Deploy On Server / Prepare Repository Remotely (push) Successful in 15s
Deploy On Server / Docker Compose Up (push) Successful in 19s
Deploy On Server / Prune Images (push) Successful in 2s
Deploy On Server / Remove .env File (push) Successful in 2s
feat: edit guest from guest groups
2026-09-18 23:43:16 +02:00

167 lines
4.7 KiB
TypeScript

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<MemberMutationResult> {
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<MemberMutationResult> {
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<string>().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 }
})
}