import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../app/app_router.dart'; import '../app/app_state.dart'; import '../services/api.dart'; import '../services/auth_storage.dart'; /// Screen to invite additional Payfrit users to join a group order /// Shown after selecting Delivery or Takeaway class GroupOrderInviteScreen extends StatefulWidget { const GroupOrderInviteScreen({super.key}); @override State createState() => _GroupOrderInviteScreenState(); } class _GroupOrderInviteScreenState extends State { final List _invitedUsers = []; final TextEditingController _searchController = TextEditingController(); bool _isSearching = false; int? _currentUserId; List _searchResults = []; @override void initState() { super.initState(); _loadCurrentUserId(); } Future _loadCurrentUserId() async { final auth = await AuthStorage.loadAuth(); if (auth != null && mounted) { setState(() { _currentUserId = auth.userId; }); } } @override void dispose() { _searchController.dispose(); super.dispose(); } Future _searchUsers(String query) async { if (query.isEmpty) { setState(() { _searchResults = []; _isSearching = false; }); return; } if (query.length < 3) { setState(() { _searchResults = []; _isSearching = false; }); return; } setState(() => _isSearching = true); try { final results = await Api.searchUsers( query: query, currentUserId: _currentUserId, ); if (mounted && _searchController.text == query) { // Filter out already invited users final filteredResults = results.where((user) => !_invitedUsers.any((invited) => invited.userId == user.userId)).toList(); setState(() { _searchResults = filteredResults; _isSearching = false; }); } } catch (e) { debugPrint('[GroupOrderInvite] Search error: $e'); if (mounted) { setState(() { _searchResults = []; _isSearching = false; }); } } } void _inviteUser(UserSearchResult user) { if (!_invitedUsers.any((u) => u.userId == user.userId)) { setState(() { _invitedUsers.add(user); _searchResults = []; _searchController.clear(); }); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Invitation sent to ${user.name}', style: const TextStyle(color: Colors.black)), backgroundColor: const Color(0xFF90EE90), behavior: SnackBarBehavior.floating, margin: const EdgeInsets.only(bottom: 80, left: 16, right: 16), ), ); } } void _removeInvite(UserSearchResult user) { setState(() { _invitedUsers.removeWhere((u) => u.userId == user.userId); }); } void _continueToRestaurants() { // Store invited users in app state final appState = context.read(); final invitedUserIds = _invitedUsers.map((u) => u.userId).toList(); appState.setGroupOrderInvites(invitedUserIds); Navigator.of(context).pushReplacementNamed(AppRoutes.restaurantSelect); } void _skipInvites() { Navigator.of(context).pushReplacementNamed(AppRoutes.restaurantSelect); } @override Widget build(BuildContext context) { final appState = context.watch(); final orderTypeLabel = appState.isDelivery ? 'Delivery' : 'Takeaway'; return Scaffold( backgroundColor: Colors.black, appBar: AppBar( backgroundColor: Colors.black, foregroundColor: Colors.white, title: Text('$orderTypeLabel Order'), elevation: 0, ), body: SafeArea( child: Padding( padding: const EdgeInsets.all(24), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Header const Icon( Icons.group_add, color: Colors.white54, size: 48, ), const SizedBox(height: 16), const Text( "Invite others to join", style: TextStyle( color: Colors.white, fontSize: 22, fontWeight: FontWeight.bold, ), textAlign: TextAlign.center, ), const SizedBox(height: 8), Text( "Add Payfrit users to share this order.\n${appState.isDelivery ? 'Split the delivery fee between everyone!' : 'Everyone pays for their own items.'}", style: const TextStyle( color: Colors.white70, fontSize: 14, ), textAlign: TextAlign.center, ), const SizedBox(height: 24), // Search field TextField( controller: _searchController, onChanged: _searchUsers, style: const TextStyle(color: Colors.white), decoration: InputDecoration( hintText: 'Search by phone or email...', hintStyle: const TextStyle(color: Colors.white38), prefixIcon: const Icon(Icons.search, color: Colors.white54), suffixIcon: _isSearching ? const Padding( padding: EdgeInsets.all(12), child: SizedBox( width: 20, height: 20, child: CircularProgressIndicator( strokeWidth: 2, color: Colors.white54, ), ), ) : null, filled: true, fillColor: Colors.grey.shade900, border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide.none, ), ), ), // Search results if (_searchResults.isNotEmpty) ...[ const SizedBox(height: 8), Container( decoration: BoxDecoration( color: Colors.grey.shade900, borderRadius: BorderRadius.circular(12), ), child: Column( children: _searchResults.map((user) { return ListTile( leading: CircleAvatar( backgroundColor: Colors.blue.withAlpha(50), child: Text( user.name[0].toUpperCase(), style: const TextStyle(color: Colors.blue), ), ), title: Text( user.name, style: const TextStyle(color: Colors.white), ), subtitle: Text( user.phone, style: const TextStyle(color: Colors.white54), ), trailing: IconButton( icon: const Icon(Icons.person_add, color: Colors.blue), onPressed: () => _inviteUser(user), ), ); }).toList(), ), ), ], const SizedBox(height: 16), // Invited users list if (_invitedUsers.isNotEmpty) ...[ const Text( 'Invited:', style: TextStyle( color: Colors.white70, fontSize: 14, fontWeight: FontWeight.w500, ), ), const SizedBox(height: 8), Wrap( spacing: 8, runSpacing: 8, children: _invitedUsers.map((user) { return Chip( avatar: CircleAvatar( backgroundColor: Colors.green.withAlpha(50), child: const Icon( Icons.check, size: 16, color: Colors.green, ), ), label: Text(user.name), deleteIcon: const Icon(Icons.close, size: 18), onDeleted: () => _removeInvite(user), backgroundColor: Colors.grey.shade800, labelStyle: const TextStyle(color: Colors.white), ); }).toList(), ), ], const Spacer(), // Continue button if (_invitedUsers.isNotEmpty) FilledButton.icon( onPressed: _continueToRestaurants, icon: const Icon(Icons.group), label: Text('Continue with ${_invitedUsers.length + 1} people'), style: FilledButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), ) else FilledButton( onPressed: _continueToRestaurants, style: FilledButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), child: const Text('Continue Alone'), ), const SizedBox(height: 12), // Skip button TextButton( onPressed: _skipInvites, child: const Text( 'Skip this step', style: TextStyle(color: Colors.white54), ), ), ], ), ), ), ); } }