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
+14
View File
@@ -324,6 +324,20 @@ pub async fn attach_agent(
Ok(StatusCode::NO_CONTENT)
}
/// `DELETE /api/research/:id` — hard-delete a topic and cascade every
/// dependent row. FK cascades on research_topic_agents,
/// research_publish_approvals, and research_outcomes; topology_runs's
/// research_topic_id back-ref is SET NULL so historical runs survive.
/// Returns 204 whether the topic existed or not (idempotent).
pub async fn delete_topic(
State(state): State<AppState>,
Authed(user): Authed,
Path(id): Path<Uuid>,
) -> Result<StatusCode, ApiError> {
cm_db::repo::research_topics::delete(&state.pool, id, user.workspace_id.as_uuid()).await?;
Ok(StatusCode::NO_CONTENT)
}
pub async fn detach_agent(
State(state): State<AppState>,
Authed(user): Authed,