import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../auth/application/auth_notifier.dart'; import '../../profile/infrastructure/profile_repository.dart'; import '../domain/player.dart'; import '../domain/team.dart'; import '../infrastructure/teams_repository.dart'; /// Public-facing form for any logged-in user to register a new team. /// /// Differs from the admin form in two ways: /// 1. Manager email/phone are first-class fields so the league can contact /// whoever created the team. /// 2. Win/loss/draw counters are not exposed — those are reserved for admins. class CreateTeamScreen extends ConsumerStatefulWidget { const CreateTeamScreen({super.key}); @override ConsumerState createState() => _CreateTeamScreenState(); } class _CreateTeamScreenState extends ConsumerState { final _formKey = GlobalKey(); final _nameCtrl = TextEditingController(); final _logoUrlCtrl = TextEditingController(); final _descCtrl = TextEditingController(); final _managerEmailCtrl = TextEditingController(); final _managerPhoneCtrl = TextEditingController(); final List<_PlayerDraft> _roster = <_PlayerDraft>[]; bool _hydratedEmail = false; bool _submitting = false; @override void dispose() { _nameCtrl.dispose(); _logoUrlCtrl.dispose(); _descCtrl.dispose(); _managerEmailCtrl.dispose(); _managerPhoneCtrl.dispose(); for (final p in _roster) { p.dispose(); } super.dispose(); } void _addPlayerRow() { setState(() => _roster.add(_PlayerDraft())); } void _removePlayerRow(_PlayerDraft draft) { setState(() { _roster.remove(draft); draft.dispose(); }); } Future _submit() async { if (!(_formKey.currentState?.validate() ?? false)) return; final user = ref.read(authNotifierProvider).valueOrNull; final id = 'team_${DateTime.now().millisecondsSinceEpoch}'; final players = []; for (var i = 0; i < _roster.length; i++) { final draft = _roster[i]; final name = draft.nameCtrl.text.trim(); if (name.isEmpty) continue; players.add( Player( id: '${id}_p$i', name: name, jerseyNumber: int.tryParse(draft.jerseyCtrl.text.trim()), position: draft.positionCtrl.text.trim().isEmpty ? null : draft.positionCtrl.text.trim(), ), ); } final team = Team( id: id, name: _nameCtrl.text.trim(), logoUrl: _logoUrlCtrl.text.trim().isEmpty ? null : _logoUrlCtrl.text.trim(), description: _descCtrl.text.trim().isEmpty ? null : _descCtrl.text.trim(), managerId: user?.uid, managerEmail: _managerEmailCtrl.text.trim(), managerPhone: _managerPhoneCtrl.text.trim().isEmpty ? null : _managerPhoneCtrl.text.trim(), players: players, // Manager-submitted teams require admin approval before going public. status: TeamStatus.pending, ); setState(() => _submitting = true); try { final newId = await ref.read(teamsRepositoryProvider).createTeam(team); // Stamp the team on the manager's profile so the dashboard finds it. if (user != null) { try { await ref .read(profileRepositoryProvider) .updateTeamId(user.uid, newId); } catch (_) { // Profile may not exist yet for legacy accounts — non-fatal. } } if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Team submitted — awaiting admin approval.'), ), ); context.go('/manager'); } catch (e) { if (!mounted) return; ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text('Could not create team: $e'))); } finally { if (mounted) setState(() => _submitting = false); } } @override Widget build(BuildContext context) { final user = ref.watch(authNotifierProvider).valueOrNull; if (!_hydratedEmail && user != null) { _managerEmailCtrl.text = user.email; _hydratedEmail = true; } final theme = Theme.of(context); return Scaffold( appBar: AppBar( title: const Text('NEW TEAM'), leading: IconButton( icon: const Icon(Icons.close), onPressed: () => context.go('/teams'), ), ), body: SafeArea( child: Form( key: _formKey, child: ListView( padding: const EdgeInsets.all(16), children: [ TextFormField( controller: _nameCtrl, decoration: const InputDecoration(labelText: 'Team name'), validator: (v) => (v == null || v.trim().isEmpty) ? 'Required' : null, ), const SizedBox(height: 12), TextFormField( controller: _logoUrlCtrl, decoration: const InputDecoration( labelText: 'Logo URL (optional)', hintText: 'https://...', ), ), const SizedBox(height: 12), TextFormField( controller: _descCtrl, decoration: const InputDecoration( labelText: 'Description (optional)', ), minLines: 2, maxLines: 5, ), const SizedBox(height: 24), Text( 'CONTACT', style: theme.textTheme.labelLarge?.copyWith(letterSpacing: 1.5), ), const SizedBox(height: 8), TextFormField( controller: _managerEmailCtrl, decoration: const InputDecoration( labelText: 'Manager email', prefixIcon: Icon(Icons.mail_outline), ), readOnly: true, validator: (v) => (v == null || v.trim().isEmpty) ? 'Required' : null, ), const SizedBox(height: 12), TextFormField( controller: _managerPhoneCtrl, decoration: const InputDecoration( labelText: 'Manager phone (optional)', prefixIcon: Icon(Icons.phone_outlined), ), keyboardType: TextInputType.phone, ), const SizedBox(height: 24), Row( children: [ Expanded( child: Text( 'ROSTER', style: theme.textTheme.labelLarge?.copyWith( letterSpacing: 1.5, ), ), ), OutlinedButton.icon( onPressed: _addPlayerRow, icon: const Icon(Icons.add, size: 18), label: const Text('Add player'), ), ], ), const SizedBox(height: 8), if (_roster.isEmpty) Padding( padding: const EdgeInsets.symmetric(vertical: 16), child: Text( 'No players yet — tap "Add player" to start your roster.', style: theme.textTheme.bodySmall?.copyWith( color: theme.colorScheme.onSurfaceVariant, ), ), ) else ..._roster.map( (draft) => _PlayerRow( draft: draft, onRemove: () => _removePlayerRow(draft), ), ), const SizedBox(height: 24), FilledButton.icon( onPressed: _submitting ? null : _submit, icon: _submitting ? const SizedBox( width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2), ) : const Icon(Icons.add_circle_outline), label: const Text('CREATE TEAM'), ), ], ), ), ), ); } } class _PlayerDraft { _PlayerDraft() : nameCtrl = TextEditingController(), jerseyCtrl = TextEditingController(), positionCtrl = TextEditingController(); final TextEditingController nameCtrl; final TextEditingController jerseyCtrl; final TextEditingController positionCtrl; void dispose() { nameCtrl.dispose(); jerseyCtrl.dispose(); positionCtrl.dispose(); } } class _PlayerRow extends StatelessWidget { const _PlayerRow({required this.draft, required this.onRemove}); final _PlayerDraft draft; final VoidCallback onRemove; @override Widget build(BuildContext context) { final theme = Theme.of(context); return Card( margin: const EdgeInsets.symmetric(vertical: 6), child: Padding( padding: const EdgeInsets.fromLTRB(12, 8, 4, 8), child: Column( children: [ Row( children: [ Expanded( child: TextFormField( controller: draft.nameCtrl, decoration: const InputDecoration(labelText: 'Player name'), validator: (v) => (v == null || v.trim().isEmpty) ? 'Required' : null, ), ), IconButton( icon: Icon( Icons.remove_circle_outline, color: theme.colorScheme.error, ), tooltip: 'Remove player', onPressed: onRemove, ), ], ), const SizedBox(height: 8), Row( children: [ SizedBox( width: 88, child: TextFormField( controller: draft.jerseyCtrl, decoration: const InputDecoration(labelText: 'Jersey #'), keyboardType: TextInputType.number, inputFormatters: [ FilteringTextInputFormatter.digitsOnly, ], ), ), const SizedBox(width: 12), Expanded( child: TextFormField( controller: draft.positionCtrl, decoration: const InputDecoration(labelText: 'Position'), ), ), ], ), ], ), ), ); } }