Mega-Tracker/src/App.tsx
Dave 471cf6fb3c Project tracker seeded from the Mega Infrastructure Candy barchart
App for the Mega Infrastructure AI Integration programme (A000) and Mega
Plant (B110). 100 activities across 2 programmes, 06 Oct 2026 - 31 Aug 2027,
parsed from the client PDF and frozen as an immutable baseline.

Views: Dashboard, Timeline (month grid + week grid matching the PDF's
Friday-anchored week numbering), Todo list, WBS, Data.

Notes on the source data:
- Hierarchy is derived from the PDF's indentation, not the activity code.
  Codes do not sort structurally (A490 sits under Workforce, A510 under
  Financials), so a code-ordered import would produce a wrong tree.
- The schedule is built from the dates, not the PDF's "Durn" column, which
  disagrees with its own dates for 13 of the 100 activities.
- Spec issues found in the PDF are surfaced in-app under Data: 2 unresolved
  "?" placeholders, 6 activities sharing one stretched 6-week window while
  declared 15 days, 13 duration mismatches, 17 spelling corrections
  (original wording preserved and viewable).

Progress (status, % complete, note) is stored behind a pluggable adapter so
the site can be static today and point at a shared JSON store later without
touching the UI.
2026-10-01 08:48:51 +02:00

77 lines
2.3 KiB
TypeScript

import { useState } from 'react'
import { StoreProvider, useStore } from './lib/store'
import { Dashboard } from './components/Dashboard'
import { Gantt } from './components/Gantt'
import { Todos } from './components/Todos'
import { Wbs } from './components/Wbs'
import { DataView } from './components/DataView'
import { ActivityPanel } from './components/ActivityPanel'
import { ThemeControls } from './components/ThemeControls'
import './styles.css'
type Tab = 'dashboard' | 'timeline' | 'todos' | 'wbs' | 'data'
const TABS: [Tab, string][] = [
['dashboard', 'Dashboard'],
['timeline', 'Timeline'],
['todos', 'Todo list'],
['wbs', 'WBS'],
['data', 'Data'],
]
function Shell() {
const [tab, setTab] = useState<Tab>('dashboard')
const [open, setOpen] = useState<string | null>(null)
const { views, index } = useStore()
const leaves = [...views.values()].filter((v) => v.isLeaf)
const done = leaves.filter((v) => v.status === 'done').length
const overall = Math.round(
leaves.reduce((s, v) => s + v.percent, 0) / (leaves.length || 1),
)
return (
<div className="app">
<header className="topbar">
<div className="brand">
<strong>Mega Infrastructure</strong>
<span>AI Integration — project tracker</span>
</div>
<nav>
{TABS.map(([k, l]) => (
<button key={k} className={tab === k ? 'on' : ''} onClick={() => setTab(k)}>
{l}
{k === 'todos' && done > 0 && <em className="badge">{done}</em>}
</button>
))}
</nav>
<div className="topmeta">
<div className="bar sm">
<i style={{ width: `${overall}%` }} />
</div>
<span>{overall}%</span>
<span className="muted sm">{index.order.length} activities</span>
<ThemeControls />
</div>
</header>
<main>
{tab === 'dashboard' && <Dashboard onOpen={setOpen} />}
{tab === 'timeline' && <Gantt onOpen={setOpen} />}
{tab === 'todos' && <Todos onOpen={setOpen} />}
{tab === 'wbs' && <Wbs onOpen={setOpen} />}
{tab === 'data' && <DataView onOpen={setOpen} />}
</main>
<ActivityPanel code={open} onClose={() => setOpen(null)} />
</div>
)
}
export default function App() {
return (
<StoreProvider>
<Shell />
</StoreProvider>
)
}