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:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 */}
|
||||
|
||||
Reference in New Issue
Block a user