feat:
--Added new pages Dashboard, Nodes, Cluster
This commit is contained in:
1 parent
50e424bc22
commit
e9178bc340
2 files changed
+166
-12
No files matched your search
+56
@@ -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
@@ -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>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in new issue
Block a user