Add deep pages: fleet, repos, status, docs, activity + fix auth
Some checks failed
Autonomous Repo Agent / autonomous-build (push) Has been cancelled
BlackRoad AI Agents / agent-response (push) Has been cancelled
🔍 BlackRoad CodeQL Security Analysis / CodeQL Analysis (javascript) (push) Has been cancelled
🔍 BlackRoad CodeQL Security Analysis / CodeQL Analysis (python) (push) Has been cancelled
CI / Test (push) Has been cancelled
Deploy to Cloudflare Pages / Deploy to Cloudflare Pages (push) Has been cancelled
Trinity Compliance Check / check-compliance (push) Has been cancelled

- Fix signup/login to work in demo mode (no API dependency)
- Add fleet page with clickable node cards → node detail pages
- Add repos page with search/filter → repo detail with README
- Add status page with real-time service health checks
- Add docs page with 12 documentation articles (architecture, tunnels, daemon, models, etc.)
- Add activity feed with live Gitea data
- Add sidebar navigation for all new pages
- Fix conversations/new Suspense boundary for Next.js 16

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

RoadChain-SHA2048: 8666c4a21fb4761b
RoadChain-Identity: alexa@sovereign
RoadChain-Full: 8666c4a21fb4761bca6ef278c572bdbc99c11ea232d10f1c2ffefa71b95026ca5720ee432b3b30d64e08940e6cb8af6ba0f9049cf2c51144f9a69939da48a393159dd3776526ff313831a2213740bfeb67a9aac4c6e26152b1e9e9f0fb49f7c99e5ae90bf603c81a86eb8cb6cec841357222802e95db38d9e53e5dc681f2a4ed8c8027a96a980dc9f9a9c45b5d971d767fddd5fd3fdd93abb4da595567f8bbe09019a5559abf3a7a09520da7a562dc5fea730528960afc013f8f67bbb6d0f645e6273c917190f2d97611314fc9da5ec4a7eabcc06b736790bee07d7ff3a282f61c96e68d9c4edb869648114dc64de9571de451d84335f08625ecb0f72e9a7fd4
This commit is contained in:
2026-03-14 18:21:58 -05:00
parent 86be7a0309
commit aab2f33897
14 changed files with 1473 additions and 25 deletions

126
app/(app)/status/page.tsx Normal file
View File

@@ -0,0 +1,126 @@
'use client';
import { useEffect, useState } from 'react';
import Link from 'next/link';
import { CheckCircle, AlertTriangle, XCircle, Clock, Activity, Radio } from 'lucide-react';
interface ServiceStatus {
name: string;
status: 'operational' | 'degraded' | 'down';
latency?: number;
}
interface StatusData {
status: string;
services: ServiceStatus[];
timestamp: string;
}
const STATUS_ICON = {
operational: CheckCircle,
degraded: AlertTriangle,
down: XCircle,
};
const STATUS_COLOR = {
operational: 'text-green-400',
degraded: 'text-amber-400',
down: 'text-red-400',
};
const STATUS_BG = {
operational: 'bg-green-400/10 border-green-400/20',
degraded: 'bg-amber-400/10 border-amber-400/20',
down: 'bg-red-400/10 border-red-400/20',
};
export default function StatusPage() {
const [data, setData] = useState<StatusData | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function load() {
try {
const res = await fetch('/api/status');
const d = await res.json();
setData(d);
} finally {
setLoading(false);
}
}
load();
const interval = setInterval(load, 60000);
return () => clearInterval(interval);
}, []);
if (loading) return (
<div className="p-6 flex items-center gap-3 text-gray-400">
<Radio className="h-4 w-4 animate-pulse text-[#FF1D6C]" />
Checking services...
</div>
);
const services = data?.services ?? [];
const overall = data?.status ?? 'unknown';
const overallLabel = overall === 'operational' ? 'All Systems Operational' :
overall === 'partial_outage' ? 'Partial Outage' : 'Major Outage';
return (
<div className="p-6 space-y-6 max-w-3xl">
<div>
<h1 className="text-2xl font-bold text-white">System Status</h1>
<p className="text-gray-400 text-sm mt-1">Real-time health of BlackRoad infrastructure</p>
</div>
{/* Overall status banner */}
<div className={`rounded-xl border p-5 ${
overall === 'operational' ? STATUS_BG.operational :
overall === 'partial_outage' ? STATUS_BG.degraded : STATUS_BG.down
}`}>
<div className="flex items-center gap-3">
{overall === 'operational' ? (
<CheckCircle className="h-6 w-6 text-green-400" />
) : overall === 'partial_outage' ? (
<AlertTriangle className="h-6 w-6 text-amber-400" />
) : (
<XCircle className="h-6 w-6 text-red-400" />
)}
<span className="text-lg font-semibold text-white">{overallLabel}</span>
</div>
<p className="text-sm text-gray-400 mt-2">
Last checked: {data?.timestamp ? new Date(data.timestamp).toLocaleString() : 'now'}
</p>
</div>
{/* Individual services */}
<div className="bg-white/5 border border-white/10 rounded-xl divide-y divide-white/5">
{services.map(svc => {
const Icon = STATUS_ICON[svc.status] ?? Activity;
const color = STATUS_COLOR[svc.status] ?? 'text-gray-400';
return (
<div key={svc.name} className="flex items-center justify-between p-4 hover:bg-white/[0.03] transition-colors">
<div className="flex items-center gap-3">
<Icon className={`h-5 w-5 ${color}`} />
<span className="text-sm text-white">{svc.name}</span>
</div>
<div className="flex items-center gap-4">
{svc.latency !== undefined && (
<span className="text-xs text-gray-500 flex items-center gap-1">
<Clock className="h-3 w-3" />
{svc.latency}ms
</span>
)}
<span className={`text-xs capitalize ${color}`}>{svc.status}</span>
</div>
</div>
);
})}
</div>
<div className="text-xs text-gray-600 text-center pt-2">
Auto-refreshes every 60s · Powered by edge health checks
</div>
</div>
);
}