u
This commit is contained in:
@@ -1,130 +1,133 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { BrowserRouter, Routes, Route, Link, Navigate, useNavigate } from 'react-router-dom';
|
||||
import { ThemeProvider, CssBaseline, AppBar, Toolbar, Typography, Button, Box, IconButton, Menu, MenuItem } from '@mui/material';
|
||||
import React, { Component } from 'react';
|
||||
import { BrowserRouter, Routes, Route, Link, Navigate } from 'react-router-dom';
|
||||
import { AppBar, Toolbar, Typography, Button, Box, IconButton, CssBaseline } from '@mui/material';
|
||||
import { ThemeProvider, createTheme } from '@mui/material/styles';
|
||||
import SettingsIcon from '@mui/icons-material/Settings';
|
||||
import ShowChartIcon from '@mui/icons-material/ShowChart';
|
||||
import DashboardIcon from '@mui/icons-material/Dashboard';
|
||||
import AccountCircle from '@mui/icons-material/AccountCircle';
|
||||
import theme from './theme';
|
||||
|
||||
import Settings from './components/Settings';
|
||||
import Chart from './components/Chart';
|
||||
import Login from './components/Login';
|
||||
import ViewManager from './components/ViewManager';
|
||||
import ViewDisplay from './components/ViewDisplay';
|
||||
|
||||
function NavBar({ user, onLogout }) {
|
||||
const navigate = useNavigate();
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
|
||||
const handleMenu = (event) => setAnchorEl(event.currentTarget);
|
||||
const handleClose = () => setAnchorEl(null);
|
||||
const handleLogout = () => {
|
||||
handleClose();
|
||||
onLogout();
|
||||
navigate('/');
|
||||
};
|
||||
|
||||
return (
|
||||
<AppBar position="static">
|
||||
<Toolbar>
|
||||
<Typography variant="h6" component="div" sx={{ flexGrow: 1, cursor: 'pointer' }} onClick={() => navigate('/')}>
|
||||
TischlerCtrl
|
||||
</Typography>
|
||||
|
||||
<Button color="inherit" startIcon={<DashboardIcon />} onClick={() => navigate('/')}>
|
||||
Views
|
||||
</Button>
|
||||
<Button color="inherit" startIcon={<ShowChartIcon />} onClick={() => navigate('/live')}>
|
||||
Live
|
||||
</Button>
|
||||
<Button color="inherit" startIcon={<SettingsIcon />} onClick={() => navigate('/settings')}>
|
||||
Settings
|
||||
</Button>
|
||||
|
||||
{user ? (
|
||||
<div>
|
||||
<IconButton
|
||||
size="large"
|
||||
onClick={handleMenu}
|
||||
color="inherit"
|
||||
>
|
||||
<AccountCircle />
|
||||
</IconButton>
|
||||
<Menu
|
||||
anchorEl={anchorEl}
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={handleClose}
|
||||
>
|
||||
<MenuItem disabled>{user.username} ({user.role})</MenuItem>
|
||||
<MenuItem onClick={handleLogout}>Logout</MenuItem>
|
||||
</Menu>
|
||||
</div>
|
||||
) : (
|
||||
<Button color="inherit" onClick={() => navigate('/login')}>
|
||||
Login
|
||||
</Button>
|
||||
)}
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
);
|
||||
}
|
||||
|
||||
export default function App() {
|
||||
const [user, setUser] = useState(null);
|
||||
const [selectedChannels, setSelectedChannels] = useState([]);
|
||||
|
||||
// Load persistence (User + Settings)
|
||||
useEffect(() => {
|
||||
try {
|
||||
const savedSettings = localStorage.getItem('selectedChannels');
|
||||
if (savedSettings) setSelectedChannels(JSON.parse(savedSettings));
|
||||
|
||||
const savedUser = localStorage.getItem('user');
|
||||
if (savedUser) setUser(JSON.parse(savedUser));
|
||||
} catch (e) {
|
||||
console.error("Failed to load persistence", e);
|
||||
const darkTheme = createTheme({
|
||||
palette: {
|
||||
mode: 'dark',
|
||||
primary: { main: '#fb4934' }, // Gruvbox red
|
||||
secondary: { main: '#83a598' }, // Gruvbox blue
|
||||
background: {
|
||||
default: '#282828', // Gruvbox dark bg
|
||||
paper: '#3c3836', // Gruvbox dark lighter
|
||||
},
|
||||
text: {
|
||||
primary: '#ebdbb2',
|
||||
secondary: '#a89984'
|
||||
}
|
||||
}, []);
|
||||
},
|
||||
});
|
||||
|
||||
const handleLogin = (userData) => {
|
||||
setUser(userData);
|
||||
localStorage.setItem('user', JSON.stringify(userData));
|
||||
export default class App extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
selectedChannels: [],
|
||||
user: null, // { username, role, token }
|
||||
loading: true
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
// Load selection from local storage
|
||||
const saved = localStorage.getItem('selectedChannels');
|
||||
if (saved) {
|
||||
try {
|
||||
this.setState({ selectedChannels: JSON.parse(saved) });
|
||||
} catch (e) {
|
||||
console.error("Failed to parse saved channels");
|
||||
}
|
||||
}
|
||||
|
||||
// Check for existing token
|
||||
const token = localStorage.getItem('authToken');
|
||||
const username = localStorage.getItem('authUser');
|
||||
const role = localStorage.getItem('authRole');
|
||||
|
||||
if (token && username) {
|
||||
this.setState({ user: { username, role, token } });
|
||||
}
|
||||
|
||||
this.setState({ loading: false });
|
||||
}
|
||||
|
||||
handleSelectionChange = (newSelection) => {
|
||||
this.setState({ selectedChannels: newSelection });
|
||||
localStorage.setItem('selectedChannels', JSON.stringify(newSelection));
|
||||
};
|
||||
|
||||
const handleLogout = () => {
|
||||
setUser(null);
|
||||
localStorage.removeItem('user');
|
||||
handleLogin = (userData) => {
|
||||
this.setState({ user: userData });
|
||||
localStorage.setItem('authToken', userData.token);
|
||||
localStorage.setItem('authUser', userData.username);
|
||||
localStorage.setItem('authRole', userData.role);
|
||||
};
|
||||
|
||||
const handleToggleChannel = (id) => {
|
||||
setSelectedChannels(prev => {
|
||||
const newSelection = prev.includes(id)
|
||||
? prev.filter(c => c !== id)
|
||||
: [...prev, id];
|
||||
localStorage.setItem('selectedChannels', JSON.stringify(newSelection));
|
||||
return newSelection;
|
||||
});
|
||||
handleLogout = () => {
|
||||
this.setState({ user: null });
|
||||
localStorage.removeItem('authToken');
|
||||
localStorage.removeItem('authUser');
|
||||
localStorage.removeItem('authRole');
|
||||
};
|
||||
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<CssBaseline />
|
||||
<BrowserRouter>
|
||||
<Box sx={{ flexGrow: 1, height: '100vh', display: 'flex', flexDirection: 'column' }}>
|
||||
<NavBar user={user} onLogout={handleLogout} />
|
||||
render() {
|
||||
const { selectedChannels, user, loading } = this.state;
|
||||
|
||||
// While checking auth, we could show loader, but it's sync here mostly.
|
||||
|
||||
return (
|
||||
<ThemeProvider theme={darkTheme}>
|
||||
<CssBaseline />
|
||||
<BrowserRouter>
|
||||
<Box sx={{ flexGrow: 1, display: 'flex', flexDirection: 'column', height: '100vh' }}>
|
||||
<AppBar position="static">
|
||||
<Toolbar>
|
||||
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
|
||||
TischlerCtrl
|
||||
</Typography>
|
||||
|
||||
<Button color="inherit" component={Link} to="/" startIcon={<DashboardIcon />}>Views</Button>
|
||||
<Button color="inherit" component={Link} to="/live" startIcon={<ShowChartIcon />}>Live</Button>
|
||||
<Button color="inherit" component={Link} to="/settings" startIcon={<SettingsIcon />}>Settings</Button>
|
||||
|
||||
{user ? (
|
||||
<Button color="inherit" onClick={this.handleLogout}>Logout ({user.username})</Button>
|
||||
) : (
|
||||
<Button color="inherit" component={Link} to="/login">Login</Button>
|
||||
)}
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
|
||||
<Box component="main" sx={{ flexGrow: 1, overflow: 'auto' }}>
|
||||
<Routes>
|
||||
<Route path="/" element={<ViewManager user={user} />} />
|
||||
<Route path="/live" element={<Chart selectedChannels={selectedChannels} />} />
|
||||
<Route path="/settings" element={<Settings selectedChannels={selectedChannels} onToggleChannel={handleToggleChannel} />} />
|
||||
<Route path="/login" element={<Login onLogin={handleLogin} />} />
|
||||
|
||||
<Route path="/views/:id" element={<ViewDisplay />} />
|
||||
<Route path="/live" element={
|
||||
<Chart
|
||||
selectedChannels={selectedChannels}
|
||||
/>
|
||||
} />
|
||||
<Route path="/settings" element={
|
||||
<Settings
|
||||
selectedChannels={selectedChannels}
|
||||
onSelectionChange={this.handleSelectionChange}
|
||||
/>
|
||||
} />
|
||||
<Route path="/login" element={<Login onLogin={this.handleLogin} />} />
|
||||
<Route path="*" element={<Navigate to="/" replace />} />
|
||||
</Routes>
|
||||
</Box>
|
||||
</Box>
|
||||
</BrowserRouter>
|
||||
</ThemeProvider>
|
||||
);
|
||||
</BrowserRouter>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user