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
83 changes: 64 additions & 19 deletions dashboard/frontend/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState } from 'react';
import { useState, useEffect } from 'react';
import Sidebar from './components/Sidebar';
import CoinsPage from './pages/CoinsPage';
import CoinHistoryPage from './pages/CoinHistoryPage';
Expand Down Expand Up @@ -29,34 +29,79 @@ export default function App() {
const [page, setPage] = useState('coins');
const [params, setParams] = useState({});
const [refreshInterval, setRefresh] = useState(30_000);
const [isMobile, setIsMobile] = useState(window.innerWidth < 768);
const [sidebarOpen, setSidebarOpen] = useState(window.innerWidth >= 768);

useEffect(() => {
const handler = () => {
const mobile = window.innerWidth < 768;
setIsMobile(mobile);
if (!mobile) setSidebarOpen(true);
};
window.addEventListener('resize', handler);
return () => window.removeEventListener('resize', handler);
}, []);

const navigate = (newPage, newParams = {}) => {
setPage(newPage);
setParams(newParams);
if (isMobile) setSidebarOpen(false);
};

const Page = PAGES[page] ?? CoinsPage;

return (
<div style={{ display: 'flex', height: '100vh', overflow: 'hidden' }}>
<Sidebar active={page} onNav={(p) => navigate(p)} />
<main style={{ flex: 1, overflowY: 'auto', padding: '28px 32px', background: '#0b0c14' }}>
<div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: 12, gap: 6 }}>
<span style={{ fontSize: 11, color: '#4a4c6a', alignSelf: 'center', textTransform: 'uppercase', letterSpacing: '0.5px' }}>Refresh</span>
{REFRESH_OPTIONS.map((opt) => (
<button
key={opt.value}
onClick={() => setRefresh(opt.value)}
style={{
padding: '4px 10px', borderRadius: 6, border: '1px solid #1e2038',
fontSize: 11, fontWeight: 500, cursor: 'pointer', transition: 'all 0.15s',
background: refreshInterval === opt.value ? '#1a1b2e' : 'transparent',
color: refreshInterval === opt.value ? '#00d4aa' : '#4a4c6a',
}}
>
{opt.label}
</button>
))}
{/* Mobile backdrop */}
{isMobile && sidebarOpen && (
<div
onClick={() => setSidebarOpen(false)}
style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.6)', zIndex: 199 }}
/>
)}

<Sidebar
active={page}
onNav={(p) => navigate(p)}
open={sidebarOpen}
isMobile={isMobile}
onClose={() => setSidebarOpen(false)}
/>

<main style={{ flex: 1, overflowY: 'auto', padding: isMobile ? '16px' : '28px 32px', background: '#0b0c14' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12, gap: 6 }}>
{/* Hamburger button */}
<button
onClick={() => setSidebarOpen(o => !o)}
style={{
display: 'flex', flexDirection: 'column', justifyContent: 'center', gap: 4,
padding: '6px 8px', borderRadius: 6, border: '1px solid #1e2038',
background: 'transparent', cursor: 'pointer', flexShrink: 0,
}}
aria-label="Toggle sidebar"
>
<span style={{ width: 16, height: 2, background: '#4a4c6a', borderRadius: 1, display: 'block' }} />
<span style={{ width: 16, height: 2, background: '#4a4c6a', borderRadius: 1, display: 'block' }} />
<span style={{ width: 16, height: 2, background: '#4a4c6a', borderRadius: 1, display: 'block' }} />
</button>

<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
<span style={{ fontSize: 11, color: '#4a4c6a', textTransform: 'uppercase', letterSpacing: '0.5px' }}>Refresh</span>
{REFRESH_OPTIONS.map((opt) => (
<button
key={opt.value}
onClick={() => setRefresh(opt.value)}
style={{
padding: '4px 10px', borderRadius: 6, border: '1px solid #1e2038',
fontSize: 11, fontWeight: 500, cursor: 'pointer', transition: 'all 0.15s',
background: refreshInterval === opt.value ? '#1a1b2e' : 'transparent',
color: refreshInterval === opt.value ? '#00d4aa' : '#4a4c6a',
}}
>
{opt.label}
</button>
))}
</div>
</div>
<Page params={params} onNavigate={navigate} refreshInterval={refreshInterval} />
</main>
Expand Down
20 changes: 17 additions & 3 deletions dashboard/frontend/src/components/Sidebar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const NAV = [
{ id: 'poolcompare', label: 'Compare Pools', icon: CompareIcon, group: 'Mining' },
];

export default function Sidebar({ active, onNav }) {
export default function Sidebar({ active, onNav, open, isMobile, onClose }) {
const [aggregatorRunning, setAggregatorRunning] = useState(null);

useEffect(() => {
Expand All @@ -21,14 +21,23 @@ export default function Sidebar({ active, onNav }) {
return () => clearInterval(id);
}, []);

if (!open) return null;

const navStyle = isMobile
? { ...s.nav, position: 'fixed', top: 0, left: 0, height: '100vh', zIndex: 200 }
: s.nav;

return (
<nav style={s.nav}>
<nav style={navStyle}>
<div style={s.logo}>
<div style={s.logoMark}>M</div>
<div>
<div style={{ flex: 1 }}>
<div style={s.logoTitle}>Mining</div>
<div style={s.logoSub}>Dashboard</div>
</div>
{isMobile && (
<button onClick={onClose} style={s.closeBtn} aria-label="Close sidebar">✕</button>
)}
</div>

<ul style={s.list}>
Expand Down Expand Up @@ -145,4 +154,9 @@ const s = {
dot: { width: 5, height: 5, borderRadius: '50%', background: '#00d4aa', marginLeft: 'auto', flexShrink: 0 },
footer: { display: 'flex', alignItems: 'center', gap: 7, padding: '12px 10px 4px', borderTop: '1px solid #1a1b2e' },
footerText: { fontSize: 11, color: '#4a4c6a', letterSpacing: '0.5px', textTransform: 'uppercase' },
closeBtn: {
background: 'transparent', border: 'none', cursor: 'pointer',
color: '#4a4c6a', fontSize: 16, padding: '2px 4px', lineHeight: 1,
flexShrink: 0,
},
};
Loading