// Gantt drill-in — scheduler-derived bars, critical chain, 80% forecast tails
function GanttView({ onOpenIssue }) {
const DS = window.CommiTeaDesignSystem_20e63b;
const { Icon } = DS;
const g = window.CT_DATA.gantt;
const LABELW = 232, DAYW = 21, ROWH = 36, HEADH = 30;
const chartW = g.days * DAYW;
const W = LABELW + chartW;
const H = HEADH + g.rows.length * ROWH;
const X = (d) => LABELW + d * DAYW;
const BAR = {
done: { bg: 'var(--paper-3)', border: 'transparent', text: 'var(--ink-3)' },
steeping: { bg: 'var(--accent)', border: 'transparent', text: 'var(--ink-inverse)' },
review: { bg: 'var(--info-tint)', border: 'var(--info)', text: 'var(--info)' },
scheduled: { bg: 'var(--spruce-2)', border: 'var(--spruce-3)', text: 'var(--accent-text)' },
};
return (
{/* legend */}
{[['steeping', 'in work'], ['review', 'in review'], ['scheduled', 'scheduled'], ['done', 'done']].map(([k, label]) => (
{label}
))}
80% tail
today
{/* chart */}
{/* week gridlines + labels */}
{g.weeks.map((w) => (
{w.label}
))}
{/* milestone 80% band */}
{g.band.label}
{/* due marker */}
{/* today rule */}
{/* rows */}
{g.rows.map((r, i) => {
const top = HEADH + i * ROWH;
const st = BAR[r.state];
return (
{/* row hairline */}
{/* label cell (sticky) */}
onOpenIssue({ id: r.id, title: r.title, labels: [], days: r.state === 'steeping' ? '4d' : undefined })}
style={{
position: 'absolute', left: 0, width: LABELW, height: ROWH, top: top, zIndex: 2, cursor: 'pointer',
display: 'flex', alignItems: 'center', gap: 8, padding: '0 12px 0 14px', background: 'var(--surface-card)',
borderRight: '1px solid var(--line-1)',
boxShadow: r.crit ? 'inset 2px 0 0 var(--accent)' : 'none',
}}
>
#{r.id}
{r.title}
{r.who}
{/* bar */}
{/* 80% tail */}
{r.p80 ? (
<>
>
) : null}
);
})}
The path holds if #87 lands by Wednesday. The dotted tails are your own history, wagging.
);
}
Object.assign(window, { GanttView });