feat(reports): show chapter progress while a report is being written (BUG-601)

The worker already persists a phase ladder and durable per-section rows;
the waiting screen parsed the progress fields and rendered none of them.
Chapter progress now drives the screen: one cell per chapter rather than
a percentage bar, since the job percent jumps 0->30 and 90->100.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016P5RoqzmUQEbeC2qjAkeGr
This commit is contained in:
Jesse_Chen
2026-09-09 03:30:15 +00:00
co-authored by Claude Fable 5
parent 301eae4b76
commit 848e39e61f
9 changed files with 684 additions and 18 deletions
@@ -57,9 +57,16 @@ async function resolvePersistenceForUser() {
userId: user.id,
persistence,
jobs: createSupabasePersonalReportJobService(supabase),
listSections: async (ownerId: string, requestId: string) => {
// sectionId and attemptCount are consumed by resolveReportRead to derive
// reader-visible chapter state; neither is forwarded to the browser raw.
listSections: async (ownerId: string, requestId: string) => {
const rows = await sections.list(ownerId, requestId);
return rows.map((row) => ({ status: row.status, lastErrorCode: row.lastErrorCode }));
return rows.map((row) => ({
sectionId: row.sectionId,
status: row.status,
attemptCount: row.attemptCount,
lastErrorCode: row.lastErrorCode,
}));
},
loadLongformAppendix: async (input: Readonly<{ userId: string; reportId: string }>) => {
const appendixRead = await supabase
+59
View File
@@ -3775,6 +3775,65 @@ input:not([type="radio"]):not([type="checkbox"]):not([class^="ant-"]):not([class
text-wrap: pretty;
}
/* Report generation progress: one cell per chapter, never a percentage.
See frontend/DESIGN.md · 报告生成等待态. */
.personal-report-state p.personal-report-progress-headline {
color: var(--color-ink);
font-family: var(--font-display);
font-size: var(--type-display-sm);
font-weight: 400;
letter-spacing: -.3px;
font-variant-numeric: tabular-nums;
}
.report-progress {
display: flex;
flex-direction: column;
gap: var(--space-4);
width: min(20rem, 100%);
}
.report-progress-track {
display: flex;
gap: 4px;
margin: 0;
padding: 0;
list-style: none;
}
.report-progress-track li {
flex: 1 1 0;
height: 4px;
border-radius: 2px;
background: var(--color-border);
}
.report-progress-track li[data-state="done"] { background: var(--color-ink); }
.report-progress-track li[data-state="failed"] { background: var(--color-ink-tertiary); }
.report-progress-track li[data-state="writing"] { background: var(--color-ink-secondary); }
.report-progress-chapters {
display: flex;
flex-direction: column;
gap: var(--space-2);
margin: 0;
padding: 0;
list-style: none;
font-size: var(--type-body-sm);
line-height: 1.5;
}
.report-progress-chapters li {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: var(--space-4);
color: var(--color-ink-secondary);
}
.report-progress-chapters li[data-state="done"] { color: var(--color-ink); }
.report-progress-chapters li[data-state="writing"] { color: var(--color-ink); }
.report-progress-chapter-name { text-align: left; }
.report-progress-chapter-status {
color: var(--color-ink-tertiary);
text-align: right;
white-space: nowrap;
}
.report-progress-chapters li[data-state="waiting"] { color: var(--color-ink-tertiary); }
.personal-report-document {
width: min(900px, 100%);
margin: var(--space-8) auto 0;