Compare commits

..

2 Commits

Author SHA1 Message Date
sebseb7
93e3baa1c5 u 2025-12-25 18:42:15 +01:00
sebseb7
4f52064b3d u 2025-12-25 18:34:49 +01:00
2 changed files with 73 additions and 10 deletions

View File

@@ -99,11 +99,17 @@ export default class App extends Component {
</Typography>
<Button color="inherit" component={Link} to="/" startIcon={<DashboardIcon />}>Views</Button>
<Button color="inherit" component={Link} to="/live" startIcon={<ShowChartIcon />}>Live</Button>
{user && (
<>
<Button color="inherit" component={Link} to="/live" startIcon={<ShowChartIcon />}>Live</Button>
</>
)}
{user && user.role === 'admin' && (
<Button color="inherit" component={Link} to="/rules" startIcon={<RuleIcon />}>Rules</Button>
)}
<Button color="inherit" component={Link} to="/settings" startIcon={<SettingsIcon />}>Settings</Button>
{user && (
<Button color="inherit" component={Link} to="/settings" startIcon={<SettingsIcon />}>Settings</Button>
)}
{user ? (
<Button color="inherit" onClick={this.handleLogout}>Logout ({user.username})</Button>

View File

@@ -9,7 +9,8 @@ export default class Chart extends Component {
super(props);
this.state = {
data: [],
loading: true
loading: true,
hiddenSeries: {} // { seriesId: true/false }
};
this.interval = null;
}
@@ -219,15 +220,41 @@ export default class Chart extends Component {
return { min: axisMin, max: axisMax };
}
toggleSeries = (seriesId) => {
this.setState(prev => ({
hiddenSeries: {
...prev.hiddenSeries,
[seriesId]: !prev.hiddenSeries[seriesId]
}
}));
};
render() {
const { loading, data } = this.state;
const { loading, data, hiddenSeries } = this.state;
const { channelConfig, windowEnd, range } = this.props;
const effectiveChannels = this.getEffectiveChannels(this.props);
if (loading) return <Box sx={{ display: 'flex', justifyContent: 'center', p: 4 }}><CircularProgress /></Box>;
if (effectiveChannels.length === 0) return <Box sx={{ p: 4 }}><Typography>No channels selected.</Typography></Box>;
const series = effectiveChannels.map(id => {
// Build legend config (all channels, for rendering custom legend)
const legendItems = effectiveChannels.map(id => {
let label = id;
let color = '#888';
if (channelConfig) {
const item = channelConfig.find(c => c.id === id);
if (item) {
if (item.alias) label = item.alias;
if (item.color) color = item.color;
}
}
return { id, label, color, hidden: !!hiddenSeries[id] };
});
// Filter out hidden series
const visibleChannels = effectiveChannels.filter(id => !hiddenSeries[id]);
const series = visibleChannels.map(id => {
// Find alias and axis if config exists
let label = id;
let yAxisKey = 'left';
@@ -281,6 +308,40 @@ export default class Chart extends Component {
return (
<Box sx={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'column', p: 2, boxSizing: 'border-box' }}>
<Paper sx={{ p: 2, flexGrow: 1, display: 'flex', flexDirection: 'column', minHeight: 0, overflow: 'hidden' }}>
{/* Custom Interactive Legend */}
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1.5, justifyContent: 'center', mb: 1, py: 0.5 }}>
{legendItems.map(item => (
<Box
key={item.id}
onClick={() => this.toggleSeries(item.id)}
sx={{
display: 'flex',
alignItems: 'center',
gap: 0.5,
cursor: 'pointer',
opacity: item.hidden ? 0.4 : 1,
textDecoration: item.hidden ? 'line-through' : 'none',
transition: 'opacity 0.2s',
userSelect: 'none',
'&:hover': { opacity: item.hidden ? 0.6 : 0.8 },
}}
>
<Box
sx={{
width: 14,
height: 14,
borderRadius: '50%',
bgcolor: item.color,
border: '2px solid',
borderColor: item.hidden ? 'grey.500' : item.color,
}}
/>
<Typography variant="body2" component="span">
{item.label}
</Typography>
</Box>
))}
</Box>
<Box sx={{ flexGrow: 1, width: '100%', height: '100%' }}>
<LineChart
dataset={data}
@@ -295,11 +356,7 @@ export default class Chart extends Component {
yAxis={yAxes}
rightAxis={hasRightAxis ? 'right' : null}
slotProps={{
legend: {
direction: 'row',
position: { vertical: 'top', horizontal: 'middle' },
padding: 0,
},
legend: { hidden: true },
lineHighlight: { strokeWidth: 3 },
}}
sx={{