feat(hub): list installed capabilities from the connected hub
HubCapabilitiesCard wires HubClient.listCapabilities() into the hub-status page below the connection form: once the app starts in hub mode and the connection is healthy, the card shows capability@version, the providing module, and its kind for every entry the hub reports. In mock mode the card prints an explanatory hint instead of loading, so no confusing empty-state appears when the user is just browsing the demo data. A small refresh-icon next to the heading lets the user re-poll without a full page reload — useful right after a chain install in another terminal. Signed-off-by: flemming-it <sf@flemming.it>
This commit is contained in:
parent
39733628b6
commit
cf0ee4fd7a
2 changed files with 131 additions and 0 deletions
|
|
@ -4,6 +4,7 @@ import '../data/hub_endpoint.dart';
|
||||||
import '../data/hub_repository.dart';
|
import '../data/hub_repository.dart';
|
||||||
import '../data/repository.dart';
|
import '../data/repository.dart';
|
||||||
import '../theme/lawheatmap_tokens.dart';
|
import '../theme/lawheatmap_tokens.dart';
|
||||||
|
import '../widgets/hub_capabilities_card.dart';
|
||||||
import '../widgets/lawheatmap_card.dart';
|
import '../widgets/lawheatmap_card.dart';
|
||||||
|
|
||||||
/// Hub connection + settings. Read-only summary at the top, then
|
/// Hub connection + settings. Read-only summary at the top, then
|
||||||
|
|
@ -238,6 +239,8 @@ class _HubStatusPageState extends State<HubStatusPage> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: LawHeatmapSpace.lg),
|
const SizedBox(height: LawHeatmapSpace.lg),
|
||||||
|
HubCapabilitiesCard(repository: repo),
|
||||||
|
const SizedBox(height: LawHeatmapSpace.lg),
|
||||||
LawHeatmapCard(
|
LawHeatmapCard(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
|
|
||||||
128
app/lib/widgets/hub_capabilities_card.dart
Normal file
128
app/lib/widgets/hub_capabilities_card.dart
Normal file
|
|
@ -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<HubCapabilitiesCard> createState() => _HubCapabilitiesCardState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _HubCapabilitiesCardState extends State<HubCapabilitiesCard> {
|
||||||
|
late Future<List<chain.CapabilityEntry>> _future;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_future = _load();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<chain.CapabilityEntry>> _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<List<chain.CapabilityEntry>>(
|
||||||
|
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 <name>` 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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue