import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../observability/presentation/observability_controller.dart'; import '../../../shared/widgets/glass_panel.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), GlassPanel( 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: GlassPanel( 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), ], ), ), ), ); } }