Files
hackerspace.sh/lib/docs/nav.ts
T
Moheeb Zara 8edc9da611 docs: audit corrections, coverage pages, screenshots, and em-dash removal
Finalizes the docs section after an adversarial audit (code review + per-page
fact-check + coherence critic) and two content requests.

- Accuracy: fixed 10 invented cross-link slugs, the admin-vs-board gating claims
  the verifiers caught (permissions matrix, /settings, invites, onboarding,
  connect-payments, proposal withdraw, Stripe reconciliation, member fields,
  door.operate), and 2 issues in the new pages (project area-at-creation, forum
  thread-delete role).
- Coverage: added 5 how-to pages so every module is documented (recurring Stripe
  dues, tasks & chores, projects, comms & forum, ops knowledge base + secrets
  vault). 34 pages total.
- Screenshots: 12 captioned app screenshots under public/docs-media/, embedded
  as figures. Each tutorial now has 2-3 step screenshots.
- Style: removed all em-dashes/en-dashes/ellipses from the docs (natural ASCII
  punctuation).
- Handoff (docs/HANDOFF-audit-cleanup.md) updated with the current state.

typecheck 0 · lint 0 errors · build ok (all 34 /docs pages prerendered)
2026-08-05 03:15:07 -07:00

114 lines
7.5 KiB
TypeScript

