feat: Wire up initiative deletion end-to-end

Add deleteInitiative tRPC procedure, wire Delete button in InitiativeCard
with confirm dialog (Shift+click bypass), remove unused onDelete prop chain.
Fix agents table FK constraints (initiative_id, account_id missing ON DELETE
SET NULL) via table recreation migration. Register conversations migration
in journal. Expand cascade delete tests to cover pages, projects, change
sets, agents (set null), and conversations (set null).
This commit is contained in:
Lukas May
2026-02-18 17:54:53 +09:00
parent 80aa3e42fb
commit 6fa025251e
8 changed files with 180 additions and 13 deletions

View File

@@ -27,19 +27,20 @@ interface InitiativeCardProps {
initiative: SerializedInitiative;
onView: () => void;
onSpawnArchitect: (mode: "discuss" | "plan") => void;
onDelete: () => void;
}
export function InitiativeCard({
initiative,
onView,
onSpawnArchitect,
onDelete,
}: InitiativeCardProps) {
const utils = trpc.useUtils();
const archiveMutation = trpc.updateInitiative.useMutation({
onSuccess: () => utils.listInitiatives.invalidate(),
});
const deleteMutation = trpc.deleteInitiative.useMutation({
onSuccess: () => utils.listInitiatives.invalidate(),
});
function handleArchive(e: React.MouseEvent) {
if (
@@ -51,6 +52,16 @@ export function InitiativeCard({
archiveMutation.mutate({ id: initiative.id, status: "archived" });
}
function handleDelete(e: React.MouseEvent) {
if (
!e.shiftKey &&
!window.confirm(`Delete "${initiative.name}"? This cannot be undone.`)
) {
return;
}
deleteMutation.mutate({ id: initiative.id });
}
// Each card fetches its own phase stats (N+1 acceptable for v1 small counts)
const phasesQuery = trpc.listPhases.useQuery({
initiativeId: initiative.id,
@@ -128,7 +139,7 @@ export function InitiativeCard({
<DropdownMenuSeparator />
<DropdownMenuItem
className="text-destructive"
onClick={onDelete}
onClick={handleDelete}
>
Delete
</DropdownMenuItem>

View File

@@ -13,7 +13,6 @@ interface InitiativeListProps {
initiativeId: string,
mode: "discuss" | "plan",
) => void;
onDeleteInitiative: (id: string) => void;
}
export function InitiativeList({
@@ -21,7 +20,6 @@ export function InitiativeList({
onCreateNew,
onViewInitiative,
onSpawnArchitect,
onDeleteInitiative,
}: InitiativeListProps) {
const initiativesQuery = trpc.listInitiatives.useQuery(
statusFilter === "all" ? undefined : { status: statusFilter },
@@ -95,7 +93,6 @@ export function InitiativeList({
initiative={initiative}
onView={() => onViewInitiative(initiative.id)}
onSpawnArchitect={(mode) => onSpawnArchitect(initiative.id, mode)}
onDelete={() => onDeleteInitiative(initiative.id)}
/>
))}
</div>

View File

@@ -67,10 +67,6 @@ function DashboardPage() {
// Architect spawning is self-contained within SpawnArchitectDropdown
// This callback is available for future toast notifications
}}
onDeleteInitiative={(_id) => {
// Delete is a placeholder (no deleteInitiative tRPC procedure yet)
// ActionMenu handles this internally when implemented
}}
/>
{/* Create initiative dialog */}