diff --git a/src/pages/roadmap.astro b/src/pages/roadmap.astro index d5aa51a..efdf520 100644 --- a/src/pages/roadmap.astro +++ b/src/pages/roadmap.astro @@ -1,212 +1,76 @@ --- -import { readFileSync } from 'fs'; -import { join } from 'path'; import AppLayout from '../layouts/AppLayout.astro'; -import { marked } from 'marked'; +import LatestDispatchBanner from '../components/LatestDispatchBanner.astro'; +import RoadmapRoute from '../components/RoadmapRoute.astro'; +import { getAllRoadmapItems } from '../lib/db'; const user = Astro.locals.user; -// Single-file roadmap — not a content collection -const raw = readFileSync(join(process.cwd(), 'content/roadmap.md'), 'utf-8'); - -// Strip YAML frontmatter -const body = raw.replace(/^---[\s\S]*?---\n/, ''); - -// Parse sections by ## headings -function parseSections(md: string) { - const sectionRe = /^## (.+)$/gm; - const sections: { title: string; items: { title: string; body: string; pilotOnly: boolean }[] }[] = []; - const matches = [...md.matchAll(sectionRe)]; - - for (let i = 0; i < matches.length; i++) { - const m = matches[i]; - const start = m.index! + m[0].length; - const end = matches[i + 1]?.index ?? md.length; - const sectionBody = md.slice(start, end).trim(); - - // Each item starts with **Title** — description - const itemRe = /\*\*([^*]+)\*\*\s*—\s*([\s\S]*?)(?=\n\n\*\*|\n\n##|$)/g; - const items: { title: string; body: string; pilotOnly: boolean }[] = []; - let itemMatch: RegExpExecArray | null; - while ((itemMatch = itemRe.exec(sectionBody)) !== null) { - const rawBody = itemMatch[2].trim(); - const pilotOnly = rawBody.includes('`pilot-only`'); - const cleanBody = rawBody.replace(/`pilot-only`/g, '').trim(); - items.push({ title: itemMatch[1], body: cleanBody, pilotOnly }); - } - - sections.push({ title: m[1], items }); - } - return sections; -} - -const sections = parseSections(body); - -const horizonColors: Record = { - 'In progress': 'var(--pigment-copper)', - 'Next': 'var(--pigment-ochre)', - 'Later': 'var(--pigment-indigo)', -}; +// Admin orders chronologically nearest-to-furthest via display_order. +const items = getAllRoadmapItems() + .sort((a, b) => a.display_order - b.display_order || a.id - b.id); ---
-
- {sections.map((section) => ( -
-
-
- -
    - {section.items.map((item) => ( -
  • -
    -

    {item.title}

    - {item.pilotOnly && ( - - Pilot - - )} -
    -

    {item.body}

    -
  • - ))} -
-
- ))} -
+ +