// Docs information architecture, organized by the Diátaxis framework:
// tutorials — learning-oriented, start-to-finish lessons
// how-to — task-oriented, goal-focused recipes
// reference — information-oriented, dry technical facts
// explanation — understanding-oriented, background & rationale
//
// This registry is the single source of truth for the docs nav, the
// static-param generation, and the content pipeline. Each page's body lives at
// content/docs/<category>/<slug>.md and is rendered by <DocsMarkdown>.
export type DocCategoryId = 'tutorials' | 'how-to' | 'reference' | 'explanation'
export type DocPage = {
slug: string
title: string
summary: string
}
export type DocCategory = {
id: DocCategoryId
label: string
tagline: string
/** Diátaxis one-liner shown on the docs home. */
diataxis: string
pages: DocPage[]
}
export const DOC_CATEGORIES: DocCategory[] = [
{
id: 'tutorials',
label: 'Tutorials',
tagline: 'Start here',
diataxis: 'Learning-oriented lessons that take you from nothing to a working space, step by step.',
pages: [
{ slug: 'getting-started', title: 'Set up your space', summary: 'Create or join a space, land as an admin, and get your bearings.' },
{ slug: 'onboard-members', title: 'Onboard your first members', summary: 'Invite people, shape the onboarding flow, and get everyone current.' },
{ slug: 'first-class', title: 'Run your first class', summary: 'Create a class, schedule a session, take signups, mark attendance, award a cert.' },
{ slug: 'dues-end-to-end', title: 'Track dues, end to end', summary: 'Add a pay-here link, import a payment, and reconcile it to a member.' },
],
},
{
id: 'how-to',
label: 'How-to guides',
tagline: 'Get a task done',
diataxis: 'Task-oriented recipes for a specific goal, assuming you already know the basics.',
pages: [
{ slug: 'import-members', title: 'Import members from a spreadsheet', summary: 'Bulk-add members by pasting rows, with tiers and statuses.' },
{ slug: 'invite-links', title: 'Create role-granting invite links', summary: 'Generate invite codes with usage caps, expiry, and a granted role.' },
{ slug: 'configure-onboarding', title: 'Configure the onboarding flow', summary: 'Add, reorder, require, and disable onboarding steps.' },
{ slug: 'connect-payments', title: 'Connect payments & reconcile dues', summary: 'Add a platform, import transactions, and match them to members.' },
{ slug: 'stripe-dues', title: 'Set up recurring Stripe dues', summary: 'Let members subscribe so dues reconcile themselves each cycle.' },
{ slug: 'build-a-form', title: 'Build a form or signable waiver', summary: 'Compose fields, set visibility, and capture immutable submissions.' },
{ slug: 'equipment-reservations', title: 'Set up equipment & reservations', summary: 'Register tools, take reservations, and gate a tool behind a certification.' },
{ slug: 'connect-a-door', title: 'Connect a door controller', summary: 'Wire a native or generic controller, assign cards, and read the access log.' },
{ slug: 'create-tasks-chores', title: 'Create tasks & recurring chores', summary: 'Add one-off and recurring work with assignments, due dates, and areas.' },
{ slug: 'track-projects', title: 'Track projects', summary: 'Run a shared board of what the space is building.' },
{ slug: 'use-comms-and-forum', title: 'Use comms & the forum', summary: 'Post to channels and threads for space-wide discussion.' },
{ slug: 'manage-ops-knowledge', title: 'Write ops docs & store secrets', summary: 'Author knowledge base entries and keep credentials in the encrypted vault.' },
{ slug: 'run-a-proposal', title: 'Run a proposal to a vote', summary: 'Open a proposal, set quorum and threshold, and record the outcome.' },
{ slug: 'handle-incidents', title: 'File & manage an incident', summary: 'Report an incident (anonymously if needed) and move it to a decision.' },
{ slug: 'publish-a-policy', title: 'Publish & version a policy', summary: 'Write a policy, publish it, and supersede it with a new version.' },
{ slug: 'customize-space', title: 'Customize roles, tiers & areas', summary: 'Rename roles, tune the permissions matrix, define tiers and areas.' },
],
},
{
id: 'reference',
label: 'Reference',
tagline: 'Look it up',
diataxis: 'Information-oriented, exhaustive descriptions of the machinery. Dry and accurate.',
pages: [
{ slug: 'modules', title: 'Modules overview', summary: 'Every module in one place, with what it does and where it lives.' },
{ slug: 'roles-and-permissions', title: 'Roles & permissions', summary: 'Built-in roles, custom roles, and the per-space permission matrix.' },
{ slug: 'members', title: 'Members: statuses, tiers & fields', summary: 'Every member status, tier, and profile field, and what each controls.' },
{ slug: 'governance', title: 'Governance reference', summary: 'Proposal types, thresholds, quorum, incident and policy lifecycles.' },
{ slug: 'payments-and-dues', title: 'Payments & dues reference', summary: 'Platforms, link statuses, dues advancement, and pay-here links.' },
{ slug: 'access-control', title: 'Access control & doors reference', summary: 'Card slots, controller types, inbound ingest, and the access log.' },
{ slug: 'forms', title: 'Forms & waivers reference', summary: 'Field types, visibility, required-form gating, and submission snapshots.' },
{ slug: 'classes-and-equipment', title: 'Classes, certifications & equipment', summary: 'Sessions, waitlists, attendance, cert awards, and reservation rules.' },
{ slug: 'integrations', title: 'Integrations, cron & webhooks', summary: 'Notification dispatch, door inbound webhooks, and the cron endpoints.' },
],
},
{
id: 'explanation',
label: 'Explanation',
tagline: 'Understand why',
diataxis: 'Understanding-oriented discussion of how things fit together and why they are built this way.',
pages: [
{ slug: 'architecture', title: 'Architecture overview', summary: 'Next.js App Router, Supabase, server actions, and the request path.' },
{ slug: 'permissions-model', title: 'The multi-space & permissions model', summary: 'How space-scoping, roles, and effective permissions actually work.' },
{ slug: 'governance-model', title: 'How governance works', summary: 'Why quorum, thresholds, recusal, and anonymity are modeled the way they are.' },
{ slug: 'security-model', title: 'Security & privacy model', summary: 'RLS, the encrypted secrets vault, authz gates, and what the server derives.' },
{ slug: 'data-and-reconciliation', title: 'Dues & reconciliation, explained', summary: 'The philosophy behind advance-only dues and hand/auto matching.' },
],
},
]
export const DOC_CATEGORY_IDS = DOC_CATEGORIES.map(c => c.id)
export function findCategory(id: string): DocCategory | undefined {
return DOC_CATEGORIES.find(c => c.id === id)
}
export function findPage(categoryId: string, slug: string): { category: DocCategory; page: DocPage } | undefined {
const category = findCategory(categoryId)
const page = category?.pages.find(p => p.slug === slug)
if (!category || !page) return undefined
return { category, page }
}
/** Flat, in-order list for prev/next navigation. */
export function flatPages(): { categoryId: DocCategoryId; slug: string; title: string }[] {
return DOC_CATEGORIES.flatMap(c => c.pages.map(p => ({ categoryId: c.id, slug: p.slug, title: p.title })))
}