use eframe::egui::Color32;
use crate::funnel::{self, NodeStatus};
#[derive(Clone, Debug, Default)]
pub struct FunnelView {
pub plans: Vec<PlanView>,
}
#[derive(Clone, Debug)]
pub struct PlanView {
pub id: String,
pub summary: String,
pub status: String,
pub idea_text: String,
pub nodes: Vec<NodeView>,
}
#[derive(Clone, Debug)]
pub struct NodeView {
pub id: String,
pub kind: String,
pub title: String,
pub status: NodeStat,
pub targets: Vec<String>,
pub deps: Vec<String>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum NodeStat {
Pending,
Ready,
InProgress,
Done,
Blocked,
Failed,
Unknown,
}
impl NodeStat {
pub fn parse(s: &str) -> Self {
match s.trim().to_ascii_lowercase().as_str() {
"pending" => Self::Pending,
"ready" => Self::Ready,
"inprogress" | "in_progress" | "active" => Self::InProgress,
"done" => Self::Done,
"blocked" => Self::Blocked,
"failed" | "abandoned" => Self::Failed,
_ => Self::Unknown,
}
}
pub fn from_node(s: NodeStatus) -> Self {
match s {
NodeStatus::Pending => Self::Pending,
NodeStatus::Ready => Self::Ready,
NodeStatus::InProgress => Self::InProgress,
NodeStatus::Done => Self::Done,
NodeStatus::Blocked => Self::Blocked,
NodeStatus::Failed => Self::Failed,
}
}
pub fn color(self) -> Color32 {
match self {
Self::Done => Color32::from_rgb(90, 200, 120),
Self::InProgress => Color32::from_rgb(80, 165, 235),
Self::Ready => Color32::from_rgb(90, 205, 200),
Self::Pending => Color32::from_rgb(140, 142, 152),
Self::Blocked => Color32::from_rgb(235, 175, 60),
Self::Failed => Color32::from_rgb(225, 85, 85),
Self::Unknown => Color32::from_rgb(120, 120, 120),
}
}
pub fn color_themed(&self, theme: &super::facett_theme::Theme) -> Color32 {
use super::facett_theme::{AMBER, GREEN, RED};
match self {
Self::Done => GREEN,
Self::InProgress => theme.accent,
Self::Ready => theme.point,
Self::Pending => theme.text_dim,
Self::Blocked => AMBER,
Self::Failed => RED,
Self::Unknown => theme.text_dim,
}
}
pub fn glyph(self) -> &'static str {
match self {
Self::Done => "✓",
Self::InProgress => "◐",
Self::Ready => "●",
Self::Pending => "○",
Self::Blocked => "‖",
Self::Failed => "✗",
Self::Unknown => "?",
}
}
pub fn label(self) -> &'static str {
match self {
Self::Done => "done",
Self::InProgress => "in progress",
Self::Ready => "ready",
Self::Pending => "pending",
Self::Blocked => "blocked",
Self::Failed => "failed",
Self::Unknown => "unknown",
}
}
}
impl FunnelView {
pub fn from_funnel(f: &funnel::Funnel) -> Self {
let mut plans = Vec::new();
for plan in f.plans.values() {
let idea_text = f.ideas.get(&plan.idea_id).map(|i| i.text.clone()).unwrap_or_default();
let mut nodes = Vec::with_capacity(plan.nodes.len());
for node in plan.nodes.values() {
let deps = plan
.edges
.iter()
.filter(|(_, to)| to == &node.id)
.map(|(from, _)| from.to_string())
.collect();
let title = node
.prompt_excerpt
.clone()
.filter(|s| !s.is_empty())
.unwrap_or_default();
nodes.push(NodeView {
id: node.id.to_string(),
kind: node.kind.clone(),
title,
status: NodeStat::from_node(node.status),
targets: node.targets.clone(),
deps,
});
}
plans.push(PlanView {
id: plan.id.to_string(),
summary: plan.summary.clone(),
status: format!("{:?}", plan.status).to_ascii_lowercase(),
idea_text,
nodes,
});
}
plans.sort_by(|a, b| a.id.cmp(&b.id));
Self { plans }
}
}