Skip to content

Commit

Permalink
Merge pull request #137 from mfreeman451/136-featui-dark-mode
Browse files Browse the repository at this point in the history
dark mode added
  • Loading branch information
mfreeman451 authored Jan 29, 2025
2 parents 5d1c75c + 67a7cb1 commit 158a194
Show file tree
Hide file tree
Showing 14 changed files with 705 additions and 360 deletions.
4 changes: 2 additions & 2 deletions pkg/cloud/api/web/dist/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
<link rel="shortcut icon" href="/favicons/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/favicons/apple-touch-icon.png" />
<link rel="manifest" href="/favicons/site.webmanifest" />
<script type="module" crossorigin src="/assets/index-DgEnPBsX.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CVKL2ChR.css">
<script type="module" crossorigin src="/assets/index-BOZ51EFD.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-Bvw9TToT.css">
</head>
<body>
<div id="root"></div>
Expand Down
4 changes: 2 additions & 2 deletions web/dist/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
<link rel="shortcut icon" href="/favicons/favicon.ico" />
<link rel="apple-touch-icon" sizes="180x180" href="/favicons/apple-touch-icon.png" />
<link rel="manifest" href="/favicons/site.webmanifest" />
<script type="module" crossorigin src="/assets/index-DgEnPBsX.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-CVKL2ChR.css">
<script type="module" crossorigin src="/assets/index-BOZ51EFD.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-Bvw9TToT.css">
</head>
<body>
<div id="root"></div>
Expand Down
4 changes: 2 additions & 2 deletions web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 31 additions & 15 deletions web/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,41 @@
import React from 'react';
import React, {useEffect, useState} from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Navbar from './components/Navbar';
import Dashboard from './components/Dashboard';
import NodeList from './components/NodeList';
import ServiceDashboard from './components/ServiceDashboard';

function App() {
const [darkMode, setDarkMode] = useState(false);

// remember user preference in localStorage
useEffect(() => {
const savedMode = localStorage.getItem('darkMode');
if (savedMode) {
setDarkMode(savedMode === 'true');
}
}, [])

useEffect(() => {
localStorage.setItem('darkMode', darkMode);
}, [darkMode])

return (
<Router>
<div className="min-h-screen bg-gray-100">
<Navbar />
<main className="container mx-auto px-4 py-8">
<Routes>
<Route path="/" element={<Dashboard />} />
<Route path="/nodes" element={<NodeList />} />
<Route path="/service/:nodeId/:serviceName" element={<ServiceDashboard />} />
</Routes>
</main>
</div>
</Router>
);
}
<div className={darkMode ? 'dark' : ''}>
<Router>
<div className="min-h-screen bg-gray-100 dark:bg-gray-900 transition-colors">
<Navbar darkMode={darkMode} setDarkMode={setDarkMode} />
<main className="container mx-auto px-4 py-8">
<Routes>
<Route path="/" element={<Dashboard />} />
<Route path="/nodes" element={<NodeList />} />
<Route path="/service/:nodeId/:serviceName" element={<ServiceDashboard />} />
</Routes>
</main>
</div>
</Router>
</div>
);
}

export default App;
85 changes: 50 additions & 35 deletions web/src/components/Dashboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,59 @@
import React, { useState, useEffect } from 'react';

function Dashboard() {
const [systemStatus, setSystemStatus] = useState(null);
const [systemStatus, setSystemStatus] = useState(null);

useEffect(() => {
const fetchStatus = async () => {
try {
const response = await fetch('/api/status');
const data = await response.json();
setSystemStatus(data);
} catch (error) {
console.error('Error fetching status:', error);
}
};
useEffect(() => {
const fetchStatus = async () => {
try {
const response = await fetch('/api/status');
const data = await response.json();
setSystemStatus(data);
} catch (error) {
console.error('Error fetching status:', error);
}
};

fetchStatus();
const interval = setInterval(fetchStatus, 10000);
return () => clearInterval(interval);
}, []);
fetchStatus();
const interval = setInterval(fetchStatus, 10000);
return () => clearInterval(interval);
}, []);

return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div className="bg-white rounded-lg shadow p-6">
<h3 className="font-bold">Total Nodes</h3>
<p className="text-2xl">{systemStatus?.total_nodes || 0}</p>
</div>
<div className="bg-white rounded-lg shadow p-6">
<h3 className="font-bold">Healthy Nodes</h3>
<p className="text-2xl">{systemStatus?.healthy_nodes || 0}</p>
</div>
<div className="bg-white rounded-lg shadow p-6">
<h3 className="font-bold">Last Update</h3>
<p className="text-2xl">
{systemStatus?.last_update ?
new Date(systemStatus.last_update).toLocaleTimeString() :
'N/A'}
</p>
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{/* Card 1 */}
<div className="bg-white dark:bg-gray-800 rounded-lg shadow p-6 transition-colors">
<h3 className="font-bold text-gray-800 dark:text-gray-100">
Total Nodes
</h3>
<p className="text-2xl text-gray-700 dark:text-gray-100 mt-2">
{systemStatus?.total_nodes || 0}
</p>
</div>

{/* Card 2 */}
<div className="bg-white dark:bg-gray-800 rounded-lg shadow p-6 transition-colors">
<h3 className="font-bold text-gray-800 dark:text-gray-100">
Healthy Nodes
</h3>
<p className="text-2xl text-gray-700 dark:text-gray-100 mt-2">
{systemStatus?.healthy_nodes || 0}
</p>
</div>

{/* Card 3 */}
<div className="bg-white dark:bg-gray-800 rounded-lg shadow p-6 transition-colors">
<h3 className="font-bold text-gray-800 dark:text-gray-100">
Last Update
</h3>
<p className="text-2xl text-gray-700 dark:text-gray-100 mt-2">
{systemStatus?.last_update
? new Date(systemStatus.last_update).toLocaleTimeString()
: 'N/A'}
</p>
</div>
</div>
</div>
);
);
}

