Skip to content

Commit 918a476

Browse files
feat: update LogbookScreen layout to display three charts in a responsive row for improved data visualization
1 parent a589a9e commit 918a476

3 files changed

Lines changed: 94 additions & 75 deletions

File tree

dashboard/lib/screens/logbook_screen.dart

Lines changed: 42 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'package:provider/provider.dart';
55
import 'package:sentracore_dashboard/models/history_sample.dart';
66
import 'package:sentracore_dashboard/providers/history_provider.dart';
77
import 'package:sentracore_dashboard/theme/app_theme.dart';
8+
import 'package:sentracore_dashboard/widgets/responsive_builder.dart';
89

910
class LogbookScreen extends StatefulWidget {
1011
const LogbookScreen({super.key});
@@ -94,41 +95,50 @@ class _LogbookScreenState extends State<LogbookScreen> {
9495
padding: const EdgeInsets.all(12),
9596
child: Column(
9697
children: [
97-
Expanded(
98-
child: _HistoryChart(
99-
title: 'CPU',
100-
color: AppTheme.primary,
101-
samples: filtered,
102-
selector: (s) => s.cpuPercent,
103-
suffix: '%',
104-
maxY: 100,
105-
),
106-
),
107-
const SizedBox(height: 10),
108-
Expanded(
109-
child: _HistoryChart(
110-
title: 'Memory',
111-
color: AppTheme.accent,
112-
samples: filtered,
113-
selector: (s) => s.memPercent,
114-
suffix: '%',
115-
maxY: 100,
98+
// 3 charts in one row (wide), stacked (narrow).
99+
SizedBox(
100+
height: 260,
101+
child: ResponsiveRowColumn(
102+
spacing: 12,
103+
useIntrinsicHeight: false,
104+
breakpoint: 980,
105+
children: [
106+
Expanded(
107+
child: _HistoryChart(
108+
title: 'CPU',
109+
color: AppTheme.primary,
110+
samples: filtered,
111+
selector: (s) => s.cpuPercent,
112+
suffix: '%',
113+
maxY: 100,
114+
),
115+
),
116+
Expanded(
117+
child: _HistoryChart(
118+
title: 'Memory',
119+
color: AppTheme.accent,
120+
samples: filtered,
121+
selector: (s) => s.memPercent,
122+
suffix: '%',
123+
maxY: 100,
124+
),
125+
),
126+
Expanded(
127+
child: _HistoryChart(
128+
title: 'Disk pressure',
129+
color: AppTheme.warning,
130+
samples: filtered,
131+
selector: (s) => s.diskPressurePercent,
132+
suffix: '%',
133+
maxY: 100,
134+
),
135+
),
136+
],
116137
),
117138
),
118-
const SizedBox(height: 10),
139+
const SizedBox(height: 12),
140+
// Give the latest processes plenty of space.
119141
Expanded(
120-
child: _HistoryChart(
121-
title: 'Disk pressure',
122-
color: AppTheme.warning,
123-
samples: filtered,
124-
selector: (s) => s.diskPressurePercent,
125-
suffix: '%',
126-
maxY: 100,
127-
),
128-
),
129-
const SizedBox(height: 10),
130-
SizedBox(
131-
height: 220,
132142
child: _TopProcessesCard(sample: filtered.last),
133143
),
134144
],

dashboard/lib/screens/overview_screen.dart

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -621,21 +621,25 @@ class _StatsStrip extends StatelessWidget {
621621
? AppTheme.success
622622
: AppTheme.textMutedFor(context),
623623
),
624+
_StatTile(
625+
label: 'Stability',
626+
value: provider.stability != null
627+
? provider.stability!.score.toStringAsFixed(0)
628+
: '—',
629+
valueColor: AppTheme.stressLow,
630+
),
624631
];
625632

626-
// Aim for 3 + 2 when space allows by computing width from constraints.
627-
// This avoids the common 4 + 1 wrap that happens with a fixed tile width.
628-
const gap = 14.0;
629-
const minTileWidth = 180.0;
630-
final canDoThree =
631-
(c.maxWidth - (gap * 2)) / 3 >= minTileWidth; // 3 columns
632-
final cols = canDoThree ? 3 : 2;
633+
// Prefer a clean 3x2 grid on wide layouts.
634+
final isWide = c.maxWidth >= 780;
635+
final cols = isWide ? 3 : 2;
636+
final gap = 14.0;
633637
final itemWidth = (c.maxWidth - (gap * (cols - 1))) / cols;
634638
return Wrap(
635639
spacing: gap,
636640
runSpacing: gap,
637641
children: [
638-
for (final t in tiles) SizedBox(width: itemWidth, child: t),
642+
for (final t in tiles) SizedBox(width: itemWidth, child: t)
639643
],
640644
);
641645
},

dashboard/lib/screens/settings_screen.dart

Lines changed: 40 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -45,23 +45,49 @@ class _SettingsScreenState extends State<SettingsScreen> {
4545
bottom: BorderSide(color: Theme.of(context).dividerColor),
4646
),
4747
),
48-
child: Column(
49-
crossAxisAlignment: CrossAxisAlignment.start,
48+
child: Row(
5049
children: [
51-
Text(
52-
'Settings',
53-
style: TextStyle(
54-
color: Theme.of(context).colorScheme.onSurface,
55-
fontSize: 16,
56-
fontWeight: FontWeight.w600,
50+
Expanded(
51+
child: Column(
52+
crossAxisAlignment: CrossAxisAlignment.start,
53+
children: [
54+
Text(
55+
'Settings',
56+
style: TextStyle(
57+
color: Theme.of(context).colorScheme.onSurface,
58+
fontSize: 16,
59+
fontWeight: FontWeight.w600,
60+
),
61+
),
62+
Text(
63+
'Alert thresholds, optional process safeguard, appearance',
64+
style: TextStyle(
65+
color: AppTheme.textMutedFor(context),
66+
fontSize: 11,
67+
),
68+
),
69+
],
5770
),
5871
),
59-
Text(
60-
'Alert thresholds, optional process safeguard, appearance',
61-
style: TextStyle(
62-
color: AppTheme.textMutedFor(context),
63-
fontSize: 11,
64-
),
72+
IconButton.filledTonal(
73+
tooltip: 'Save preferences',
74+
onPressed: () async {
75+
await settings.save();
76+
if (!context.mounted) return;
77+
final eng = context.read<EngineProvider>();
78+
final ok = await eng.pushUserPreferences();
79+
if (!context.mounted) return;
80+
ScaffoldMessenger.of(context).showSnackBar(
81+
SnackBar(
82+
content: Text(
83+
ok
84+
? 'Preferences saved'
85+
: 'Saved locally; engine was offline — will sync when connected',
86+
),
87+
),
88+
);
89+
},
90+
icon: const Icon(Icons.save_outlined),
6591
),
6692
],
6793
),
@@ -424,27 +450,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
424450
onChanged: settings.setDesktopNotifications,
425451
),
426452
),
427-
const SizedBox(height: 24),
428-
FilledButton.icon(
429-
onPressed: () async {
430-
await settings.save();
431-
if (!context.mounted) return;
432-
final eng = context.read<EngineProvider>();
433-
final ok = await eng.pushUserPreferences();
434-
if (!context.mounted) return;
435-
ScaffoldMessenger.of(context).showSnackBar(
436-
SnackBar(
437-
content: Text(
438-
ok
439-
? 'Preferences saved'
440-
: 'Saved locally; engine was offline — will sync when connected',
441-
),
442-
),
443-
);
444-
},
445-
icon: const Icon(Icons.save_outlined),
446-
label: const Text('Save preferences'),
447-
),
448453
],
449454
),
450455
),

0 commit comments

Comments
 (0)