import 'package:flutter/material.dart'; import '../api.dart'; import '../main.dart'; import 'files_admin_screen.dart'; import 'teamer_admin_screen.dart'; import 'ui.dart'; import 'wahl_admin_screen.dart'; /// Leitungsteam admin: KCs, their Gemeinden, and pending self-registrations. class AdminScreen extends StatefulWidget { const AdminScreen({super.key}); @override State createState() => _AdminScreenState(); } class _AdminScreenState extends State { Future>? _future; @override void didChangeDependencies() { super.didChangeDependencies(); _future ??= AppScope.of(context).api.kcs(); } void _reload() => setState(() => _future = AppScope.of(context).api.kcs()); Future _createKc() async { final api = AppScope.of(context).api; final name = await promptText(context, 'Neues KC', 'Name'); if (name == null || name.isEmpty || !mounted) return; try { await api.createKc(name); if (mounted) _reload(); } catch (e) { if (mounted) toast(context, '$e'); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Verwaltung')), floatingActionButton: FloatingActionButton.extended( onPressed: _createKc, icon: const Icon(Icons.add), label: const Text('KC'), ), body: FutureBuilder>( future: _future, builder: (context, snap) { if (snap.connectionState != ConnectionState.done) { return const Center(child: CircularProgressIndicator()); } if (snap.hasError) { return Center( child: Padding( padding: const EdgeInsets.all(24), child: Text('${snap.error}', textAlign: TextAlign.center), ), ); } final kcs = snap.data!; if (kcs.isEmpty) { return const Center(child: Text('Noch keine KCs. Unten anlegen.')); } return ListView( children: [ for (final kc in kcs) ListTile( leading: const Icon(Icons.festival), title: Text(kc.name), subtitle: Text('Code ${kc.inviteCode}'), trailing: const Icon(Icons.chevron_right), onTap: () => Navigator.of(context).push( MaterialPageRoute(builder: (_) => KcDetailScreen(kc: kc)), ), ), ], ); }, ), ); } } class KcDetailScreen extends StatefulWidget { const KcDetailScreen({super.key, required this.kc}); final Kc kc; @override State createState() => _KcDetailScreenState(); } class _KcDetailScreenState extends State { Future>? _gemeinden; Future>? _requests; Api get _api => AppScope.of(context).api; @override void didChangeDependencies() { super.didChangeDependencies(); _gemeinden ??= _api.gemeinden(widget.kc.id); _requests ??= _api.onboardingRequests(widget.kc.id); } void _reloadGemeinden() => setState(() => _gemeinden = _api.gemeinden(widget.kc.id)); void _reloadRequests() => setState(() => _requests = _api.onboardingRequests(widget.kc.id)); Future _addGemeinde() async { final api = _api; final name = await promptText(context, 'Neue Gemeinde', 'Name'); if (name == null || name.isEmpty || !mounted) return; try { await api.createGemeinde(widget.kc.id, name); if (mounted) _reloadGemeinden(); } catch (e) { if (mounted) toast(context, '$e'); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(widget.kc.name)), body: ListView( padding: const EdgeInsets.all(16), children: [ Card( child: ListTile( title: const Text('Einladungscode'), subtitle: Text(widget.kc.inviteCode), trailing: const Icon(Icons.qr_code_2), ), ), Card( child: ListTile( leading: const Icon(Icons.how_to_vote), title: const Text('Workshop-Wahlen'), trailing: const Icon(Icons.chevron_right), onTap: () => Navigator.of(context).push( MaterialPageRoute(builder: (_) => WahlAdminScreen(kcId: widget.kc.id)), ), ), ), Card( child: ListTile( leading: const Icon(Icons.folder_shared), title: const Text('Dateien'), trailing: const Icon(Icons.chevron_right), onTap: () => Navigator.of(context).push( MaterialPageRoute(builder: (_) => FilesAdminScreen(kcId: widget.kc.id)), ), ), ), const SizedBox(height: 16), SectionHeader('Gemeinden', action: TextButton.icon( onPressed: _addGemeinde, icon: const Icon(Icons.add), label: const Text('Hinzufügen'), )), _GemeindeList(future: _gemeinden!, onRetry: _reloadGemeinden), const Divider(height: 40), Text('Offene Verantwortlichen-Anfragen', style: Theme.of(context).textTheme.titleMedium), const SizedBox(height: 8), _RequestList( future: _requests!, onAction: (id, approve) async { try { approve ? await _api.approveOnboarding(id) : await _api.rejectOnboarding(id); _reloadRequests(); } catch (e) { if (context.mounted) toast(context, '$e'); } }, ), ], ), ); } } class _GemeindeList extends StatelessWidget { const _GemeindeList({required this.future, required this.onRetry}); final Future> future; final VoidCallback onRetry; @override Widget build(BuildContext context) { return FutureBuilder>( future: future, builder: (context, snap) { if (snap.connectionState != ConnectionState.done) { return const Padding( padding: EdgeInsets.all(12), child: Center(child: CircularProgressIndicator()), ); } if (snap.hasError) { return TextButton(onPressed: onRetry, child: Text('Fehler: ${snap.error}')); } final gemeinden = snap.data!; if (gemeinden.isEmpty) return const Text('Noch keine Gemeinden.'); return Column( children: [ for (final g in gemeinden) ListTile( dense: true, leading: const Icon(Icons.church), title: Text(g.name), trailing: const Icon(Icons.chevron_right), onTap: () => Navigator.of(context).push( MaterialPageRoute( builder: (_) => TeamerAdminScreen(gemeindeId: g.id, gemeindeName: g.name), ), ), ), ], ); }, ); } } class _RequestList extends StatelessWidget { const _RequestList({required this.future, required this.onAction}); final Future> future; final Future Function(String id, bool approve) onAction; @override Widget build(BuildContext context) { return FutureBuilder>( future: future, builder: (context, snap) { if (snap.connectionState != ConnectionState.done) { return const Padding( padding: EdgeInsets.all(12), child: Center(child: CircularProgressIndicator()), ); } if (snap.hasError) { return Text('Fehler: ${snap.error}'); } final requests = snap.data!; if (requests.isEmpty) return const Text('Keine offenen Anfragen.'); return Column( children: [ for (final r in requests) Card( child: ListTile( title: Text(r.userName.isEmpty ? r.userEmail : r.userName), subtitle: Text('${r.userEmail}\nGemeinde: ${r.gemeindeName}'), isThreeLine: true, trailing: Row( mainAxisSize: MainAxisSize.min, children: [ IconButton( tooltip: 'Genehmigen', icon: const Icon(Icons.check, color: Colors.green), onPressed: () => onAction(r.id, true), ), IconButton( tooltip: 'Ablehnen', icon: const Icon(Icons.close, color: Colors.red), onPressed: () => onAction(r.id, false), ), ], ), ), ), ], ); }, ); } }