feat: invitation deletion
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
"use server"
|
||||
|
||||
import { revalidatePath } from "next/cache"
|
||||
import { redirect } from "next/navigation"
|
||||
|
||||
import { auth } from "@/auth"
|
||||
import { isAdminEmail } from "@/lib/admin-auth"
|
||||
import { deleteInvitationGroup } from "@/lib/db/queries/invitations"
|
||||
import { deleteUploadObject } from "@/lib/storage/uploads"
|
||||
import { deleteInvitationSchema } from "@/lib/validation/invitation"
|
||||
|
||||
export interface DeleteInvitationActionState {
|
||||
error: string | null
|
||||
}
|
||||
|
||||
export async function deleteInvitationAction(
|
||||
_previousState: DeleteInvitationActionState,
|
||||
formData: FormData,
|
||||
): Promise<DeleteInvitationActionState> {
|
||||
const session = await auth()
|
||||
const adminEmail = session?.user?.email
|
||||
|
||||
if (!isAdminEmail(adminEmail)) {
|
||||
return { error: "You are not allowed to delete invitations." }
|
||||
}
|
||||
|
||||
const authorizedAdminEmail = adminEmail ?? ""
|
||||
|
||||
const parsed = deleteInvitationSchema.safeParse({
|
||||
invitationId: formData.get("invitationId"),
|
||||
})
|
||||
|
||||
if (!parsed.success) {
|
||||
return { error: "Could not delete this guest group." }
|
||||
}
|
||||
|
||||
let deleted: Awaited<ReturnType<typeof deleteInvitationGroup>>
|
||||
|
||||
try {
|
||||
deleted = await deleteInvitationGroup(parsed.data.invitationId, authorizedAdminEmail)
|
||||
} catch (error) {
|
||||
console.error("Failed to delete invitation group", error)
|
||||
return { error: "Could not delete this guest group. Please try again." }
|
||||
}
|
||||
|
||||
if (!deleted) {
|
||||
return { error: "This guest group was already removed." }
|
||||
}
|
||||
|
||||
if (deleted.photoObjectKey) {
|
||||
try {
|
||||
await deleteUploadObject(deleted.photoObjectKey)
|
||||
} catch (error) {
|
||||
console.error("Failed to delete uploaded photo object", {
|
||||
invitationId: deleted.id,
|
||||
objectKey: deleted.photoObjectKey,
|
||||
error,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
revalidatePath("/admin/guests")
|
||||
redirect("/admin/guests?deleted=1")
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import Link from "next/link"
|
||||
import { notFound } from "next/navigation"
|
||||
|
||||
import { CopyInviteButton } from "@/components/admin/copy-invite-button"
|
||||
import { DeleteInvitationDialog } from "@/components/admin/delete-invitation-dialog"
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { Button } from "@/components/ui/button"
|
||||
@@ -104,11 +105,15 @@ export default async function GuestDetailPage({
|
||||
</h1>
|
||||
<p className="text-muted-foreground">Invitation details and member list.</p>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button asChild variant="outline">
|
||||
<Link href="/admin/guests">Back to list</Link>
|
||||
</Button>
|
||||
<CopyInviteButton inviteUrl={inviteUrl} />
|
||||
<DeleteInvitationDialog
|
||||
invitationId={detail.invitation.id}
|
||||
displayName={detail.invitation.display_name}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user