export default Dashboard;
export default Dashboard;
85 changes: 64 additions & 21 deletions web/src/components/DuskDashboard.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import React, { useState, useEffect } from 'react';
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts';
import {
LineChart,
Line,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
Legend,
ResponsiveContainer,
} from 'recharts';

const DuskDashboard = () => {
const [nodeStatus, setNodeStatus] = useState(null);
Expand All @@ -18,8 +27,8 @@ const DuskDashboard = () => {
console.log('Fetched nodes:', nodes);

// Find the Dusk node
const duskNode = nodes.find(node =>
node.services?.some(service => service.name === 'dusk')
const duskNode = nodes.find((node) =>
node.services?.some((service) => service.name === 'dusk')
);

if (!duskNode) {
Expand All @@ -29,7 +38,7 @@ const DuskDashboard = () => {
console.log('Found Dusk node:', duskNode);

// Get the Dusk service
const duskService = duskNode.services.find(s => s.name === 'dusk');
const duskService = duskNode.services.find((s) => s.name === 'dusk');
console.log('Dusk service:', duskService);

setNodeStatus(duskService);
Expand All @@ -55,15 +64,19 @@ const DuskDashboard = () => {
if (loading) {
return (
<div className="flex justify-center items-center h-64">
<div className="text-lg">Loading...</div>
<div className="text-lg dark:text-gray-100 transition-colors">
Loading...
</div>
</div>
);
}

if (error) {
return (
<div className="flex justify-center items-center h-64">
<div className="text-red-500 text-lg">{error}</div>
<div className="text-red-500 dark:text-red-400 text-lg transition-colors">
{error}
</div>
</div>
);
}
Expand All @@ -72,41 +85,71 @@ const DuskDashboard = () => {
console.log('Node details:', details);

return (
<div className="space-y-6">
<div className="space-y-6 transition-colors">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="bg-white rounded-lg shadow p-6">
<h3 className="text-lg font-semibold mb-2">Node Status</h3>
<div className={`text-lg ${nodeStatus?.available ? 'text-green-600' : 'text-red-600'}`}>
{/* Card 1: Node Status */}
<div className="bg-white dark:bg-gray-800 rounded-lg shadow p-6 transition-colors">
<h3 className="text-lg font-semibold mb-2 text-gray-800 dark:text-gray-100">
Node Status
</h3>
<div
className={`text-lg transition-colors ${
nodeStatus?.available
? 'text-green-600 dark:text-green-400'
: 'text-red-600 dark:text-red-400'
}`}
>
{nodeStatus?.available ? 'Online' : 'Offline'}
</div>
</div>

<div className="bg-white rounded-lg shadow p-6">
<h3 className="text-lg font-semibold mb-2">Current Height</h3>
<div className="text-lg">{details.height || 'N/A'}</div>
{/* Card 2: Current Height */}
<div className="bg-white dark:bg-gray-800 rounded-lg shadow p-6 transition-colors">
<h3 className="text-lg font-semibold mb-2 text-gray-800 dark:text-gray-100">
Current Height
</h3>
<div className="text-lg text-gray-800 dark:text-gray-100">
{details.height || 'N/A'}
</div>
</div>

<div className="bg-white rounded-lg shadow p-6">
<h3 className="text-lg font-semibold mb-2">Latest Hash</h3>
<div className="text-sm font-mono break-all">{details.hash || 'N/A'}</div>
{/* Card 3: Latest Hash */}
<div className="bg-white dark:bg-gray-800 rounded-lg shadow p-6 transition-colors">
<h3 className="text-lg font-semibold mb-2 text-gray-800 dark:text-gray-100">
Latest Hash
</h3>
<div className="text-sm font-mono break-all text-gray-700 dark:text-gray-200">
{details.hash || 'N/A'}
</div>
</div>
</div>

{/* Block History Chart */}
{blockHistory.length > 0 && (
<div className="bg-white rounded-lg shadow p-6">
<h3 className="text-lg font-semibold mb-4">Block Height History</h3>
<div className="bg-white dark:bg-gray-800 rounded-lg shadow p-6 transition-colors">
<h3 className="text-lg font-semibold mb-4 text-gray-800 dark:text-gray-100">
Block Height History
</h3>
<div className="h-64">
<ResponsiveContainer width="100%" height="100%">
<LineChart data={blockHistory}>
<CartesianGrid strokeDasharray="3 3" />
<CartesianGrid
strokeDasharray="3 3"
// Optionally adjust stroke color for dark mode
stroke="#ccc"
/>
<XAxis
dataKey="timestamp"
tickFormatter={(ts) => new Date(ts).toLocaleTimeString()}
// For dark mode, consider override. Recharts doesn't read tailwind classes directly.
/>
<YAxis />
<Tooltip
labelFormatter={(ts) => new Date(ts).toLocaleString()}
formatter={(value, name) => [value, name === 'height' ? 'Block Height' : name]}
formatter={(value, name) => [
value,
name === 'height' ? 'Block Height' : name,
]}
/>
<Legend />
<Line
Expand All @@ -125,4 +168,4 @@ const DuskDashboard = () => {
);
};

export default DuskDashboard;
export default DuskDashboard;
Loading

0 comments on commit 158a194

Please sign in to comment.