//! Milestone domain types and DTOs. use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use strum_macros::EnumString; use crate::id_types::{MilestoneId, UserId, ProjectId}; use super::shared::{CssClass, DbValue, ParseableEnum}; // ============ Milestones ============ /// Lifecycle status of a milestone. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, EnumString)] pub enum MilestoneStatus { /// Milestone is still being worked toward. #[strum(serialize = "Open")] #[default] Open, /// All milestone tasks are done. #[strum(serialize = "Completed")] Completed, } impl MilestoneStatus { /// Returns a human-readable display string. pub fn as_str(&self) -> &'static str { match self { MilestoneStatus::Open => "Open", MilestoneStatus::Completed => "Completed", } } } impl ParseableEnum for MilestoneStatus {} impl DbValue for MilestoneStatus { fn db_value(&self) -> &'static str { match self { MilestoneStatus::Open => "open", MilestoneStatus::Completed => "completed", } } } impl CssClass for MilestoneStatus { fn css_class(&self) -> &'static str { match self { MilestoneStatus::Open => "milestone-open", MilestoneStatus::Completed => "milestone-completed", } } } /// A project milestone representing a scope boundary. #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Milestone { /// Unique identifier. pub id: MilestoneId, /// Owner user ID. pub user_id: UserId, /// Parent project ID. pub project_id: ProjectId, /// Milestone name. pub name: String, /// Optional description. pub description: String, /// Display order (lower = first). pub position: i32, /// Target completion date. pub target_date: Option, /// Current status. pub status: MilestoneStatus, /// When the milestone was created. pub created_at: DateTime, } /// Data for creating a new milestone. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct NewMilestone { pub project_id: ProjectId, pub name: String, pub description: String, pub position: i32, pub target_date: Option, }