Fix round deletion FK constraint with migration and defensive code
Build and Push Docker Image / build (push) Failing after 7m3s
Details
Build and Push Docker Image / build (push) Failing after 7m3s
Details
- Add SQL migration to CASCADE Evaluation.formId and SET NULL ProjectFile.roundId - Explicitly delete evaluations in round delete transaction as defensive measure - Make sidebar Apply Page link dynamic using current edition context Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
5c4200158f
commit
573785e440
|
|
@ -0,0 +1,13 @@
|
|||
-- Fix round deletion FK constraint errors
|
||||
-- Add CASCADE on Evaluation.formId so deleting EvaluationForm cascades to Evaluations
|
||||
-- Add SET NULL on ProjectFile.roundId so deleting Round nullifies the reference
|
||||
|
||||
-- AlterTable: Evaluation.formId -> onDelete CASCADE
|
||||
ALTER TABLE "Evaluation" DROP CONSTRAINT "Evaluation_formId_fkey";
|
||||
ALTER TABLE "Evaluation" ADD CONSTRAINT "Evaluation_formId_fkey"
|
||||
FOREIGN KEY ("formId") REFERENCES "EvaluationForm"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AlterTable: ProjectFile.roundId -> onDelete SET NULL
|
||||
ALTER TABLE "ProjectFile" DROP CONSTRAINT "ProjectFile_roundId_fkey";
|
||||
ALTER TABLE "ProjectFile" ADD CONSTRAINT "ProjectFile_roundId_fkey"
|
||||
FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
|
@ -38,6 +38,7 @@ import {
|
|||
import { getInitials } from '@/lib/utils'
|
||||
import { Logo } from '@/components/shared/logo'
|
||||
import { EditionSelector } from '@/components/shared/edition-selector'
|
||||
import { useEdition } from '@/contexts/edition-context'
|
||||
import { UserAvatar } from '@/components/shared/user-avatar'
|
||||
import { NotificationBell } from '@/components/shared/notification-bell'
|
||||
import { trpc } from '@/lib/trpc/client'
|
||||
|
|
@ -145,10 +146,19 @@ export function AdminSidebar({ user }: AdminSidebarProps) {
|
|||
const pathname = usePathname()
|
||||
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false)
|
||||
const { data: avatarUrl } = trpc.avatar.getUrl.useQuery()
|
||||
const { currentEdition } = useEdition()
|
||||
|
||||
const isSuperAdmin = user.role === 'SUPER_ADMIN'
|
||||
const roleLabel = roleLabels[user.role || ''] || 'User'
|
||||
|
||||
// Build dynamic admin nav with current edition's apply page
|
||||
const dynamicAdminNav = adminNavigation.map((item) => {
|
||||
if (item.name === 'Apply Page' && currentEdition?.id) {
|
||||
return { ...item, href: `/admin/programs/${currentEdition.id}/apply-settings` }
|
||||
}
|
||||
return item
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Mobile menu button */}
|
||||
|
|
@ -235,7 +245,7 @@ export function AdminSidebar({ user }: AdminSidebarProps) {
|
|||
<p className="mb-2 px-3 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground/60">
|
||||
Administration
|
||||
</p>
|
||||
{adminNavigation.map((item) => {
|
||||
{dynamicAdminNav.map((item) => {
|
||||
let isActive = pathname.startsWith(item.href)
|
||||
if (item.activeMatch) {
|
||||
isActive = pathname.includes(item.activeMatch)
|
||||
|
|
|
|||
|
|
@ -615,6 +615,12 @@ export const roundRouter = router({
|
|||
userAgent: ctx.userAgent,
|
||||
})
|
||||
|
||||
// Delete evaluations first to avoid FK constraint on Evaluation.formId
|
||||
// (formId FK may not have CASCADE in older DB schemas)
|
||||
await tx.evaluation.deleteMany({
|
||||
where: { form: { roundId: input.id } },
|
||||
})
|
||||
|
||||
await tx.round.delete({
|
||||
where: { id: input.id },
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue