import 'package:flutter/material.dart'; import '../api.dart'; import '../main.dart'; import 'ui.dart'; /// Teamer administration for one Gemeinde — usable by the Leitungsteam or the /// responsible Gemeinde Verantwortliche/r. class TeamerAdminScreen extends StatefulWidget { const TeamerAdminScreen({ super.key, required this.gemeindeId, required this.gemeindeName, }); final String gemeindeId; final String gemeindeName; @override State createState() => _TeamerAdminScreenState(); } class _TeamerAdminScreenState extends State { Future>? _teamer; Future>? _invites; Api get _api => AppScope.of(context).api; @override void didChangeDependencies() { super.didChangeDependencies(); _teamer ??= _api.teamerFor(widget.gemeindeId); _invites ??= _api.teamerInvitesFor(widget.gemeindeId); } void _reloadTeamer() => setState(() => _teamer = _api.teamerFor(widget.gemeindeId)); void _reloadInvites() => setState(() => _invites = _api.teamerInvitesFor(widget.gemeindeId)); Future _addTeamer() async { final api = _api; final v = await showDialog<(String, String, String, String)>( context: context, builder: (_) => const _NewTeamerDialog(), ); if (v == null || !mounted) return; try { await api.createTeamer( widget.gemeindeId, firstName: v.$1, lastName: v.$2, email: v.$3, password: v.$4, ); if (mounted) _reloadTeamer(); } catch (e) { if (mounted) toast(context, '$e'); } } Future _addInvite({required bool personal}) async { final api = _api; String? email; if (personal) { email = await promptText(context, 'E-Mail-Invite', 'E-Mail-Adresse'); if (email == null || email.isEmpty || !mounted) return; } try { final inv = await api.createTeamerInvite(widget.gemeindeId, email: email); if (!mounted) return; _reloadInvites(); showDialog( context: context, builder: (_) => AlertDialog( title: const Text('Invite erstellt'), content: SelectableText( personal ? 'E-Mail an ${inv.email} ausgelöst.\n\nToken: ${inv.token}' : 'Gruppen-Link-Token (mehrfach nutzbar):\n\n${inv.token}', ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('OK'), ), ], ), ); } catch (e) { if (mounted) toast(context, '$e'); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Teamer:innen · ${widget.gemeindeName}')), body: ListView( padding: const EdgeInsets.all(16), children: [ SectionHeader('Konten', action: TextButton.icon( onPressed: _addTeamer, icon: const Icon(Icons.person_add), label: const Text('Anlegen'), )), FutureBuilder>( future: _teamer, builder: (context, snap) { if (snap.connectionState != ConnectionState.done) { return const LinearProgressIndicator(); } if (snap.hasError) return Text('Fehler: ${snap.error}'); final list = snap.data!; if (list.isEmpty) return const Text('Noch keine Teamer:innen.'); return Column( children: [ for (final t in list) ListTile( dense: true, leading: const Icon(Icons.person), title: Text(t.name.isEmpty ? t.email : t.name), subtitle: Text(t.email), ), ], ); }, ), const Divider(height: 40), SectionHeader('Einladungen', action: Wrap( spacing: 4, children: [ TextButton( onPressed: () => _addInvite(personal: false), child: const Text('Gruppen-Link'), ), TextButton( onPressed: () => _addInvite(personal: true), child: const Text('per E-Mail'), ), ], )), FutureBuilder>( future: _invites, builder: (context, snap) { if (snap.connectionState != ConnectionState.done) { return const LinearProgressIndicator(); } if (snap.hasError) return Text('Fehler: ${snap.error}'); final list = snap.data!; if (list.isEmpty) return const Text('Keine Einladungen.'); return Column( children: [ for (final i in list) ListTile( dense: true, leading: Icon(i.revoked ? Icons.block : i.email != null ? Icons.mail : Icons.link), title: Text(i.email ?? 'Gruppen-Link'), subtitle: Text( '${i.usedCount}${i.maxUses != null ? '/${i.maxUses}' : ''} genutzt' '${i.revoked ? ' · widerrufen' : ''}', ), trailing: SelectableText( i.token.substring(0, 8), style: Theme.of(context).textTheme.labelSmall, ), ), ], ); }, ), ], ), ); } } class _NewTeamerDialog extends StatefulWidget { const _NewTeamerDialog(); @override State<_NewTeamerDialog> createState() => _NewTeamerDialogState(); } class _NewTeamerDialogState extends State<_NewTeamerDialog> { final _first = TextEditingController(); final _last = TextEditingController(); final _email = TextEditingController(); final _password = TextEditingController(); @override Widget build(BuildContext context) { return AlertDialog( title: const Text('Teamer:in anlegen'), content: Column( mainAxisSize: MainAxisSize.min, children: [ TextField(controller: _first, decoration: const InputDecoration(labelText: 'Vorname')), TextField(controller: _last, decoration: const InputDecoration(labelText: 'Nachname')), TextField(controller: _email, decoration: const InputDecoration(labelText: 'E-Mail')), TextField( controller: _password, obscureText: true, decoration: const InputDecoration(labelText: 'Passwort (min. 8)'), ), ], ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('Abbrechen')), FilledButton( onPressed: () => Navigator.of(context).pop(( _first.text.trim(), _last.text.trim(), _email.text.trim(), _password.text, )), child: const Text('Anlegen'), ), ], ); } }