Skip to content

Commit 0bddb96

Browse files
committed
feat(docs): add MDX docs for voting, claims, treasury, and contract verification
1 parent 8091e71 commit 0bddb96

18 files changed

Lines changed: 465 additions & 1 deletion

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,5 +168,6 @@ jobs:
168168

169169
- run: npm ci
170170
- run: npm run lint
171+
- run: npm run check-docs
171172
- run: npm run build
172173
- run: npm test

frontend/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
"build": "next build",
88
"analyze": "ANALYZE=true next build",
99
"lint": "next lint",
10-
"test": "jest --passWithNoTests"
10+
"test": "jest --passWithNoTests",
11+
"check-docs": "node scripts/check-docs-links.js"
1112
},
1213
"dependencies": {
1314
"@hookform/resolvers": "^5.2.2",
@@ -20,6 +21,7 @@
2021
"clsx": "^2.1.1",
2122
"lucide-react": "^1.7.0",
2223
"next": "15.2.3",
24+
"next-mdx-remote": "^6.0.0",
2325
"react": "^19.0.0",
2426
"react-dom": "^19.0.0",
2527
"react-hook-form": "^7.72.0",
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#!/usr/bin/env node
2+
/**
3+
* Checks that every internal link in src/content/*.mdx resolves to a known
4+
* /docs/* route. Exits 1 if any broken links are found.
5+
*/
6+
const fs = require('fs')
7+
const path = require('path')
8+
9+
const CONTENT_DIR = path.join(__dirname, '..', 'src', 'content')
10+
const VALID_ROUTES = new Set([
11+
'/docs/voting',
12+
'/docs/claims',
13+
'/docs/treasury',
14+
'/docs/contracts',
15+
])
16+
17+
const LINK_RE = /\[([^\]]+)\]\(([^)]+)\)/g
18+
19+
let broken = 0
20+
21+
for (const file of fs.readdirSync(CONTENT_DIR).filter((f) => f.endsWith('.mdx'))) {
22+
const src = fs.readFileSync(path.join(CONTENT_DIR, file), 'utf8')
23+
for (const [, , href] of src.matchAll(LINK_RE)) {
24+
if (!href.startsWith('/')) continue // skip external links
25+
if (!VALID_ROUTES.has(href)) {
26+
console.error(`[broken-link] ${file}: "${href}" is not a valid docs route`)
27+
broken++
28+
}
29+
}
30+
}
31+
32+
if (broken > 0) {
33+
console.error(`\n${broken} broken internal link(s) found.`)
34+
process.exit(1)
35+
} else {
36+
console.log('All internal docs links OK.')
37+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { loadMdx } from '@/lib/load-mdx'
2+
3+
export const metadata = { title: 'Claim Timelines — NiffyInsur Docs' }
4+
5+
export default async function ClaimsPage() {
6+
const { content } = await loadMdx('claims')
7+
return <>{content}</>
8+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { loadMdx } from '@/lib/load-mdx'
2+
import { ContractTable } from '@/components/docs/contract-table'
3+
4+
export const metadata = { title: 'Contract Addresses — NiffyInsur Docs' }
5+
6+
export default async function ContractsPage() {
7+
const { content } = await loadMdx('contracts')
8+
return (
9+
<>
10+
{content}
11+
<ContractTable />
12+
</>
13+
)
14+
}

frontend/src/app/docs/layout.tsx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { DocsSidebar } from '@/components/docs/docs-sidebar'
2+
import Link from 'next/link'
3+
4+
const GIT_SHA = process.env.NEXT_PUBLIC_GIT_SHA ?? process.env.VERCEL_GIT_COMMIT_SHA ?? ''
5+
6+
export default function DocsLayout({ children }: { children: React.ReactNode }) {
7+
return (
8+
<div className="min-h-screen flex flex-col">
9+
<header className="border-b bg-white px-6 py-3 flex items-center gap-4">
10+
<Link href="/" className="text-sm font-semibold text-blue-600 hover:underline">
11+
← NiffyInsur
12+
</Link>
13+
<span className="text-gray-300">|</span>
14+
<Link href="/docs/voting" className="text-sm font-medium text-gray-700">
15+
Docs
16+
</Link>
17+
</header>
18+
19+
<div className="flex flex-1 max-w-5xl mx-auto w-full px-4 py-8 gap-8">
20+
<DocsSidebar />
21+
<main className="flex-1 min-w-0 prose prose-gray max-w-none">{children}</main>
22+
</div>
23+
24+
<footer className="border-t px-6 py-3 text-xs text-gray-400 flex justify-between">
25+
<span>NiffyInsur Docs</span>
26+
{GIT_SHA && <span>Version: {GIT_SHA.slice(0, 7)}</span>}
27+
</footer>
28+
</div>
29+
)
30+
}

frontend/src/app/docs/page.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { redirect } from 'next/navigation'
2+
3+
export default function DocsIndex() {
4+
redirect('/docs/voting')
5+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { loadMdx } from '@/lib/load-mdx'
2+
3+
export const metadata = { title: 'Treasury & Pause — NiffyInsur Docs' }
4+
5+
export default async function TreasuryPage() {
6+
const { content } = await loadMdx('treasury')
7+
return <>{content}</>
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { loadMdx } from '@/lib/load-mdx'
2+
3+
export const metadata = { title: 'Voting Mechanics — NiffyInsur Docs' }
4+
5+
export default async function VotingPage() {
6+
const { content } = await loadMdx('voting')
7+
return <>{content}</>
8+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
'use client'
2+
3+
import { useState } from 'react'
4+
import { getContracts, type Network } from '@/lib/network-manifest'
5+
6+
export function ContractTable() {
7+
const [network, setNetwork] = useState<Network>('testnet')
8+
const contracts = getContracts(network)
9+
10+
return (
11+
<div className="my-6">
12+
<div className="flex items-center gap-3 mb-4">
13+
<span className="text-sm font-medium text-gray-700">Network:</span>
14+
<div className="flex rounded-md border overflow-hidden text-sm">
15+
{(['testnet', 'public'] as Network[]).map((n) => (
16+
<button
17+
key={n}
18+
onClick={() => setNetwork(n)}
19+
className={`px-3 py-1.5 transition-colors ${
20+
network === n
21+
? 'bg-blue-600 text-white'
22+
: 'bg-white text-gray-600 hover:bg-gray-50'
23+
}`}
24+
>
25+
{n === 'public' ? 'Mainnet' : 'Testnet'}
26+
</button>
27+
))}
28+
</div>
29+
</div>
30+
31+
<div className="overflow-x-auto rounded-lg border">
32+
<table className="w-full text-sm">
33+
<thead className="bg-gray-50 text-left">
34+
<tr>
35+
<th className="px-4 py-2 font-medium text-gray-600">Contract</th>
36+
<th className="px-4 py-2 font-medium text-gray-600">Contract ID</th>
37+
<th className="px-4 py-2 font-medium text-gray-600">WASM Hash</th>
38+
<th className="px-4 py-2 font-medium text-gray-600">Deployed</th>
39+
<th className="px-4 py-2 font-medium text-gray-600">Explorer</th>
40+
</tr>
41+
</thead>
42+
<tbody>
43+
{contracts.map((c) => (
44+
<tr key={c.name} className="border-t">
45+
<td className="px-4 py-2 font-mono font-medium">{c.name}</td>
46+
<td className="px-4 py-2 font-mono text-xs break-all">{c.contractId}</td>
47+
<td className="px-4 py-2 font-mono text-xs break-all">{c.expectedWasmHash}</td>
48+
<td className="px-4 py-2 text-xs text-gray-500">
49+
{new Date(c.deployedAt).toLocaleDateString()}
50+
</td>
51+
<td className="px-4 py-2">
52+
<a
53+
href={c.stellarExpertUrl}
54+
target="_blank"
55+
rel="noopener noreferrer"
56+
className="text-blue-600 hover:underline text-xs"
57+
>
58+
Stellar Expert ↗
59+
</a>
60+
</td>
61+
</tr>
62+
))}
63+
</tbody>
64+
</table>
65+
</div>
66+
</div>
67+
)
68+
}

0 commit comments

Comments
 (0)