//! Decision Workflows API endpoints. use axum::{ extract::{Path, State}, http::StatusCode, routing::{get, post}, Json, Router, }; use serde::Deserialize; use serde_json::{json, Value}; use sqlx::PgPool; use uuid::Uuid; use crate::auth::AuthUser; use crate::plugins::builtin::decision_workflows::{ WorkflowInstance, WorkflowPhase, WorkflowService, WorkflowTemplate, }; // ============================================================================ // Request Types // ============================================================================ #[derive(Debug, Deserialize)] pub struct CreateTemplateRequest { pub name: String, pub description: Option, } #[derive(Debug, Deserialize)] pub struct AddPhaseRequest { pub name: String, pub phase_type: String, pub sequence_order: i32, pub duration_hours: i32, pub quorum_value: f64, } // ============================================================================ // Handlers // ============================================================================ /// List workflow templates for a community async fn list_templates( Path(community_id): Path, State(pool): State, ) -> Result>, (StatusCode, String)> { let templates = WorkflowService::list_templates(&pool, Some(community_id)) .await .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?; Ok(Json(templates)) } /// Create a new workflow template async fn create_template( auth: AuthUser, Path(community_id): Path, State(pool): State, Json(req): Json, ) -> Result, (StatusCode, String)> { let template_id = WorkflowService::create_template( &pool, community_id, &req.name, req.description.as_deref(), auth.user_id, ) .await .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?; Ok(Json(json!({"id": template_id}))) } /// Get phases for a template async fn get_template_phases( Path(template_id): Path, State(pool): State, ) -> Result>, (StatusCode, String)> { let phases = WorkflowService::get_template_phases(&pool, template_id) .await .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?; Ok(Json(phases)) } /// Add a phase to a template async fn add_phase( _auth: AuthUser, Path(template_id): Path, State(pool): State, Json(req): Json, ) -> Result, (StatusCode, String)> { let phase_id = WorkflowService::add_phase( &pool, template_id, &req.name, &req.phase_type, req.sequence_order, req.duration_hours, req.quorum_value, ) .await .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?; Ok(Json(json!({"id": phase_id}))) } /// Get workflow for a proposal async fn get_workflow_for_proposal( Path(proposal_id): Path, State(pool): State, ) -> Result>, (StatusCode, String)> { let workflow = WorkflowService::get_workflow_for_proposal(&pool, proposal_id) .await .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?; Ok(Json(workflow)) } /// Get workflow progress async fn get_workflow_progress( Path(proposal_id): Path, State(pool): State, ) -> Result, (StatusCode, String)> { let progress = WorkflowService::get_workflow_progress(&pool, proposal_id) .await .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?; Ok(Json(progress)) } /// Manually advance workflow phase async fn advance_phase( auth: AuthUser, Path(proposal_id): Path, State(pool): State, ) -> Result, (StatusCode, String)> { let success = WorkflowService::advance_phase(&pool, proposal_id, Some(auth.user_id), None) .await .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?; Ok(Json(json!({"success": success}))) } // ============================================================================ // Router // ============================================================================ pub fn router(pool: PgPool) -> Router { Router::new() // Templates .route("/api/communities/{community_id}/workflow-templates", get(list_templates).post(create_template)) .route("/api/workflow-templates/{template_id}/phases", get(get_template_phases).post(add_phase)) // Proposal workflows .route("/api/proposals/{proposal_id}/workflow", get(get_workflow_for_proposal)) .route("/api/proposals/{proposal_id}/workflow/progress", get(get_workflow_progress)) .route("/api/proposals/{proposal_id}/workflow/advance", post(advance_phase)) .with_state(pool) }