--Added new pages Dashboard, Nodes, Cluster
This commit is contained in:
george committed 2026-09-08 15:10:34 +05:30
1 parent 50e424bc22
commit e9178bc340
2 files changed
+166 -12

No files matched your search

+56
View File
@@ -257,6 +257,13 @@
margin: 0; margin: 0;
} }
.page-actions {
display: flex;
flex-wrap: wrap;
gap: 10px;
justify-content: flex-end;
}
.primary-button, .primary-button,
.secondary-button { .secondary-button {
display: inline-flex; display: inline-flex;
@@ -284,6 +291,12 @@
background: var(--accent-border); background: var(--accent-border);
} }
.primary-button:disabled,
.secondary-button:disabled {
cursor: not-allowed;
opacity: 0.65;
}
.secondary-button { .secondary-button {
border: 1px solid var(--border); border: 1px solid var(--border);
background: var(--bg); background: var(--bg);
@@ -295,6 +308,11 @@
background: var(--accent-bg); background: var(--accent-bg);
} }
.secondary-button:disabled:hover {
border-color: var(--border);
background: var(--bg);
}
.primary-button:focus-visible, .primary-button:focus-visible,
.secondary-button:focus-visible, .secondary-button:focus-visible,
.register-form input:focus-visible { .register-form input:focus-visible {
@@ -302,6 +320,40 @@
outline-offset: 2px; outline-offset: 2px;
} }
.status-message {
width: fit-content;
margin-bottom: 18px;
border: 1px solid var(--border);
border-radius: 6px;
font-size: 14px;
font-weight: 600;
padding: 8px 12px;
}
.status-message.success {
border-color: #86efac;
background: #f0fdf4;
color: #166534;
}
.status-message.error {
border-color: #fecaca;
background: #fef2f2;
color: #991b1b;
}
.theme-dark .status-message.success {
border-color: #166534;
background: rgba(22, 101, 52, 0.18);
color: #bbf7d0;
}
.theme-dark .status-message.error {
border-color: #991b1b;
background: rgba(153, 27, 27, 0.18);
color: #fecaca;
}
.register-form { .register-form {
border: 1px solid var(--border); border: 1px solid var(--border);
border-radius: 8px; border-radius: 8px;
@@ -379,6 +431,10 @@
flex-direction: column; flex-direction: column;
} }
.page-actions {
justify-content: flex-start;
}
.form-grid { .form-grid {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
+110 -12
View File
@@ -4,9 +4,12 @@ import './App.css'
const navItems = [ const navItems = [
{ id: 'dashboard', label: 'Dashboard' }, { id: 'dashboard', label: 'Dashboard' },
{ id: 'nodes', label: 'Nodes' }, { id: 'nodes', label: 'Nodes' },
] { id: 'clusters', label: 'Clusters' },
] as const
type PageId = (typeof navItems)[number]['id']
type Theme = 'light' | 'dark' type Theme = 'light' | 'dark'
type TokenStatus = 'idle' | 'loading' | 'success' | 'error'
const getSavedTheme = (): Theme => { const getSavedTheme = (): Theme => {
try { try {
@@ -24,13 +27,67 @@ const saveTheme = (theme: Theme) => {
} }
} }
const getSavedPage = (): PageId => {
try {
const savedPage = localStorage.getItem('activePage')
return navItems.some((item) => item.id === savedPage)
? (savedPage as PageId)
: 'dashboard'
} catch {
return 'dashboard'
}
}
const savePage = (page: PageId) => {
try {
localStorage.setItem('activePage', page)
} catch {
return
}
}
const getSessionToken = async () => {
const response = await fetch('http://127.0.0.1:8000/get-token', {
method: 'POST',
})
if (!response.ok) {
throw new Error('Failed to get token')
}
const data = (await response.json()) as {
access_token: string
token_type: string
}
localStorage.setItem('access_token', data.access_token)
const storedToken = localStorage.getItem('access_token')
if (!storedToken) {
throw new Error('Token was not stored')
}
console.log('Token successfully stored:', storedToken)
return storedToken
}
function App() { function App() {
const [isCollapsed, setIsCollapsed] = useState(false) const [isCollapsed, setIsCollapsed] = useState(false)
const [activePage, setActivePage] = useState('dashboard') const [activePage, setActivePage] = useState<PageId>(getSavedPage)
const [theme, setTheme] = useState<Theme>(getSavedTheme) const [theme, setTheme] = useState<Theme>(getSavedTheme)
const [isRegisterFormOpen, setIsRegisterFormOpen] = useState(false) const [isRegisterFormOpen, setIsRegisterFormOpen] = useState(false)
const [tokenStatus, setTokenStatus] = useState<TokenStatus>('idle')
const [tokenError, setTokenError] = useState('')
const nextTheme = theme === 'light' ? 'dark' : 'light' const nextTheme = theme === 'light' ? 'dark' : 'light'
const selectPage = (page: PageId) => {
setActivePage(page)
savePage(page)
}
const toggleTheme = () => { const toggleTheme = () => {
setTheme((currentTheme) => { setTheme((currentTheme) => {
const updatedTheme = currentTheme === 'light' ? 'dark' : 'light' const updatedTheme = currentTheme === 'light' ? 'dark' : 'light'
@@ -41,6 +98,21 @@ function App() {
}) })
} }
const handleTempButtonClick = async () => {
setTokenStatus('loading')
setTokenError('')
try {
await getSessionToken()
setTokenStatus('success')
} catch (error) {
setTokenStatus('error')
setTokenError(
error instanceof Error ? error.message : 'Failed to get token',
)
}
}
return ( return (
<div <div
className={`app-shell theme-${theme} ${isCollapsed ? 'nav-collapsed' : ''}`} className={`app-shell theme-${theme} ${isCollapsed ? 'nav-collapsed' : ''}`}
@@ -66,7 +138,7 @@ function App() {
type="button" type="button"
className={`nav-link ${activePage === item.id ? 'active' : ''}`} className={`nav-link ${activePage === item.id ? 'active' : ''}`}
title={isCollapsed ? item.label : undefined} title={isCollapsed ? item.label : undefined}
onClick={() => setActivePage(item.id)} onClick={() => selectPage(item.id)}
aria-current={activePage === item.id ? 'page' : undefined} aria-current={activePage === item.id ? 'page' : undefined}
> >
<span className="nav-icon" aria-hidden="true"> <span className="nav-icon" aria-hidden="true">
@@ -94,7 +166,7 @@ function App() {
</aside> </aside>
<main className="main-content"> <main className="main-content">
{activePage === 'dashboard' ? ( {activePage === 'dashboard' && (
<section className="content-panel" id="dashboard"> <section className="content-panel" id="dashboard">
<p className="eyebrow">Dashboard</p> <p className="eyebrow">Dashboard</p>
<h1>Welcome back</h1> <h1>Welcome back</h1>
@@ -104,19 +176,39 @@ function App() {
grows. grows.
</p> </p>
</section> </section>
) : ( )}
{activePage === 'nodes' && (
<section className="content-panel nodes-page" id="nodes"> <section className="content-panel nodes-page" id="nodes">
<div className="page-header"> <div className="page-header">
<h1>Nodes</h1> <h1>Nodes</h1>
<button <div className="page-actions">
type="button" <button
className="primary-button" type="button"
onClick={() => setIsRegisterFormOpen(true)} className="primary-button"
> onClick={() => setIsRegisterFormOpen(true)}
Register Nodes >
</button> Register Nodes
</button>
<button
type="button"
className="secondary-button"
onClick={handleTempButtonClick}
disabled={tokenStatus === 'loading'}
>
{tokenStatus === 'loading' ? 'Getting Token' : 'Temp Button'}
</button>
</div>
</div> </div>
{tokenStatus === 'success' && (
<p className="status-message success">Token successfully stored.</p>
)}
{tokenStatus === 'error' && (
<p className="status-message error">{tokenError}</p>
)}
{isRegisterFormOpen ? ( {isRegisterFormOpen ? (
<form className="register-form"> <form className="register-form">
<div className="form-grid"> <div className="form-grid">
@@ -174,6 +266,12 @@ function App() {
) : null} ) : null}
</section> </section>
)} )}
{activePage === 'clusters' && (
<section className="content-panel" id="clusters">
<h1>Clusters</h1>
</section>
)}
</main> </main>
</div> </div>
) )