Project · Plans: a roadmap, a burndown, milestones, workload
Roadmap lays initiatives across months in lanes: each a bar from its first day through its last, filled as far as it has come, under the months' names and a line at today; hover a bar for its dates. Bars sit at fractions of the span, so nothing is measured, and a strip too narrow for its months scrolls sideways at a least width per month. GanttChart and TimelineView are the charts GanttChart. BurndownChart lives with the charts, on their scales, labels, tiles and paint: the ideal line from the scope down to nothing, what was left at each day's end so far, and a line at today; hover a day to read it. MilestoneProgress shows a milestone's issues done as a bar and a count, when it is due, and whether it keeps pace: on track, at risk when the work trails the time gone by more than a tenth, late past its day, or done. WorkloadView sets each person's points against what they can take, the bar turning red past it, in columns that line up. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
fc0c3e564d
commit
75f00883ac
13 files changed
+1113
-8
No files matched your search
@@ -9,6 +9,7 @@ use gpui::{
|
||||
use jiff::{Timestamp, ToSpan, civil::Date, tz::TimeZone};
|
||||
|
||||
mod boards;
|
||||
mod plans;
|
||||
mod tasks;
|
||||
|
||||
use super::Page;
|
||||
@@ -147,5 +148,9 @@ fn render(window: &mut Window, cx: &mut App) -> AnyElement {
|
||||
.child(boards::kanban(window, cx))
|
||||
.child(boards::panel(window, cx))
|
||||
.child(boards::labels(window, cx))
|
||||
.child(plans::roadmap(cx))
|
||||
.child(plans::burndown(cx))
|
||||
.child(plans::milestones(cx))
|
||||
.child(plans::workload(cx))
|
||||
.into_any_element()
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
use ely_gpui_component::{
|
||||
charts::BurndownChart,
|
||||
project::{Initiative, Load, MilestoneProgress, Roadmap, WorkloadView},
|
||||
};
|
||||
use gpui::{App, IntoElement, ParentElement, Styled, div, px};
|
||||
use jiff::{ToSpan, civil::Date};
|
||||
|
||||
use super::{team, today};
|
||||
use crate::{probe::probe, ui::section};
|
||||
|
||||
/// A day `offset` days from `today`.
|
||||
fn day(today: Date, offset: i64) -> Date {
|
||||
today.checked_add(offset.days()).expect("a day nearby")
|
||||
}
|
||||
|
||||
pub fn roadmap(cx: &mut App) -> impl IntoElement + use<> {
|
||||
let today = today();
|
||||
let at = |offset: i64| day(today, offset);
|
||||
let map = Roadmap::new("project-roadmap", at(-40), 6)
|
||||
.today(today)
|
||||
.lane(
|
||||
"Design",
|
||||
[
|
||||
Initiative::new("concept", "Concept", at(-40), at(-12)).progress(1.0),
|
||||
Initiative::new("schematic", "Schematic design", at(-14), at(30)).progress(0.55),
|
||||
Initiative::new("details", "Detail drawings", at(28), at(95)),
|
||||
],
|
||||
)
|
||||
.lane(
|
||||
"Site",
|
||||
[
|
||||
Initiative::new("survey", "Land survey", at(-20), at(6)).progress(0.8),
|
||||
Initiative::new("permit", "Permit set", at(10), at(70)).progress(0.1),
|
||||
],
|
||||
)
|
||||
.lane(
|
||||
"Client",
|
||||
[Initiative::new("reviews", "Monthly reviews", at(-35), at(120)).progress(0.3)],
|
||||
);
|
||||
section(
|
||||
"GanttChart / TimelineView · Roadmap",
|
||||
"A roadmap lays initiatives across months in lanes, each bar filled as far as it has come, under a line at today; hover a bar for its dates. GanttChart and TimelineView are the charts GanttChart, for tasks by the day.",
|
||||
cx,
|
||||
)
|
||||
.child(probe("project-roadmap", div().w(px(720.)).child(map)))
|
||||
}
|
||||
|
||||
pub fn burndown(cx: &mut App) -> impl IntoElement + use<> {
|
||||
let today = today();
|
||||
let (first, last) = (day(today, -6), day(today, 7));
|
||||
let chart = BurndownChart::new("project-burndown", first, last, 40.0)
|
||||
.left([40.0, 38.0, 38.0, 33.0, 34.0, 29.0, 24.0])
|
||||
.today(today);
|
||||
section(
|
||||
"Burndown Chart",
|
||||
"Work left over a sprint: the ideal line from the scope down to nothing, what was left at each day's end so far, and a line at today. Work added mid-sprint lifts the line. Hover a day to read it. It lives with the charts as BurndownChart.",
|
||||
cx,
|
||||
)
|
||||
.child(probe("project-burndown", div().w(px(640.)).child(chart)))
|
||||
}
|
||||
|
||||
pub fn milestones(cx: &mut App) -> impl IntoElement + use<> {
|
||||
let today = today();
|
||||
let at = |offset: i64| day(today, offset);
|
||||
let rows = [
|
||||
MilestoneProgress::new("project-concept", "Concept", (at(-40), at(-12)), (14, 14)),
|
||||
MilestoneProgress::new(
|
||||
"project-schematic",
|
||||
"Schematic design",
|
||||
(at(-14), at(30)),
|
||||
(9, 20),
|
||||
),
|
||||
MilestoneProgress::new("project-permit", "Permit set", (at(-30), at(10)), (4, 16)),
|
||||
MilestoneProgress::new("project-survey", "Land survey", (at(-20), at(-2)), (7, 9)),
|
||||
];
|
||||
section(
|
||||
"MilestoneProgress",
|
||||
"A milestone's issues done as a bar and a count, when it is due, and whether it keeps pace: on track, at risk when the work trails the time gone, late past its day, or done.",
|
||||
cx,
|
||||
)
|
||||
.child(probe(
|
||||
"project-milestones",
|
||||
div()
|
||||
.w(px(420.))
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_6()
|
||||
.children(rows.map(|row| row.today(today))),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn workload(cx: &mut App) -> impl IntoElement + use<> {
|
||||
let people = team();
|
||||
let loads = [(12.0, 10.0), (7.0, 10.0), (9.0, 8.0), (4.0, 10.0)];
|
||||
let rows = people
|
||||
.into_iter()
|
||||
.zip(loads)
|
||||
.map(|(person, (assigned, capacity))| Load::new(person, assigned, capacity));
|
||||
section(
|
||||
"WorkloadView",
|
||||
"Who carries how much this sprint: each person's points against what they can take, the bar turning red past it.",
|
||||
cx,
|
||||
)
|
||||
.child(probe(
|
||||
"project-workload",
|
||||
div().w(px(420.)).child(WorkloadView::new("project-workload", rows, "pts")),
|
||||
))
|
||||
}
|
||||
Reference in new issue
Block a user