Skip to content

Commit 6638578

Browse files
feat: integrate TopStatusBar widget into dashboard and update theme color usage for dual-theme support
1 parent 1d408e9 commit 6638578

7 files changed

Lines changed: 68 additions & 35 deletions

File tree

dashboard/lib/screens/dashboard_screen.dart

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'package:sentracore_dashboard/screens/diagnostics_screen.dart';
88
import 'package:sentracore_dashboard/theme/app_theme.dart';
99
import 'package:sentracore_dashboard/widgets/connection_banner.dart';
1010
import 'package:sentracore_dashboard/providers/settings_provider.dart';
11+
import 'package:sentracore_dashboard/widgets/top_status_bar.dart';
1112

1213
/// Root shell with persistent navigation rail and page switching.
1314
class DashboardScreen extends StatefulWidget {
@@ -35,6 +36,7 @@ class _DashboardScreenState extends State<DashboardScreen> {
3536
body: Column(
3637
children: [
3738
if (!provider.connected) const ConnectionBanner(),
39+
const TopStatusBar(),
3840
Expanded(
3941
child: Row(
4042
children: [
@@ -78,7 +80,7 @@ class _SentraNavRail extends StatelessWidget {
7880
final stability = provider.stability;
7981
final stabilityColor = stability != null
8082
? AppTheme.stabilityColor(stability.state)
81-
: AppTheme.textMuted;
83+
: AppTheme.textMutedFor(context);
8284
final stabilityScore = stability?.score.toStringAsFixed(0) ?? '--';
8385

8486
return Container(
@@ -142,7 +144,6 @@ class _SentraNavRail extends StatelessWidget {
142144
color: stabilityColor,
143145
fontSize: 16,
144146
fontWeight: FontWeight.w800,
145-
fontFamily: 'Outfit',
146147
),
147148
),
148149
Text(
@@ -237,7 +238,7 @@ class _SentraNavRail extends StatelessWidget {
237238
),
238239
child: Icon(
239240
isSelected ? activeIcon : icon,
240-
color: isSelected ? AppTheme.primary : AppTheme.textMuted,
241+
color: isSelected ? AppTheme.primary : AppTheme.textMutedFor(context),
241242
size: 22,
242243
),
243244
),

dashboard/lib/theme/app_theme.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,4 +152,20 @@ class AppTheme {
152152
? darkTextSecondary
153153
: lightTextSecondary)
154154
.withValues(alpha: 0.85);
155+
156+
// ---------------------------------------------------------------------------
157+
// Backward-compatible getters (legacy)
158+
//
159+
// Many widgets in this codebase reference `AppTheme.textMuted`, `surfaceLight`,
160+
// etc. These were historically "dark defaults". Keep them to avoid churn and
161+
// migrate callsites gradually to the `...For(context)` methods for true
162+
// dual-theme support.
163+
// ---------------------------------------------------------------------------
164+
static Color get background => darkBackground;
165+
static Color get surface => darkSurface;
166+
static Color get surfaceLight => darkSurfaceLight;
167+
static Color get border => darkBorder;
168+
static Color get textPrimary => darkTextPrimary;
169+
static Color get textSecondary => darkTextSecondary;
170+
static Color get textMuted => darkTextSecondary;
155171
}

dashboard/lib/widgets/connection_banner.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class ConnectionBanner extends StatelessWidget {
3939
Text(
4040
'Make sure the engine is running: python -m engine.main',
4141
style: TextStyle(
42-
color: AppTheme.textMuted,
42+
color: AppTheme.textMutedFor(context),
4343
fontSize: 11,
4444
),
4545
),

dashboard/lib/widgets/metric_chart_card.dart

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class MetricChartCard extends StatelessWidget {
3636
Text(
3737
title,
3838
style: TextStyle(
39-
color: AppTheme.textSecondary,
39+
color: AppTheme.textSecondaryFor(context),
4040
fontSize: 12,
4141
fontWeight: FontWeight.w500,
4242
),
@@ -61,7 +61,7 @@ class MetricChartCard extends StatelessWidget {
6161
child: Text(
6262
'Collecting data...',
6363
style: TextStyle(
64-
color: AppTheme.textMuted,
64+
color: AppTheme.textMutedFor(context),
6565
fontSize: 11,
6666
),
6767
),
@@ -75,7 +75,9 @@ class MetricChartCard extends StatelessWidget {
7575
drawVerticalLine: false,
7676
horizontalInterval: maxY / 4,
7777
getDrawingHorizontalLine: (value) => FlLine(
78-
color: AppTheme.border.withValues(alpha: 0.5),
78+
color: Theme.of(context)
79+
.dividerColor
80+
.withValues(alpha: 0.5),
7981
strokeWidth: 0.5,
8082
),
8183
),

dashboard/lib/widgets/prediction_panel.dart

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class PredictionPanel extends StatelessWidget {
2525
Text(
2626
'Prediction & Forecasting',
2727
style: TextStyle(
28-
color: AppTheme.textPrimary,
28+
color: AppTheme.textPrimaryFor(context),
2929
fontSize: 14,
3030
fontWeight: FontWeight.w600,
3131
),
@@ -34,17 +34,19 @@ class PredictionPanel extends StatelessWidget {
3434
),
3535
Divider(height: 24, color: Theme.of(context).dividerColor),
3636
if (prediction == null)
37-
_buildEmptyState()
37+
_buildEmptyState(context)
3838
else ...[
39-
_buildRiskScore(prediction.riskScore),
39+
_buildRiskScore(context, prediction.riskScore),
4040
const SizedBox(height: 16),
4141
_buildEtaRow(
42+
context,
4243
'Memory Exhaustion',
4344
prediction.memoryExhaustionEtaSec,
4445
Icons.memory,
4546
),
4647
const SizedBox(height: 8),
4748
_buildEtaRow(
49+
context,
4850
'CPU Saturation',
4951
prediction.cpuCriticalEtaSec,
5052
Icons.speed,
@@ -56,19 +58,19 @@ class PredictionPanel extends StatelessWidget {
5658
);
5759
}
5860

59-
Widget _buildEmptyState() {
61+
Widget _buildEmptyState(BuildContext context) {
6062
return Center(
6163
child: Padding(
6264
padding: const EdgeInsets.all(24.0),
6365
child: Text(
6466
'Waiting for trend data...',
65-
style: TextStyle(color: AppTheme.textMuted),
67+
style: TextStyle(color: AppTheme.textMutedFor(context)),
6668
),
6769
),
6870
);
6971
}
7072

71-
Widget _buildRiskScore(double score) {
73+
Widget _buildRiskScore(BuildContext context, double score) {
7274
Color color = AppTheme.stressLow;
7375
if (score > 60) {
7476
color = AppTheme.stressCritical;
@@ -83,7 +85,8 @@ class PredictionPanel extends StatelessWidget {
8385
mainAxisAlignment: MainAxisAlignment.spaceBetween,
8486
children: [
8587
Text('Degradation Risk',
86-
style: TextStyle(color: AppTheme.textSecondary, fontSize: 12)),
88+
style: TextStyle(
89+
color: AppTheme.textSecondaryFor(context), fontSize: 12)),
8790
Text('${score.toStringAsFixed(0)}%',
8891
style: TextStyle(color: color, fontWeight: FontWeight.bold)),
8992
],
@@ -93,7 +96,8 @@ class PredictionPanel extends StatelessWidget {
9396
borderRadius: BorderRadius.circular(4),
9497
child: LinearProgressIndicator(
9598
value: score / 100,
96-
backgroundColor: AppTheme.surfaceLight,
99+
backgroundColor:
100+
Theme.of(context).dividerColor.withValues(alpha: 0.2),
97101
valueColor: AlwaysStoppedAnimation<Color>(color),
98102
minHeight: 6,
99103
),
@@ -102,9 +106,10 @@ class PredictionPanel extends StatelessWidget {
102106
);
103107
}
104108

105-
Widget _buildEtaRow(String label, double? etaSec, IconData icon) {
109+
Widget _buildEtaRow(
110+
BuildContext context, String label, double? etaSec, IconData icon) {
106111
String text = 'Stable';
107-
Color color = AppTheme.textMuted;
112+
Color color = AppTheme.textMutedFor(context);
108113

109114
if (etaSec != null) {
110115
if (etaSec < 60) {
@@ -120,15 +125,16 @@ class PredictionPanel extends StatelessWidget {
120125
return Container(
121126
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
122127
decoration: BoxDecoration(
123-
color: AppTheme.surfaceLight,
128+
color: AppTheme.surfaceLightFor(context),
124129
borderRadius: BorderRadius.circular(6),
125130
),
126131
child: Row(
127132
children: [
128-
Icon(icon, size: 14, color: AppTheme.textSecondary),
133+
Icon(icon, size: 14, color: AppTheme.textSecondaryFor(context)),
129134
const SizedBox(width: 8),
130135
Text(label,
131-
style: TextStyle(color: AppTheme.textSecondary, fontSize: 13)),
136+
style: TextStyle(
137+
color: AppTheme.textSecondaryFor(context), fontSize: 13)),
132138
const Spacer(),
133139
Text(
134140
text,

dashboard/lib/widgets/rca_panel.dart

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class RcaPanel extends StatelessWidget {
2525
Text(
2626
'Root Cause Analysis',
2727
style: TextStyle(
28-
color: AppTheme.textPrimary,
28+
color: AppTheme.textPrimaryFor(context),
2929
fontSize: 14,
3030
fontWeight: FontWeight.w600,
3131
),
@@ -34,21 +34,26 @@ class RcaPanel extends StatelessWidget {
3434
),
3535
Divider(height: 24, color: Theme.of(context).dividerColor),
3636
if (rca == null)
37-
_buildEmptyState()
37+
_buildEmptyState(context)
3838
else ...[
39-
_buildAlertBanner(provider.currentState?.alert.lastMessage ?? ''),
39+
_buildAlertBanner(
40+
context, provider.currentState?.alert.lastMessage ?? ''),
4041
const SizedBox(height: 16),
4142
_buildDataRow(
42-
'Primary Bottleneck:', rca.primaryBottleneck.toUpperCase()),
43+
context,
44+
'Primary Bottleneck:',
45+
rca.primaryBottleneck.toUpperCase()),
4346
if (rca.suspectProcess != null) ...[
4447
const SizedBox(height: 8),
45-
_buildDataRow('Suspect Process:',
48+
_buildDataRow(
49+
context,
50+
'Suspect Process:',
4651
'${rca.suspectProcess!['name']} (PID: ${rca.suspectProcess!['pid']})'),
4752
],
4853
if (rca.triggerEvent != null) ...[
4954
const SizedBox(height: 8),
5055
_buildDataRow(
51-
'Trigger Event:', rca.triggerEvent!['event_type']),
56+
context, 'Trigger Event:', rca.triggerEvent!['event_type']),
5257
],
5358
],
5459
],
@@ -57,19 +62,19 @@ class RcaPanel extends StatelessWidget {
5762
);
5863
}
5964

60-
Widget _buildEmptyState() {
65+
Widget _buildEmptyState(BuildContext context) {
6166
return Center(
6267
child: Padding(
6368
padding: const EdgeInsets.all(24.0),
6469
child: Text(
6570
'No recent alerts to analyze.',
66-
style: TextStyle(color: AppTheme.textMuted),
71+
style: TextStyle(color: AppTheme.textMutedFor(context)),
6772
),
6873
),
6974
);
7075
}
7176

72-
Widget _buildAlertBanner(String message) {
77+
Widget _buildAlertBanner(BuildContext context, String message) {
7378
if (message.isEmpty) return const SizedBox.shrink();
7479

7580
return Container(
@@ -80,26 +85,29 @@ class RcaPanel extends StatelessWidget {
8085
),
8186
child: Text(
8287
message,
83-
style:
84-
TextStyle(color: AppTheme.textSecondary, fontSize: 13, height: 1.4),
88+
style: TextStyle(
89+
color: AppTheme.textSecondaryFor(context),
90+
fontSize: 13,
91+
height: 1.4,
92+
),
8593
),
8694
);
8795
}
8896

89-
Widget _buildDataRow(String label, String value) {
97+
Widget _buildDataRow(BuildContext context, String label, String value) {
9098
return Row(
9199
crossAxisAlignment: CrossAxisAlignment.start,
92100
children: [
93101
SizedBox(
94102
width: 130,
95103
child: Text(label,
96-
style: TextStyle(color: AppTheme.textSecondary, fontSize: 13)),
104+
style: const TextStyle(color: Colors.grey, fontSize: 13)),
97105
),
98106
Expanded(
99107
child: Text(
100108
value,
101109
style: TextStyle(
102-
color: AppTheme.textPrimary,
110+
color: AppTheme.textPrimaryFor(context),
103111
fontSize: 13,
104112
fontWeight: FontWeight.w500),
105113
),

dashboard/lib/widgets/top_status_bar.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class TopStatusBar extends StatelessWidget {
1111
final provider = context.watch<EngineProvider>();
1212
final state = provider.currentState;
1313
final stability = provider.stability;
14-
final risk = state?.prediction.riskScore;
14+
final risk = state?.prediction?.riskScore;
1515
final trend = state?.trend;
1616

1717
final stabilityState = (stability?.state ?? 'unknown').toLowerCase();

0 commit comments

Comments
 (0)