feat(dashboard): add edit button per universe card with rename/delete actions
Some checks failed
CI / validate (push) Has been cancelled

Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
2026-08-28 15:44:23 +05:00
parent 0a38ffdaf0
commit 034966f9ef
3 changed files with 137 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
import { requireCampaignAccess, stageTwoApiError, stageTwoDatabase, stageTwoUuid } from '~/server/utils/stage-two-supabase'
import { requireStageTwoUser } from '~/server/utils/stage-two-supabase'
export default defineEventHandler(async (event) => {
try {
const user = await requireStageTwoUser(event)
const campaignId = stageTwoUuid(event.context.params?.id ?? '')
const { owner } = await requireCampaignAccess(campaignId, user.id, true)
if (!owner) throw createError({ statusCode: 403, statusMessage: 'Only the campaign owner can delete it.' })
// Delete associated data first to respect relational integrity
await stageTwoDatabase(`characters?campaign_id=eq.${campaignId}`, { method: 'DELETE' })
await stageTwoDatabase(`rounds?campaign_id=eq.${campaignId}`, { method: 'DELETE' })
await stageTwoDatabase(`game_events?campaign_id=eq.${campaignId}`, { method: 'DELETE' })
await stageTwoDatabase(`dice_rolls?campaign_id=eq.${campaignId}`, { method: 'DELETE' })
await stageTwoDatabase(`player_intents?campaign_id=eq.${campaignId}`, { method: 'DELETE' })
await stageTwoDatabase(`story_summaries?campaign_id=eq.${campaignId}`, { method: 'DELETE' })
await stageTwoDatabase(`memories?campaign_id=eq.${campaignId}`, { method: 'DELETE' })
await stageTwoDatabase(`relationships?campaign_id=eq.${campaignId}`, { method: 'DELETE' })
await stageTwoDatabase(`audit_entries?campaign_id=eq.${campaignId}`, { method: 'DELETE' })
// Delete linked world record if it has no other campaigns
const campaigns = await stageTwoDatabase<Array<{ world_id: string }>>(
`campaigns?select=world_id&id=eq.${campaignId}`,
)
const worldId = campaigns[0]?.world_id as string | undefined
if (worldId) {
const otherCampaigns = await stageTwoDatabase<Array<{ id: string }>>(
`campaigns?select=id&world_id=eq.${worldId}&id=neq.${campaignId}`,
)
if (!otherCampaigns.length) {
await stageTwoDatabase(`world_entities?world_id=eq.${worldId}`, { method: 'DELETE' })
await stageTwoDatabase(`worlds?id=eq.${worldId}`, { method: 'DELETE' })
}
}
await stageTwoDatabase(`campaign_members?campaign_id=eq.${campaignId}`, { method: 'DELETE' })
await stageTwoDatabase(`invites?campaign_id=eq.${campaignId}`, { method: 'DELETE' })
await stageTwoDatabase(`campaigns?id=eq.${campaignId}`, { method: 'DELETE' })
return { success: true }
} catch (error) {
stageTwoApiError(error)
}
})

View File

@@ -0,0 +1,27 @@
import { z } from 'zod'
import { requireCampaignAccess, requireStageTwoSafeText, stageTwoApiError, stageTwoDatabase, stageTwoUuid } from '~/server/utils/stage-two-supabase'
import { requireStageTwoUser } from '~/server/utils/stage-two-supabase'
const PatchBodySchema = z.object({
title: z.string().trim().min(1).max(200),
}).strict()
export default defineEventHandler(async (event) => {
try {
const user = await requireStageTwoUser(event)
const campaignId = stageTwoUuid(event.context.params?.id ?? '')
await requireCampaignAccess(campaignId, user.id, true)
const body = PatchBodySchema.parse(await readBody(event))
requireStageTwoSafeText(body.title)
const campaigns = await stageTwoDatabase<Array<Record<string, unknown>>>(`campaigns?id=eq.${campaignId}&limit=1`, {
method: 'PATCH',
body: JSON.stringify({ title: body.title, updated_at: new Date().toISOString() }),
prefer: 'return=representation',
})
if (!campaigns[0]) throw createError({ statusCode: 404, statusMessage: 'Campaign not found.' })
return { campaign: campaigns[0] }
} catch (error) {
stageTwoApiError(error)
}
})