148 lines
5.0 KiB
Dart
148 lines
5.0 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
import '../../observability/presentation/observability_controller.dart';
|
|
|
|
class OverviewPage extends ConsumerWidget {
|
|
const OverviewPage({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context, WidgetRef ref) {
|
|
final state = ref.watch(observabilityControllerProvider);
|
|
final heartbeat = state.lastHeartbeat;
|
|
final telemetry = state.observedTelemetryKinds
|
|
.map((kind) => kind.label)
|
|
.join(', ');
|
|
final identity = heartbeat == null
|
|
? 'Not observed'
|
|
: 'System ${heartbeat.systemId} / component ${heartbeat.componentId}';
|
|
final identityDetail = heartbeat == null
|
|
? 'Awaiting HEARTBEAT.'
|
|
: 'Reported by last HEARTBEAT: '
|
|
'${heartbeat.armed ? 'Armed' : 'Disarmed'} · ${heartbeat.vehicleState}';
|
|
|
|
return LayoutBuilder(
|
|
builder: (context, constraints) {
|
|
final horizontalPadding = constraints.maxWidth < 600 ? 16.0 : 24.0;
|
|
final cardWidth = constraints.maxWidth < 640
|
|
? constraints.maxWidth - (horizontalPadding * 2)
|
|
: 280.0;
|
|
|
|
return ListView(
|
|
padding: EdgeInsets.all(horizontalPadding),
|
|
children: [
|
|
Text('Overview', style: Theme.of(context).textTheme.headlineMedium),
|
|
const SizedBox(height: 8),
|
|
const Text(
|
|
'This workspace is receive-only. A link or replay never implies a '
|
|
'safe, armed, or command-capable vehicle.',
|
|
),
|
|
const SizedBox(height: 24),
|
|
Wrap(
|
|
spacing: 16,
|
|
runSpacing: 16,
|
|
children: [
|
|
_SummaryCard(
|
|
title: 'Source',
|
|
value: state.sourceLabel,
|
|
width: cardWidth,
|
|
detail: state.isReplay
|
|
? 'Deterministic replay; no physical vehicle is connected.'
|
|
: 'Receive-only transport.',
|
|
icon: Icons.visibility_outlined,
|
|
),
|
|
_SummaryCard(
|
|
title: 'Vehicle identity',
|
|
value: identity,
|
|
detail: identityDetail,
|
|
width: cardWidth,
|
|
icon: Icons.memory_outlined,
|
|
),
|
|
_SummaryCard(
|
|
title: 'Observed streams',
|
|
value: telemetry.isEmpty ? 'None' : telemetry,
|
|
detail: state.isReplay
|
|
? '${state.events.length} replayed observation(s).'
|
|
: '${state.events.length} observation(s).',
|
|
width: cardWidth,
|
|
icon: Icons.sensors_outlined,
|
|
),
|
|
_SummaryCard(
|
|
title: 'Decoder accounting',
|
|
value:
|
|
'${state.parserDropCount} parser / ${state.semanticDropCount} semantic',
|
|
detail: 'Counters are zero in the typed replay fixture.',
|
|
width: cardWidth,
|
|
icon: Icons.rule_outlined,
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 24),
|
|
Card(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(20),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'Configuration is unavailable',
|
|
style: Theme.of(context).textTheme.titleLarge,
|
|
),
|
|
const SizedBox(height: 8),
|
|
const Text(
|
|
'Kaptan firmware does not yet accept inbound configuration '
|
|
'authority. Ground Suite will not show upload, commit, arm, '
|
|
'or shutdown controls until the firmware exposes reviewed '
|
|
'authenticated ingress and safe activation.',
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
class _SummaryCard extends StatelessWidget {
|
|
const _SummaryCard({
|
|
required this.title,
|
|
required this.value,
|
|
required this.detail,
|
|
required this.icon,
|
|
required this.width,
|
|
});
|
|
|
|
final String title;
|
|
final String value;
|
|
final String detail;
|
|
final IconData icon;
|
|
final double width;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return SizedBox(
|
|
width: width,
|
|
child: Card(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(20),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Icon(icon),
|
|
const SizedBox(height: 16),
|
|
Text(title, style: Theme.of(context).textTheme.labelLarge),
|
|
const SizedBox(height: 4),
|
|
Text(value, style: Theme.of(context).textTheme.titleLarge),
|
|
const SizedBox(height: 8),
|
|
Text(detail),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|