feat:
--Added Initial steps
This commit is contained in:
commit
50e424bc22
19 files changed
+2148
No files matched your search
+182
@@ -0,0 +1,182 @@
|
||||
import { useState } from 'react'
|
||||
import './App.css'
|
||||
|
||||
const navItems = [
|
||||
{ id: 'dashboard', label: 'Dashboard' },
|
||||
{ id: 'nodes', label: 'Nodes' },
|
||||
]
|
||||
|
||||
type Theme = 'light' | 'dark'
|
||||
|
||||
const getSavedTheme = (): Theme => {
|
||||
try {
|
||||
return localStorage.getItem('theme') === 'dark' ? 'dark' : 'light'
|
||||
} catch {
|
||||
return 'light'
|
||||
}
|
||||
}
|
||||
|
||||
const saveTheme = (theme: Theme) => {
|
||||
try {
|
||||
localStorage.setItem('theme', theme)
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
function App() {
|
||||
const [isCollapsed, setIsCollapsed] = useState(false)
|
||||
const [activePage, setActivePage] = useState('dashboard')
|
||||
const [theme, setTheme] = useState<Theme>(getSavedTheme)
|
||||
const [isRegisterFormOpen, setIsRegisterFormOpen] = useState(false)
|
||||
const nextTheme = theme === 'light' ? 'dark' : 'light'
|
||||
|
||||
const toggleTheme = () => {
|
||||
setTheme((currentTheme) => {
|
||||
const updatedTheme = currentTheme === 'light' ? 'dark' : 'light'
|
||||
|
||||
saveTheme(updatedTheme)
|
||||
|
||||
return updatedTheme
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`app-shell theme-${theme} ${isCollapsed ? 'nav-collapsed' : ''}`}
|
||||
>
|
||||
<aside className="side-nav" aria-label="Primary navigation">
|
||||
<div className="nav-header">
|
||||
<span className="nav-title">PG Multi Master</span>
|
||||
<button
|
||||
type="button"
|
||||
className="nav-toggle"
|
||||
onClick={() => setIsCollapsed((collapsed) => !collapsed)}
|
||||
aria-label={isCollapsed ? 'Expand navigation' : 'Collapse navigation'}
|
||||
aria-expanded={!isCollapsed}
|
||||
>
|
||||
<span aria-hidden="true">{isCollapsed ? '>' : '<'}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<nav className="nav-list">
|
||||
{navItems.map((item) => (
|
||||
<button
|
||||
key={item.id}
|
||||
type="button"
|
||||
className={`nav-link ${activePage === item.id ? 'active' : ''}`}
|
||||
title={isCollapsed ? item.label : undefined}
|
||||
onClick={() => setActivePage(item.id)}
|
||||
aria-current={activePage === item.id ? 'page' : undefined}
|
||||
>
|
||||
<span className="nav-icon" aria-hidden="true">
|
||||
{item.label.charAt(0)}
|
||||
</span>
|
||||
<span className="nav-label">{item.label}</span>
|
||||
</button>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="theme-toggle"
|
||||
onClick={toggleTheme}
|
||||
title={isCollapsed ? `Switch to ${nextTheme} theme` : undefined}
|
||||
aria-label={`Switch to ${nextTheme} theme`}
|
||||
>
|
||||
<span className="theme-icon" aria-hidden="true">
|
||||
{theme === 'light' ? 'D' : 'L'}
|
||||
</span>
|
||||
<span className="theme-label">
|
||||
{theme === 'light' ? 'Dark mode' : 'Light mode'}
|
||||
</span>
|
||||
</button>
|
||||
</aside>
|
||||
|
||||
<main className="main-content">
|
||||
{activePage === 'dashboard' ? (
|
||||
<section className="content-panel" id="dashboard">
|
||||
<p className="eyebrow">Dashboard</p>
|
||||
<h1>Welcome back</h1>
|
||||
<p>
|
||||
Your main workspace is ready. The left navigation can collapse
|
||||
now, and more sections can be added to the nav list as the app
|
||||
grows.
|
||||
</p>
|
||||
</section>
|
||||
) : (
|
||||
<section className="content-panel nodes-page" id="nodes">
|
||||
<div className="page-header">
|
||||
<h1>Nodes</h1>
|
||||
<button
|
||||
type="button"
|
||||
className="primary-button"
|
||||
onClick={() => setIsRegisterFormOpen(true)}
|
||||
>
|
||||
Register Nodes
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{isRegisterFormOpen ? (
|
||||
<form className="register-form">
|
||||
<div className="form-grid">
|
||||
<label className="form-field">
|
||||
<span>Node Name</span>
|
||||
<input name="node_name" type="text" required />
|
||||
</label>
|
||||
|
||||
<label className="form-field">
|
||||
<span>Host</span>
|
||||
<input
|
||||
name="host"
|
||||
type="text"
|
||||
inputMode="decimal"
|
||||
pattern="^((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])$"
|
||||
placeholder="192.168.1.10"
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label className="form-field">
|
||||
<span>Port</span>
|
||||
<input name="port" type="number" min="1" max="65535" required />
|
||||
</label>
|
||||
|
||||
<label className="form-field">
|
||||
<span>Database Name</span>
|
||||
<input name="dbname" type="text" required />
|
||||
</label>
|
||||
|
||||
<label className="form-field">
|
||||
<span>User</span>
|
||||
<input name="user" type="text" required />
|
||||
</label>
|
||||
|
||||
<label className="form-field">
|
||||
<span>Password</span>
|
||||
<input name="password" type="password" required />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="form-actions">
|
||||
<button
|
||||
type="button"
|
||||
className="secondary-button"
|
||||
onClick={() => setIsRegisterFormOpen(false)}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit" className="primary-button">
|
||||
Register
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
) : null}
|
||||
</section>
|
||||
)}
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
Reference in new issue
Block a user