diff --git a/app/lib/pages/hub_status_page.dart b/app/lib/pages/hub_status_page.dart index 82454c8..8784ecb 100644 --- a/app/lib/pages/hub_status_page.dart +++ b/app/lib/pages/hub_status_page.dart @@ -4,6 +4,7 @@ import '../data/hub_endpoint.dart'; import '../data/hub_repository.dart'; import '../data/repository.dart'; import '../theme/lawheatmap_tokens.dart'; +import '../widgets/hub_capabilities_card.dart'; import '../widgets/lawheatmap_card.dart'; /// Hub connection + settings. Read-only summary at the top, then @@ -238,6 +239,8 @@ class _HubStatusPageState extends State { ), ), const SizedBox(height: LawHeatmapSpace.lg), + HubCapabilitiesCard(repository: repo), + const SizedBox(height: LawHeatmapSpace.lg), LawHeatmapCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/app/lib/widgets/hub_capabilities_card.dart b/app/lib/widgets/hub_capabilities_card.dart new file mode 100644 index 0000000..42d644e --- /dev/null +++ b/app/lib/widgets/hub_capabilities_card.dart @@ -0,0 +1,128 @@ +import 'package:chain_client_sdk/chain_client_sdk.dart' as chain; +import 'package:flutter/material.dart'; + +import '../data/hub_repository.dart'; +import '../data/repository.dart'; +import '../theme/lawheatmap_tokens.dart'; +import 'lawheatmap_card.dart'; + +/// Lists the capabilities the connected hub has installed. +/// Empty / explanatory state when the active repository is not +/// a live [HubRepository]. Real wire to HubClient.listCapabilities(). +class HubCapabilitiesCard extends StatefulWidget { + const HubCapabilitiesCard({super.key, required this.repository}); + + final EvaluationRepository repository; + + @override + State createState() => _HubCapabilitiesCardState(); +} + +class _HubCapabilitiesCardState extends State { + late Future> _future; + + @override + void initState() { + super.initState(); + _future = _load(); + } + + Future> _load() async { + final repo = widget.repository; + if (repo is! HubRepository || !repo.isHealthy) return const []; + return repo.client.listCapabilities(); + } + + @override + Widget build(BuildContext context) { + final t = Theme.of(context).textTheme; + final repo = widget.repository; + final liveHub = repo is HubRepository && repo.isHealthy; + + return LawHeatmapCard( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row(children: [ + Text('Installierte Capabilities', style: t.headlineSmall), + const Spacer(), + if (liveHub) + IconButton( + tooltip: 'Neu laden', + onPressed: () => setState(() => _future = _load()), + icon: const Icon(Icons.refresh), + ), + ]), + const SizedBox(height: LawHeatmapSpace.sm), + if (!liveHub) + Text( + 'Sichtbar sobald die App im Hub-Modus startet und ' + 'der Hub erreichbar ist (siehe Verbindung oben).', + style: t.labelSmall?.copyWith( + color: LawHeatmapColors.mute, + ), + ) + else + FutureBuilder>( + future: _future, + builder: (context, snap) { + if (snap.connectionState == ConnectionState.waiting) { + return const Padding( + padding: EdgeInsets.all(LawHeatmapSpace.md), + child: CircularProgressIndicator(), + ); + } + if (snap.hasError) { + return Text('Fehler: ${snap.error}', + style: t.labelLarge + ?.copyWith(color: LawHeatmapColors.harmWarm)); + } + final items = snap.data ?? const []; + if (items.isEmpty) { + return Text( + 'Hub hat noch keine Capabilities installiert. ' + 'Mit `chain install ` hinzufügen.', + style: t.bodyLarge?.copyWith( + color: LawHeatmapColors.mute, + ), + ); + } + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + for (final c in items) + Padding( + padding: const EdgeInsets.symmetric(vertical: 4), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 280, + child: Text( + '${c.capability}@${c.version}', + style: t.bodyLarge?.copyWith( + fontFamily: LawHeatmapTypography.mono, + ), + ), + ), + Expanded( + child: Text( + '${c.moduleName} ${c.moduleVersion}' + '${c.kind.isEmpty ? "" : " · ${c.kind}"}', + style: t.labelSmall?.copyWith( + color: LawHeatmapColors.mute, + ), + ), + ), + ], + ), + ), + ], + ); + }, + ), + ], + ), + ); + } +}