Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions chimera/web-ui/app/api/telemetry/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { NextResponse } from 'next/server';

export async function GET() {
// Stub telemetry record until backend wiring is complete
const data = {
hash: 'demo-hash',
metrics: {
eccErrors: 0,
temperature: 70,
nvlinkErrors: 0,
migEvents: [],
},
};
return NextResponse.json(data);
}
12 changes: 11 additions & 1 deletion chimera/web-ui/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
import AuthProvider from '../components/AuthProvider';
import TelemetryClient from '../components/TelemetryClient';

export default function Home() {
return <div className="p-4">Chimera Dashboard</div>;
return (
<AuthProvider>
<div className="p-4 space-y-4">
<h1 className="text-xl font-bold">Chimera Dashboard</h1>
<TelemetryClient />
</div>
</AuthProvider>
);
}
18 changes: 18 additions & 0 deletions chimera/web-ui/components/AuthProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
'use client';

import { createContext, useContext } from 'react';

export interface User {
name: string;
}

const AuthContext = createContext<User | null>({ name: 'demo-user' });

export function useUser() {
return useContext(AuthContext);
}

export default function AuthProvider({ children }: { children: React.ReactNode }) {
// TODO: replace with real OIDC integration
return <AuthContext.Provider value={{ name: 'demo-user' }}>{children}</AuthContext.Provider>;
}
27 changes: 27 additions & 0 deletions chimera/web-ui/components/TelemetryClient.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use client';

import { useEffect, useState } from 'react';
import TelemetryTable, { TelemetryRecord } from './TelemetryTable';

export default function TelemetryClient() {
const [records, setRecords] = useState<TelemetryRecord[]>([]);

useEffect(() => {
async function load() {
const res = await fetch('/api/telemetry');
if (res.ok) {
const data = await res.json();
setRecords([data]);
}
}
load();
const id = setInterval(load, 5000);
return () => clearInterval(id);
}, []);

return (
<div>
<TelemetryTable records={records} />
</div>
);
}
36 changes: 36 additions & 0 deletions chimera/web-ui/components/TelemetryTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
export interface TelemetryRecord {
hash: string;
metrics: {
eccErrors: number;
temperature: number;
nvlinkErrors: number;
migEvents: string[];
};
}

export default function TelemetryTable({ records }: { records: TelemetryRecord[] }) {
return (
<table className="min-w-full divide-y divide-gray-200">
<thead>
<tr>
<th className="px-4 py-2 text-left">Hash</th>
<th className="px-4 py-2 text-left">ECC Errors</th>
<th className="px-4 py-2 text-left">Temp (C)</th>
<th className="px-4 py-2 text-left">NVLink Errors</th>
<th className="px-4 py-2 text-left">MIG Events</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200">
{records.map((r, idx) => (
<tr key={idx} className="hover:bg-gray-50">
<td className="px-4 py-2 font-mono text-sm">{r.hash}</td>
<td className="px-4 py-2">{r.metrics.eccErrors}</td>
<td className="px-4 py-2">{r.metrics.temperature}</td>
<td className="px-4 py-2">{r.metrics.nvlinkErrors}</td>
<td className="px-4 py-2">{r.metrics.migEvents.join(', ')}</td>
</tr>
))}
</tbody>
</table>
);
}
5 changes: 4 additions & 1 deletion chimera/web-ui/tailwind.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
module.exports = {
content: ["./app/**/*.{js,ts,jsx,tsx}"] ,
content: [
"./app/**/*.{js,ts,jsx,tsx}",
"./components/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
Expand Down
Loading