// API client module for VAPORA frontend // Handles all HTTP communication with backend use gloo_net::http::Request; use crate::config::AppConfig; // Re-export types from vapora-shared pub use vapora_shared::models::{ Agent, Project, Task, TaskPriority, TaskStatus, Workflow, }; /// API client for backend communication #[derive(Clone)] pub struct ApiClient { base_url: String, } impl ApiClient { /// Create new API client with configuration pub fn new(config: &AppConfig) -> Self { Self { base_url: config.api_url.clone(), } } /// Fetch all projects for a tenant pub async fn fetch_projects(&self, tenant_id: &str) -> Result, String> { let url = format!("{}/api/v1/projects?tenant_id={}", self.base_url, tenant_id); Request::get(&url) .send() .await .map_err(|e| format!("Failed to send request: {}", e))? .json() .await .map_err(|e| format!("Failed to parse response: {}", e)) } /// Fetch single project by ID pub async fn fetch_project(&self, project_id: &str) -> Result { let url = format!("{}/api/v1/projects/{}", self.base_url, project_id); Request::get(&url) .send() .await .map_err(|e| format!("Failed to send request: {}", e))? .json() .await .map_err(|e| format!("Failed to parse response: {}", e)) } /// Create new project pub async fn create_project(&self, project: &Project) -> Result { let url = format!("{}/api/v1/projects", self.base_url); let body = serde_json::to_string(project).map_err(|e| e.to_string())?; Request::post(&url) .header("Content-Type", "application/json") .body(body) .map_err(|e| e.to_string())? .send() .await .map_err(|e| format!("Failed to send request: {}", e))? .json() .await .map_err(|e| format!("Failed to parse response: {}", e)) } /// Fetch all tasks for a project pub async fn fetch_tasks(&self, project_id: &str) -> Result, String> { let url = format!("{}/api/v1/tasks?project_id={}", self.base_url, project_id); Request::get(&url) .send() .await .map_err(|e| format!("Failed to send request: {}", e))? .json() .await .map_err(|e| format!("Failed to parse response: {}", e)) } /// Create new task pub async fn create_task(&self, task: &Task) -> Result { let url = format!("{}/api/v1/tasks", self.base_url); let body = serde_json::to_string(task).map_err(|e| e.to_string())?; Request::post(&url) .header("Content-Type", "application/json") .body(body) .map_err(|e| e.to_string())? .send() .await .map_err(|e| format!("Failed to send request: {}", e))? .json() .await .map_err(|e| format!("Failed to parse response: {}", e)) } /// Update task status pub async fn update_task_status(&self, task_id: &str, status: TaskStatus) -> Result { let url = format!("{}/api/v1/tasks/{}", self.base_url, task_id); let body = serde_json::json!({ "status": status }).to_string(); Request::put(&url) .header("Content-Type", "application/json") .body(body) .map_err(|e| e.to_string())? .send() .await .map_err(|e| format!("Failed to send request: {}", e))? .json() .await .map_err(|e| format!("Failed to parse response: {}", e)) } /// Reorder task (drag & drop support) pub async fn reorder_task( &self, task_id: &str, new_order: i32, new_status: Option, ) -> Result { let url = format!("{}/api/v1/tasks/{}/reorder", self.base_url, task_id); let body = serde_json::json!({ "task_order": new_order, "status": new_status }) .to_string(); Request::put(&url) .header("Content-Type", "application/json") .body(body) .map_err(|e| e.to_string())? .send() .await .map_err(|e| format!("Failed to send request: {}", e))? .json() .await .map_err(|e| format!("Failed to parse response: {}", e)) } /// Fetch all agents pub async fn fetch_agents(&self) -> Result, String> { let url = format!("{}/api/v1/agents", self.base_url); Request::get(&url) .send() .await .map_err(|e| format!("Failed to send request: {}", e))? .json() .await .map_err(|e| format!("Failed to parse response: {}", e)) } /// Fetch single agent by ID pub async fn fetch_agent(&self, agent_id: &str) -> Result { let url = format!("{}/api/v1/agents/{}", self.base_url, agent_id); Request::get(&url) .send() .await .map_err(|e| format!("Failed to send request: {}", e))? .json() .await .map_err(|e| format!("Failed to parse response: {}", e)) } /// Fetch all workflows for a tenant pub async fn fetch_workflows(&self, tenant_id: &str) -> Result, String> { let url = format!("{}/api/v1/workflows?tenant_id={}", self.base_url, tenant_id); Request::get(&url) .send() .await .map_err(|e| format!("Failed to send request: {}", e))? .json() .await .map_err(|e| format!("Failed to parse response: {}", e)) } }