Add a Production dashboard with an animated knowledge graph

The sidebar now groups Production under Dashboard and the four existing
pages under Settings, and Production is the landing page. It shows content
in progress, material, content made, approved and LMS-ready counts, plus a
knowledge graph linking Brain documents, results, found sources and
schedules, with the schedule and what is being made right now beside it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Iqbal
2026-10-27 15:57:10 +07:00
parent 6d4850f4ce
commit ddbdbc7d44
8 changed files with 608 additions and 16 deletions

38
src/express/graph.test.ts Normal file
View File

@@ -0,0 +1,38 @@
import { describe, expect, it } from 'vitest'
import type { Source } from '../api/engine'
import { buildGraph, HEIGHT, WIDTH } from './graph'
import type { FoundSource, Material } from './results'
import type { Schedule } from './schedules'
const doc = (docId: number, title: string) => ({ docId, title, originalName: `${title}.pdf` }) as Source
const material = (id: number, status: Material['status'], sources: string[]) =>
({ id, agentId: 7, kind: 'summary', status, planTitle: `Lesson ${id}`, sources }) as Material
const schedule = { agent: { id: 7, name: 'Express · 2 summary per week' } } as Schedule
describe('buildGraph', () => {
const data = {
docs: [doc(1, 'Onboarding'), doc(2, 'Safety')],
materials: [material(10, 'accepted', ['Onboarding.pdf', 'Safety']), material(11, 'generating', [])],
sources: [{ id: 5, agentId: 7, title: 'Article', documentId: 2 } as FoundSource],
schedules: [schedule],
}
const graph = buildGraph(data, c => c)
it('puts results in progress in their own cluster', () => {
expect(graph.nodes.find(n => n.id === 'mat-7-10')?.cluster).toBe('made')
expect(graph.nodes.find(n => n.id === 'mat-7-11')?.cluster).toBe('working')
})
it('links results to their sources and schedule, and found sources to their document', () => {
const cross = graph.links.filter(l => !l.hub).map(l => `${l.source}>${l.target}`)
expect(cross).toEqual(expect.arrayContaining(['mat-7-10>doc-1', 'mat-7-10>doc-2', 'mat-7-10>agent-7', 'found-7-5>doc-2']))
})
it('lays out the same data the same way, inside the canvas', () => {
expect(buildGraph(data, c => c).nodes).toEqual(graph.nodes)
for (const n of graph.nodes) {
expect(n.x).toBeGreaterThanOrEqual(0); expect(n.x).toBeLessThanOrEqual(WIDTH)
expect(n.y).toBeGreaterThanOrEqual(0); expect(n.y).toBeLessThanOrEqual(HEIGHT)
}
})
})