Files
commitea/apps/desktop/src/renderer/src/components/screens/focus-screen.tsx
Croissant Le Doux 5176589f0c feat(desktop): complete the P3 screens — Board, Runway, Issue, Capture (#17–#20)
Ports the nine remaining design-handoff screens to typed TSX and wires
them into the shell, retiring the placeholders for every primary view.

- The pot (#17 / P3-4): BoardScreen 5-column kanban with live search +
  empty state, Tabs into GanttView (state bars, critical inset rules,
  dotted 80% tails, today rule) and DepsGraph (layered DAG, bezier edges,
  spruce critical path, milestone terminal).
- Runway (#18 / P3-5): RunwayScreen (range bars via RunwayBar, capacity,
  calibration teaser), CalibrationScreen (scatter + honest diagonal +
  ×1.18 fit, per-label/per-person bias), MilestoneScreen (stats strip,
  cone, grouped issues).
- Issue + Inbox (#19 / P3-6): IssueScreen (human intent left, machine-
  derived lifecycle/forecast/deps right, "writes as you" composer),
  InboxScreen (day-grouped, filter tabs, unread dots, mark-all-read).
- Capture (#20 / P3-7): CaptureScreen dump→interview→review→filed state
  machine with running clock.

Shell: openIssue now carries an IssueRef; adds readIds (inbox) + issue
state; routes board/runway/calibration/milestone/issue/inbox/capture and
the runway→calibration/milestone + issue back-stack. Focus/Standup pass
IssueRefs. Select/Input gain defaultValue (Capture's editable tray).
Fixtures mirrored from data.js. typecheck + 11 e2e green; all nine
screens screenshot-verified in light.

Closes P3-4, P3-5, P3-6, P3-7.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 15:41:45 -04:00

100 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React from 'react'
import { FOCUS, type FocusIssue, type IssueRef, TODAY } from '../../data/fixtures.js'
import { BurnUpCone } from '../charts/chart.js'
import { Badge, Button, Card, IconButton, Tag } from '../ui/index.js'
/**
* Morning service — the Now/Next/Later focus cards + the milestone burn-up cone.
* Data is fixture (data.js) until P2's scheduler + Monte Carlo feed it.
*/
export function FocusScreen({ onOpenIssue }: { onOpenIssue: (issue: IssueRef) => void }) {
const FocusRow = ({ slot, issue, jade }: { slot: string; issue: FocusIssue; jade?: boolean }) => (
<Card
overline={slot}
jade={jade}
title={
<a
href="#"
onClick={(e) => {
e.preventDefault()
onOpenIssue({ id: issue.id, title: issue.title, labels: issue.labels })
}}
style={{ color: 'inherit', border: 'none' }}
>
{issue.title}
</a>
}
actions={<IconButton icon="ellipsis" label="More" size="sm" />}
footer={
jade ? (
<>
<Button size="sm">Start</Button>
<Button size="sm" variant="ghost">
Defer
</Button>
<span style={{ marginLeft: 'auto', font: 'var(--text-caption)', color: 'var(--ink-3)' }}>
scheduler pick · critical path
</span>
</>
) : null
}
>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10, flexWrap: 'wrap' }}>
<span style={{ font: 'var(--text-data)', color: 'var(--ink-3)' }}>#{issue.id}</span>
{issue.labels.map((l) => (
<Tag key={l} label={l} />
))}
{issue.steeping ? (
<Badge tone="warn" dot>
steeping {issue.steeping}
</Badge>
) : null}
</div>
<p style={{ font: 'var(--text-agent)', color: 'var(--ink-2)', margin: 0 }}>{issue.rationale}</p>
</Card>
)
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
<header
style={{
borderBottom: 'var(--rule-double)',
paddingBottom: 14,
display: 'flex',
alignItems: 'baseline',
justifyContent: 'space-between',
gap: 12,
}}
>
<div>
<h1 style={{ font: 'var(--text-display)', color: 'var(--ink-1)', margin: 0 }}>Morning service</h1>
<p style={{ font: 'var(--text-data)', color: 'var(--ink-3)', margin: '6px 0 0', whiteSpace: 'nowrap' }}>
{TODAY} · reconcile 3.2s
</p>
</div>
<Badge tone="ok" dot>
ahead of forecast
</Badge>
</header>
<div style={{ display: 'grid', gridTemplateColumns: '1.4fr 1fr 1fr', gap: 14, alignItems: 'start' }}>
<FocusRow slot="Now" issue={FOCUS.now} jade />
<FocusRow slot="Next" issue={FOCUS.next} />
<FocusRow slot="Later" issue={FOCUS.later} />
</div>
<Card
overline="Milestone · Beta"
title={<>80% this lands <span style={{ whiteSpace: 'nowrap' }}>Mar 312</span></>}
actions={<IconButton icon="chart-line" label="Open runway" size="sm" />}
>
<BurnUpCone />
<p style={{ font: 'var(--text-agent)', color: 'var(--ink-2)', margin: '10px 0 0' }}>
The cone has narrowed since Friday. Im quietly pleased.
</p>
</Card>
</div>
)
}