research sidebar: delete-with-confirm per topic
ci / gates (push) Successful in 6s
ci / frontend (push) Successful in 30s
ci / rust (push) Successful in 2m38s
ci / e2e (push) Has been skipped
ci / publish (push) Successful in 2m40s

Mirror the row-level delete affordance the loops sidebar already
has. Loops was wired earlier; research had a bare title-only card
with no way to remove a stale topic.

- cm-db: research_topics::delete cascades via existing FK rules
  (research_topic_agents, research_publish_approvals, and the new
  research_outcomes all CASCADE on topic_id; topology_runs's
  research_topic_id back-ref is SET NULL so historical runs stay).
- cm-api: DELETE /api/research/{id} → 204. Idempotent.
- Frontend: deleteTopic helper. ResearchList row is now a card
  with the existing title/status/outcome header plus a trash icon
  that flips the card into an inline "Delete topic + all outcomes?"
  confirm strip. Confirm → red Delete / gray Cancel. If the
  deleted topic was selected, selection clears; local counter
  bumps the list refetch without waiting on a parent.
This commit is contained in:
Omar Sobh
2026-07-08 17:21:51 -07:00
parent a2d3d85ebe
commit 316cdbf929
6 changed files with 224 additions and 38 deletions
+15
View File
@@ -98,6 +98,21 @@ pub async fn get(
Ok(row)
}
/// Hard-delete a topic and cascade every dependent row. FK cascades on
/// research_topic_agents, research_publish_approvals, and research_outcomes
/// clean themselves up; topology_runs.research_topic_id is SET NULL so
/// historical runs survive with the back-ref cleared.
pub async fn delete(pool: &PgPool, id: Uuid, workspace_id: Uuid) -> Result<(), DbError> {
sqlx::query!(
"DELETE FROM research_topics WHERE id = $1 AND workspace_id = $2",
id,
workspace_id,
)
.execute(pool)
.await?;
Ok(())
}
/// Non-status fields; the state machine transitions are their own endpoints.
pub async fn update_fields(
pool: &PgPool,