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
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