use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use uuid::Uuid; /// Status of a flow #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum FlowStatus { /// Flow is in progress InProgress, /// Flow is completed Completed, /// Flow is stuck at a step Stuck, /// Flow is cancelled Cancelled, } impl std::fmt::Display for FlowStatus { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { FlowStatus::InProgress => write!(f, "In Progress"), FlowStatus::Completed => write!(f, "Completed"), FlowStatus::Stuck => write!(f, "Stuck"), FlowStatus::Cancelled => write!(f, "Cancelled"), } } } /// Type of flow #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum FlowType { /// Company registration flow CompanyRegistration, /// User onboarding flow UserOnboarding, /// Service activation flow ServiceActivation, /// Payment processing flow PaymentProcessing, /// Asset tokenization flow AssetTokenization, /// Certification flow Certification, /// License application flow LicenseApplication, } impl std::fmt::Display for FlowType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { FlowType::CompanyRegistration => write!(f, "Company Registration"), FlowType::UserOnboarding => write!(f, "User Onboarding"), FlowType::ServiceActivation => write!(f, "Service Activation"), FlowType::PaymentProcessing => write!(f, "Payment Processing"), FlowType::AssetTokenization => write!(f, "Asset Tokenization"), FlowType::Certification => write!(f, "Certification"), FlowType::LicenseApplication => write!(f, "License Application"), } } } /// Filter for flows #[derive(Debug, Clone, Serialize, Deserialize)] pub enum FlowFilter { /// All flows All, /// Only in progress flows InProgress, /// Only completed flows Completed, /// Only stuck flows Stuck, /// Only cancelled flows Cancelled, /// Flows of a specific type ByType(FlowType), } impl std::fmt::Display for FlowFilter { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { FlowFilter::All => write!(f, "All"), FlowFilter::InProgress => write!(f, "In Progress"), FlowFilter::Completed => write!(f, "Completed"), FlowFilter::Stuck => write!(f, "Stuck"), FlowFilter::Cancelled => write!(f, "Cancelled"), FlowFilter::ByType(flow_type) => write!(f, "Type: {}", flow_type), } } } /// A step in a flow #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FlowStep { /// Step ID pub id: String, /// Step name pub name: String, /// Step description pub description: String, /// Step status pub status: StepStatus, /// Step order in the flow pub order: u32, /// Step started at pub started_at: Option>, /// Step completed at pub completed_at: Option>, /// Step logs pub logs: Vec, } impl FlowStep { /// Creates a new flow step pub fn new(name: String, description: String, order: u32) -> Self { Self { id: Uuid::new_v4().to_string(), name, description, status: StepStatus::Pending, order, started_at: None, completed_at: None, logs: Vec::new(), } } /// Starts the step pub fn start(&mut self) { self.status = StepStatus::InProgress; self.started_at = Some(Utc::now()); self.add_log("Step started".to_string()); } /// Completes the step pub fn complete(&mut self) { self.status = StepStatus::Completed; self.completed_at = Some(Utc::now()); self.add_log("Step completed".to_string()); } /// Marks the step as stuck pub fn mark_stuck(&mut self, reason: String) { self.status = StepStatus::Stuck; self.add_log(format!("Step stuck: {}", reason)); } /// Adds a log entry to the step pub fn add_log(&mut self, message: String) { self.logs.push(FlowLog::new(message)); } } /// Status of a step in a flow #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum StepStatus { /// Step is pending Pending, /// Step is in progress InProgress, /// Step is completed Completed, /// Step is stuck Stuck, /// Step is skipped Skipped, } impl std::fmt::Display for StepStatus { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { StepStatus::Pending => write!(f, "Pending"), StepStatus::InProgress => write!(f, "In Progress"), StepStatus::Completed => write!(f, "Completed"), StepStatus::Stuck => write!(f, "Stuck"), StepStatus::Skipped => write!(f, "Skipped"), } } } /// A log entry in a flow step #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FlowLog { /// Log ID pub id: String, /// Log message pub message: String, /// Log timestamp pub timestamp: DateTime, } impl FlowLog { /// Creates a new flow log pub fn new(message: String) -> Self { Self { id: Uuid::new_v4().to_string(), message, timestamp: Utc::now(), } } } /// A flow with multiple steps #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Flow { /// Flow ID pub id: String, /// Flow name pub name: String, /// Flow description pub description: String, /// Flow type pub flow_type: FlowType, /// Flow status pub status: FlowStatus, /// Flow owner ID pub owner_id: String, /// Flow owner name pub owner_name: String, /// Flow created at pub created_at: DateTime, /// Flow updated at pub updated_at: DateTime, /// Flow completed at pub completed_at: Option>, /// Flow steps pub steps: Vec, /// Progress percentage pub progress_percentage: u8, /// Current step pub current_step: Option, } impl Flow { /// Creates a new flow pub fn new(name: &str, description: &str, flow_type: FlowType, owner_id: &str, owner_name: &str) -> Self { let id = Uuid::new_v4().to_string(); let now = Utc::now(); let steps = vec![ FlowStep::new("Initialization".to_string(), "Setting up the flow".to_string(), 1), FlowStep::new("Processing".to_string(), "Processing the flow data".to_string(), 2), FlowStep::new("Finalization".to_string(), "Completing the flow".to_string(), 3), ]; // Set the first step as in progress let mut flow = Self { id, name: name.to_string(), description: description.to_string(), flow_type, status: FlowStatus::InProgress, owner_id: owner_id.to_string(), owner_name: owner_name.to_string(), steps, created_at: now, updated_at: now, completed_at: None, progress_percentage: 0, current_step: None, }; // Calculate progress and set current step flow.update_progress(); flow } fn update_progress(&mut self) { // Calculate progress percentage let total_steps = self.steps.len(); if total_steps == 0 { self.progress_percentage = 100; return; } let completed_steps = self.steps.iter().filter(|s| s.status == StepStatus::Completed).count(); self.progress_percentage = ((completed_steps as f32 / total_steps as f32) * 100.0) as u8; // Find current step self.current_step = self.steps.iter() .find(|s| s.status == StepStatus::InProgress) .cloned(); // Update flow status based on steps if self.progress_percentage == 100 { self.status = FlowStatus::Completed; } else if self.steps.iter().any(|s| s.status == StepStatus::Stuck) { self.status = FlowStatus::Stuck; } else { self.status = FlowStatus::InProgress; } } pub fn advance_step(&mut self) -> Result<(), String> { let current_index = self.steps.iter().position(|s| s.status == StepStatus::InProgress); if let Some(index) = current_index { // Mark current step as completed self.steps[index].status = StepStatus::Completed; self.steps[index].completed_at = Some(Utc::now()); // If there's a next step, mark it as in progress if index + 1 < self.steps.len() { self.steps[index + 1].status = StepStatus::InProgress; self.steps[index + 1].started_at = Some(Utc::now()); } self.updated_at = Utc::now(); self.update_progress(); Ok(()) } else { Err("No step in progress to advance".to_string()) } } pub fn mark_step_stuck(&mut self, reason: &str) -> Result<(), String> { let current_index = self.steps.iter().position(|s| s.status == StepStatus::InProgress); if let Some(index) = current_index { // Mark current step as stuck self.steps[index].status = StepStatus::Stuck; // Add a log entry for the stuck reason self.steps[index].add_log(reason.to_string()); self.updated_at = Utc::now(); self.update_progress(); Ok(()) } else { Err("No step in progress to mark as stuck".to_string()) } } pub fn add_log_to_step(&mut self, step_id: &str, message: &str) -> Result<(), String> { if let Some(step) = self.steps.iter_mut().find(|s| s.id == step_id) { step.add_log(message.to_string()); self.updated_at = Utc::now(); Ok(()) } else { Err(format!("Step with ID {} not found", step_id)) } } } /// Flow statistics #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FlowStatistics { /// Total number of flows pub total_flows: usize, /// Number of in progress flows pub in_progress_flows: usize, /// Number of completed flows pub completed_flows: usize, /// Number of stuck flows pub stuck_flows: usize, /// Number of cancelled flows pub cancelled_flows: usize, } impl FlowStatistics { /// Creates new flow statistics pub fn new(flows: &[Flow]) -> Self { let total_flows = flows.len(); let in_progress_flows = flows.iter() .filter(|flow| flow.status == FlowStatus::InProgress) .count(); let completed_flows = flows.iter() .filter(|flow| flow.status == FlowStatus::Completed) .count(); let stuck_flows = flows.iter() .filter(|flow| flow.status == FlowStatus::Stuck) .count(); let cancelled_flows = flows.iter() .filter(|flow| flow.status == FlowStatus::Cancelled) .count(); Self { total_flows, in_progress_flows, completed_flows, stuck_flows, cancelled_flows, } } }