import 'package:flutter/material.dart'; import '../../services/notification_service.dart'; class InvitationsPage extends StatefulWidget { @override _InvitationsPageState createState() => _InvitationsPageState(); } class _InvitationsPageState extends State { bool _isAccepted = false; bool _isLoading = false; Future _acceptCoffeeInvite() async { setState(() { _isLoading = true; }); try { final success = await NotificationService() .sendCoffeeInviteAcceptedNotification(); if (success) { setState(() { _isAccepted = true; _isLoading = false; }); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( 'Coffee invite accepted! Notification sent to everyone.', ), backgroundColor: Colors.green, duration: Duration(seconds: 3), ), ); } else { setState(() { _isLoading = false; }); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( 'Failed to send notification. Check Firebase service account configuration.', ), backgroundColor: Colors.orange, duration: Duration(seconds: 3), ), ); } } catch (e) { setState(() { _isLoading = false; }); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Error: $e'), backgroundColor: Colors.red, duration: Duration(seconds: 3), ), ); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text( 'Invitations', style: TextStyle(fontWeight: FontWeight.w600), ), backgroundColor: Colors.white, foregroundColor: Color(0xFF6A4C93), elevation: 0, bottom: PreferredSize( preferredSize: Size.fromHeight(1), child: Container(height: 1, color: Colors.grey[200]), ), automaticallyImplyLeading: false, ), body: Padding( padding: EdgeInsets.all(16), child: Column( children: [ // Coffee Invitation Card Container( margin: EdgeInsets.only(bottom: 16), padding: EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.08), blurRadius: 10, offset: Offset(0, 2), ), ], border: Border.all( color: Colors.grey.withOpacity(0.2), width: 1, ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header with icon and title Row( children: [ Container( padding: EdgeInsets.all(8), decoration: BoxDecoration( color: Color(0xFF6A4C93).withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: Icon( Icons.coffee, color: Color(0xFF6A4C93), size: 24, ), ), SizedBox(width: 12), Expanded( child: Text( 'Who\'s down for coffee?', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w600, color: Colors.black87, ), ), ), ], ), SizedBox(height: 16), // Description Text( 'Quick coffee break at the campus café. Let\'s catch up!', style: TextStyle( fontSize: 14, color: Colors.grey[700], height: 1.4, ), ), SizedBox(height: 16), // Status indicator Row( children: [ Container( padding: EdgeInsets.symmetric( horizontal: 8, vertical: 4, ), decoration: BoxDecoration( color: _isAccepted ? Colors.green.withOpacity(0.1) : Colors.orange.withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Text( _isAccepted ? 'Accepted ✓' : '1 more person needed', style: TextStyle( fontSize: 12, fontWeight: FontWeight.w500, color: _isAccepted ? Colors.green[700] : Colors.orange[700], ), ), ), Spacer(), Text( '10 min ago', style: TextStyle(fontSize: 12, color: Colors.grey[500]), ), ], ), SizedBox(height: 16), // Accept button SizedBox( width: double.infinity, height: 44, child: ElevatedButton( onPressed: _isAccepted ? null : (_isLoading ? null : _acceptCoffeeInvite), style: ElevatedButton.styleFrom( backgroundColor: _isAccepted ? Colors.grey[300] : Color(0xFF6A4C93), foregroundColor: _isAccepted ? Colors.grey[600] : Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), elevation: _isAccepted ? 0 : 2, ), child: _isLoading ? SizedBox( width: 20, height: 20, child: CircularProgressIndicator( valueColor: AlwaysStoppedAnimation( Colors.white, ), strokeWidth: 2, ), ) : Text( _isAccepted ? 'Accepted' : 'Accept Invite', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, ), ), ), ), ], ), ), // Info text if (!_isAccepted) Container( padding: EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.blue.withOpacity(0.05), borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.blue.withOpacity(0.2)), ), child: Row( children: [ Icon(Icons.info_outline, color: Colors.blue[600], size: 20), SizedBox(width: 8), Expanded( child: Text( 'This is a test invitation :)', style: TextStyle(fontSize: 13, color: Colors.blue[700]), ), ), ], ), ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: () { Navigator.push( context, MaterialPageRoute(builder: (context) => CreateInvitationPage()), ); }, backgroundColor: Color(0xFF6A4C93), child: Icon(Icons.add, color: Colors.white), ), ); } } class CreateInvitationPage extends StatefulWidget { @override _CreateInvitationPageState createState() => _CreateInvitationPageState(); } class _CreateInvitationPageState extends State { final _formKey = GlobalKey(); final _titleController = TextEditingController(); final _descriptionController = TextEditingController(); final _locationController = TextEditingController(); final _maxParticipantsController = TextEditingController(); DateTime? _selectedDate; TimeOfDay? _selectedTime; int? _selectedTagIndex; final List> _availableTags = [ { "name": "Sports", "color_hex": "#FF6B35", "icon_name": "sports_soccer" }, { "name": "Food", "color_hex": "#F7931E", "icon_name": "restaurant" }, { "name": "Gaming", "color_hex": "#FFD23F", "icon_name": "games" }, { "name": "Study", "color_hex": "#06FFA5", "icon_name": "menu_book" }, { "name": "Social", "color_hex": "#118AB2", "icon_name": "group" }, { "name": "Travel", "color_hex": "#06D6A0", "icon_name": "flight" }, { "name": "Music", "color_hex": "#8E44AD", "icon_name": "music_note" }, { "name": "Movies", "color_hex": "#E74C3C", "icon_name": "movie" } ]; @override void dispose() { _titleController.dispose(); _descriptionController.dispose(); _locationController.dispose(); _maxParticipantsController.dispose(); super.dispose(); } IconData _getIconFromName(String iconName) { switch (iconName) { case 'sports_soccer': return Icons.sports_soccer; case 'restaurant': return Icons.restaurant; case 'games': return Icons.games; case 'menu_book': return Icons.menu_book; case 'group': return Icons.group; case 'flight': return Icons.flight; case 'music_note': return Icons.music_note; case 'movie': return Icons.movie; default: return Icons.category; } } Color _getColorFromHex(String hexColor) { return Color(int.parse(hexColor.substring(1, 7), radix: 16) + 0xFF000000); } Future _selectDate() async { final DateTime? picked = await showDatePicker( context: context, initialDate: DateTime.now(), firstDate: DateTime.now(), lastDate: DateTime.now().add(Duration(days: 365)), builder: (context, child) { return Theme( data: Theme.of(context).copyWith( colorScheme: ColorScheme.light( primary: Color(0xFF6A4C93), ), ), child: child!, ); }, ); if (picked != null && picked != _selectedDate) { setState(() { _selectedDate = picked; }); } } Future _selectTime() async { final TimeOfDay? picked = await showTimePicker( context: context, initialTime: TimeOfDay.now(), builder: (context, child) { return Theme( data: Theme.of(context).copyWith( colorScheme: ColorScheme.light( primary: Color(0xFF6A4C93), ), ), child: child!, ); }, ); if (picked != null && picked != _selectedTime) { setState(() { _selectedTime = picked; }); } } void _handleSubmit() { if (_formKey.currentState!.validate()) { if (_selectedTagIndex == null) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Please select a tag for your invitation'), backgroundColor: Colors.red, ), ); return; } final invitationData = { "title": _titleController.text.trim(), "description": _descriptionController.text.trim(), "date": _selectedDate?.toIso8601String(), "time": _selectedTime != null ? "${_selectedTime!.hour}:${_selectedTime!.minute.toString().padLeft(2, '0')}" : null, "location": _locationController.text.trim().isEmpty ? null : _locationController.text.trim(), "max_participants": int.parse(_maxParticipantsController.text), "tag": _availableTags[_selectedTagIndex!], }; print("Invitation JSON: $invitationData"); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Invitation created! Check console for JSON output.'), backgroundColor: Color(0xFF6A4C93), ), ); } } @override Widget build(BuildContext context) { return Scaffold( body: Container( decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [Color(0xFF32B0A5), Color(0xFF4600B9)], stops: [0.0, 0.5], ), ), child: SafeArea( child: Column( children: [ Padding( padding: EdgeInsets.all(16), child: Row( children: [ IconButton( onPressed: () => Navigator.of(context).pop(), icon: Icon(Icons.arrow_back, color: Colors.white), ), Text( 'Create Invitation', style: TextStyle( fontSize: 20, fontWeight: FontWeight.w600, color: Colors.white, ), ), ], ), ), Expanded( child: Container( margin: EdgeInsets.all(16), padding: EdgeInsets.all(24), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), blurRadius: 20, offset: Offset(0, 5), ), ], ), child: Form( key: _formKey, child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text( 'Create New Invitation', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: Colors.black87, ), textAlign: TextAlign.center, ), SizedBox(height: 24), TextFormField( controller: _titleController, decoration: InputDecoration( labelText: 'Title *', prefixIcon: Icon(Icons.title), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Color(0xFF6A4C93)), ), ), validator: (value) { if (value == null || value.trim().isEmpty) { return 'Please enter a title'; } return null; }, ), SizedBox(height: 16), TextFormField( controller: _descriptionController, maxLines: 3, decoration: InputDecoration( labelText: 'Description *', prefixIcon: Icon(Icons.description), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Color(0xFF6A4C93)), ), alignLabelWithHint: true, ), validator: (value) { if (value == null || value.trim().isEmpty) { return 'Please enter a description'; } return null; }, ), SizedBox(height: 16), Row( children: [ Expanded( child: InkWell( onTap: _selectDate, child: Container( padding: EdgeInsets.symmetric(vertical: 16, horizontal: 12), decoration: BoxDecoration( border: Border.all(color: Colors.grey[400]!), borderRadius: BorderRadius.circular(12), ), child: Row( children: [ Icon(Icons.calendar_today, color: Colors.grey[600]), SizedBox(width: 12), Text( _selectedDate == null ? 'Select Date (Optional)' : '${_selectedDate!.day}/${_selectedDate!.month}/${_selectedDate!.year}', style: TextStyle( color: _selectedDate == null ? Colors.grey[600] : Colors.black87, fontSize: 16, ), ), ], ), ), ), ), SizedBox(width: 12), Expanded( child: InkWell( onTap: _selectTime, child: Container( padding: EdgeInsets.symmetric(vertical: 16, horizontal: 12), decoration: BoxDecoration( border: Border.all(color: Colors.grey[400]!), borderRadius: BorderRadius.circular(12), ), child: Row( children: [ Icon(Icons.access_time, color: Colors.grey[600]), SizedBox(width: 12), Text( _selectedTime == null ? 'Select Time (Optional)' : '${_selectedTime!.hour}:${_selectedTime!.minute.toString().padLeft(2, '0')}', style: TextStyle( color: _selectedTime == null ? Colors.grey[600] : Colors.black87, fontSize: 16, ), ), ], ), ), ), ), ], ), SizedBox(height: 16), TextFormField( controller: _locationController, decoration: InputDecoration( labelText: 'Location (Optional)', prefixIcon: Icon(Icons.location_on), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Color(0xFF6A4C93)), ), ), ), SizedBox(height: 16), TextFormField( controller: _maxParticipantsController, keyboardType: TextInputType.number, decoration: InputDecoration( labelText: 'Maximum Participants *', prefixIcon: Icon(Icons.people), border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Color(0xFF6A4C93)), ), ), validator: (value) { if (value == null || value.trim().isEmpty) { return 'Please enter maximum participants'; } final intValue = int.tryParse(value); if (intValue == null || intValue < 1) { return 'Please enter a valid number greater than 0'; } return null; }, ), SizedBox(height: 20), Text( 'Select Tag *', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: Colors.black87, ), ), SizedBox(height: 12), Wrap( spacing: 8, runSpacing: 8, children: List.generate(_availableTags.length, (index) { final tag = _availableTags[index]; final isSelected = _selectedTagIndex == index; return GestureDetector( onTap: () { setState(() { _selectedTagIndex = index; }); }, child: Container( padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: isSelected ? _getColorFromHex(tag['color_hex']) : Colors.grey[100], borderRadius: BorderRadius.circular(20), border: Border.all( color: isSelected ? _getColorFromHex(tag['color_hex']) : Colors.grey[300]!, width: 2, ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( _getIconFromName(tag['icon_name']), size: 18, color: isSelected ? Colors.white : _getColorFromHex(tag['color_hex']), ), SizedBox(width: 6), Text( tag['name'], style: TextStyle( color: isSelected ? Colors.white : Colors.black87, fontWeight: FontWeight.w500, ), ), ], ), ), ); }), ), SizedBox(height: 32), Container( height: 56, child: ElevatedButton( onPressed: _handleSubmit, style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF6A4C93), foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), elevation: 2, ), child: Text( 'Create Invitation', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w600, ), ), ), ), ], ), ), ), ), ), ], ), ), ), ); } }