impl TeamOnboarding {
#[must_use]
pub fn new(config: OnboardingConfig) -> Self {
Self {
sessions: HashMap::new(),
tutorials: TutorialLibrary::new(),
config,
}
}
pub fn start_onboarding(
&mut self,
team_id: TeamId,
preferences: TeamPreferences,
) -> Result<OnboardingSession> {
let session = OnboardingSession {
team_id: team_id.clone(),
current_phase: OnboardingPhase::Introduction,
completed_tutorials: Vec::new(),
quality_mode: QualityMode::Observe,
started_at: std::time::SystemTime::now(),
progress: OnboardingProgress {
tutorials_completed: 0,
tutorials_total: self.tutorials.count_tutorials(),
exercises_completed: 0,
quality_improvements: 0,
days_active: 0,
engagement_score: 0.0,
},
preferences,
};
self.sessions.insert(team_id, session.clone());
Ok(session)
}
pub fn get_recommendations(&self, team_id: &TeamId) -> Result<Vec<Tutorial>> {
let session = self
.sessions
.get(team_id)
.ok_or_else(|| anyhow::anyhow!("Team not found: {team_id}"))?;
let phase_tutorials = self
.tutorials
.get_tutorials_for_phase(&session.current_phase);
let mut recommendations = Vec::new();
for tutorial in phase_tutorials {
if !session.completed_tutorials.contains(&tutorial.id) {
let prerequisites_met = tutorial
.prerequisites
.iter()
.all(|prereq| session.completed_tutorials.contains(prereq));
if prerequisites_met {
recommendations.push(tutorial);
}
}
}
recommendations.sort_by(|a, b| {
let relevance_a = self.calculate_relevance(a, &session.preferences);
let relevance_b = self.calculate_relevance(b, &session.preferences);
relevance_b
.partial_cmp(&relevance_a)
.unwrap_or(std::cmp::Ordering::Equal)
});
Ok(recommendations)
}
pub fn complete_tutorial(
&mut self,
team_id: &TeamId,
tutorial_id: String,
exercises_completed: u32,
) -> Result<()> {
let needs_update = {
let session = self
.sessions
.get(team_id)
.ok_or_else(|| anyhow::anyhow!("Team not found: {team_id}"))?;
!session.completed_tutorials.contains(&tutorial_id)
};
if needs_update {
let engagement_score = {
let session = self
.sessions
.get(team_id)
.ok_or_else(|| anyhow::anyhow!("Team not found: {team_id}"))?;
let mut temp_session = session.clone();
temp_session.progress.tutorials_completed += 1;
temp_session.progress.exercises_completed += exercises_completed;
self.calculate_engagement_score(&temp_session)
};
let session = self
.sessions
.get_mut(team_id)
.ok_or_else(|| anyhow::anyhow!("Team not found: {team_id}"))?;
session.completed_tutorials.push(tutorial_id);
session.progress.tutorials_completed += 1;
session.progress.exercises_completed += exercises_completed;
session.progress.engagement_score = engagement_score;
let current_phase = session.current_phase.clone();
let tutorials_completed = session.progress.tutorials_completed;
let should_advance = match current_phase {
OnboardingPhase::Introduction if tutorials_completed >= 2 => true,
OnboardingPhase::MonitoringSetup if tutorials_completed >= 4 => true,
OnboardingPhase::MetricsLearning if tutorials_completed >= 6 => true,
OnboardingPhase::EnforcementConfig if tutorials_completed >= 8 => true,
OnboardingPhase::AutomationSetup if tutorials_completed >= 10 => true,
_ => false,
};
if should_advance {
let next_phase = match current_phase {
OnboardingPhase::Introduction => OnboardingPhase::MonitoringSetup,
OnboardingPhase::MonitoringSetup => OnboardingPhase::MetricsLearning,
OnboardingPhase::MetricsLearning => OnboardingPhase::EnforcementConfig,
OnboardingPhase::EnforcementConfig => OnboardingPhase::AutomationSetup,
OnboardingPhase::AutomationSetup => OnboardingPhase::AdvancedFeatures,
OnboardingPhase::AdvancedFeatures => OnboardingPhase::ProductionReady,
OnboardingPhase::ProductionReady => OnboardingPhase::ProductionReady,
};
session.current_phase = next_phase;
}
}
Ok(())
}
pub fn generate_progress_report(&self, team_id: &TeamId) -> Result<OnboardingReport> {
let session = self
.sessions
.get(team_id)
.ok_or_else(|| anyhow::anyhow!("Team not found: {team_id}"))?;
let completion_percentage = (f64::from(session.progress.tutorials_completed)
/ f64::from(session.progress.tutorials_total))
* 100.0;
let current_phase_progress = self.calculate_phase_progress(session);
let recommended_next_steps = self.get_recommendations(team_id)?;
let achievements = self.calculate_achievements(session);
Ok(OnboardingReport {
team_id: team_id.clone(),
current_phase: session.current_phase.clone(),
overall_completion: completion_percentage,
phase_progress: current_phase_progress,
days_active: session.progress.days_active,
engagement_score: session.progress.engagement_score,
achievements,
recommended_next_steps: recommended_next_steps.into_iter().take(3).collect(),
quality_mode: session.quality_mode,
})
}
}