From e1dac10d27cf39748b97b4e196158246698448f6 Mon Sep 17 00:00:00 2001 From: Julian Tabel Date: Mon, 9 Feb 2026 11:28:22 +0100 Subject: [PATCH] Fix run deletion crash and transfer modal initialization error Run deletion now properly cleans up boss_results, genlocke_transfers, and genlocke_leg references before deleting the run. Also fix showTransferModal being referenced before initialization in RunEncounters by moving its useState declaration above useLegSurvivors. Co-Authored-By: Claude Opus 4.6 --- ...ocke-tracker-p74f--genlocke-transfer-ui.md | 4 +-- backend/src/app/api/runs.py | 33 ++++++++++++++++++- frontend/src/pages/RunEncounters.tsx | 2 +- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/.beans/nuzlocke-tracker-p74f--genlocke-transfer-ui.md b/.beans/nuzlocke-tracker-p74f--genlocke-transfer-ui.md index b79a2ab..fb31ba6 100644 --- a/.beans/nuzlocke-tracker-p74f--genlocke-transfer-ui.md +++ b/.beans/nuzlocke-tracker-p74f--genlocke-transfer-ui.md @@ -1,11 +1,11 @@ --- # nuzlocke-tracker-p74f title: Genlocke transfer UI -status: in-progress +status: completed type: feature priority: normal created_at: 2026-02-09T07:42:33Z -updated_at: 2026-02-09T10:14:34Z +updated_at: 2026-02-09T10:20:53Z parent: nuzlocke-tracker-25mh blocking: - nuzlocke-tracker-lsc2 diff --git a/backend/src/app/api/runs.py b/backend/src/app/api/runs.py index 4d0816f..293740e 100644 --- a/backend/src/app/api/runs.py +++ b/backend/src/app/api/runs.py @@ -6,9 +6,11 @@ from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import joinedload, selectinload from app.core.database import get_session +from app.models.boss_result import BossResult from app.models.encounter import Encounter from app.models.game import Game from app.models.genlocke import Genlocke, GenlockeLeg +from app.models.genlocke_transfer import GenlockeTransfer from app.models.nuzlocke_run import NuzlockeRun from app.schemas.run import RunCreate, RunDetailResponse, RunGenlockeContext, RunResponse, RunUpdate @@ -198,13 +200,42 @@ async def delete_run( if run is None: raise HTTPException(status_code=404, detail="Run not found") - # Delete associated encounters first + # Delete associated boss results first + boss_results = await session.execute( + select(BossResult).where(BossResult.run_id == run_id) + ) + for br in boss_results.scalars(): + await session.delete(br) + + # Delete genlocke transfers referencing this run's encounters + encounter_ids_result = await session.execute( + select(Encounter.id).where(Encounter.run_id == run_id) + ) + encounter_ids = [row[0] for row in encounter_ids_result] + if encounter_ids: + transfers = await session.execute( + select(GenlockeTransfer).where( + GenlockeTransfer.source_encounter_id.in_(encounter_ids) + | GenlockeTransfer.target_encounter_id.in_(encounter_ids) + ) + ) + for t in transfers.scalars(): + await session.delete(t) + + # Delete associated encounters encounters = await session.execute( select(Encounter).where(Encounter.run_id == run_id) ) for enc in encounters.scalars(): await session.delete(enc) + # Unlink from any genlocke leg + leg_result = await session.execute( + select(GenlockeLeg).where(GenlockeLeg.run_id == run_id) + ) + for leg in leg_result.scalars(): + leg.run_id = None + await session.delete(run) await session.commit() return Response(status_code=204) diff --git a/frontend/src/pages/RunEncounters.tsx b/frontend/src/pages/RunEncounters.tsx index c1a59aa..c9024fd 100644 --- a/frontend/src/pages/RunEncounters.tsx +++ b/frontend/src/pages/RunEncounters.tsx @@ -396,6 +396,7 @@ export function RunEncounters() { const runIdNum = Number(runId) const { data: run, isLoading, error } = useRun(runIdNum) const advanceLeg = useAdvanceLeg() + const [showTransferModal, setShowTransferModal] = useState(false) const { data: survivors } = useLegSurvivors( run?.genlocke?.genlockeId ?? 0, run?.genlocke?.legOrder ?? 0, @@ -423,7 +424,6 @@ export function RunEncounters() { const [showHofModal, setShowHofModal] = useState(false) const [showShinyModal, setShowShinyModal] = useState(false) const [showEggModal, setShowEggModal] = useState(false) - const [showTransferModal, setShowTransferModal] = useState(false) const [expandedBosses, setExpandedBosses] = useState>(new Set()) const [showTeam, setShowTeam] = useState(true) const [filter, setFilter] = useState<'all' | RouteStatus>('all')