history_page.dart 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_riverpod/flutter_riverpod.dart';
  3. import 'package:go_router/go_router.dart';
  4. import '../../../../services/analysis_providers.dart';
  5. import '../../../shared/presentation/widgets/async_value_view.dart';
  6. import '../../../shared/presentation/widgets/lab_section_scaffold.dart';
  7. class HistoryPage extends ConsumerWidget {
  8. const HistoryPage({super.key});
  9. @override
  10. Widget build(BuildContext context, WidgetRef ref) {
  11. final theme = Theme.of(context);
  12. final observations = ref.watch(observationListProvider);
  13. final sessions = ref.watch(sessionListProvider);
  14. return LabSectionScaffold(
  15. eyebrow: 'History',
  16. title: 'Browse prior observations and experiment sessions.',
  17. description:
  18. 'This page keeps the mobile lab stateful by showing the observation archive and the experiment session archive side by side.',
  19. children: [
  20. AsyncValueView(
  21. value: observations,
  22. loadingMessage: 'Loading observations...',
  23. data: (items) => Card(
  24. child: Padding(
  25. padding: const EdgeInsets.all(20),
  26. child: Column(
  27. crossAxisAlignment: CrossAxisAlignment.start,
  28. children: [
  29. Text(
  30. 'Observation Archive',
  31. style: theme.textTheme.titleLarge,
  32. ),
  33. const SizedBox(height: 8),
  34. if (items.isEmpty)
  35. Text(
  36. 'No observations stored yet.',
  37. style: theme.textTheme.bodyMedium,
  38. )
  39. else
  40. ...items.map(
  41. (item) => ListTile(
  42. contentPadding: EdgeInsets.zero,
  43. title: Text(item.id),
  44. subtitle: Text(
  45. '${item.durationMs} ms | ${item.sampleRate} Hz | ${item.tags.join(', ')}',
  46. ),
  47. trailing: const Icon(Icons.chevron_right),
  48. onTap: () {
  49. ref
  50. .read(sessionActionsProvider)
  51. .selectObservation(item.id);
  52. context.goNamed('observation');
  53. },
  54. ),
  55. ),
  56. ],
  57. ),
  58. ),
  59. ),
  60. ),
  61. const SizedBox(height: 16),
  62. AsyncValueView(
  63. value: sessions,
  64. loadingMessage: 'Loading session archive...',
  65. data: (items) => Card(
  66. child: Padding(
  67. padding: const EdgeInsets.all(20),
  68. child: Column(
  69. crossAxisAlignment: CrossAxisAlignment.start,
  70. children: [
  71. Text('Session Archive', style: theme.textTheme.titleLarge),
  72. const SizedBox(height: 8),
  73. if (items.isEmpty)
  74. Text(
  75. 'No backend sessions found yet.',
  76. style: theme.textTheme.bodyMedium,
  77. )
  78. else
  79. ...items.map(
  80. (item) => ListTile(
  81. contentPadding: EdgeInsets.zero,
  82. title: Text(item.observationId),
  83. subtitle: Text('${item.status} | ${item.message}'),
  84. trailing: const Icon(Icons.chevron_right),
  85. onTap: () {
  86. ref
  87. .read(sessionActionsProvider)
  88. .selectSession(item.sessionId);
  89. context.goNamed('observation');
  90. },
  91. ),
  92. ),
  93. ],
  94. ),
  95. ),
  96. ),
  97. ),
  98. ],
  99. );
  100. }
  101. }