import 'package:flutter/material.dart'; import '../../models/invitation_models.dart'; import '../../services/invitations_service.dart'; import '../../services/user_service.dart'; import '../../utils/invitation_utils.dart'; class InvitationDetailsPage extends StatefulWidget { final int invitationId; final bool isOwner; final bool isParticipant; const InvitationDetailsPage({ Key? key, required this.invitationId, required this.isOwner, this.isParticipant = true, }) : super(key: key); @override _InvitationDetailsPageState createState() => _InvitationDetailsPageState(); } class _InvitationDetailsPageState extends State { InvitationDetails? _invitationDetails; bool _isLoading = true; bool _isCancelling = false; bool _isAccepting = false; bool _isCurrentlyParticipant = false; String? _errorMessage; @override void initState() { super.initState(); _isCurrentlyParticipant = widget.isParticipant; _loadInvitationDetails(); } Future _loadInvitationDetails() async { setState(() { _isLoading = true; _errorMessage = null; }); final result = await InvitationsService.getInvitationDetails(widget.invitationId); if (mounted) { setState(() { _isLoading = false; if (result['success']) { _invitationDetails = result['data']; } else { _errorMessage = result['message']; } }); // Update participation status after loading details if (result['success']) { await _updateParticipationStatus(); } } } Widget _buildAvatarOrInitial(String displayName, String? avatar) { return Container( width: 40, height: 40, decoration: BoxDecoration( color: Color(0xFF6A4C93).withOpacity(0.1), borderRadius: BorderRadius.circular(20), ), child: avatar != null ? ClipRRect( borderRadius: BorderRadius.circular(20), child: Image.network( avatar, width: 40, height: 40, fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { return Center( child: Text( displayName.isNotEmpty ? displayName[0].toUpperCase() : '?', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xFF6A4C93), ), ), ); }, ), ) : Center( child: Text( displayName.isNotEmpty ? displayName[0].toUpperCase() : '?', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xFF6A4C93), ), ), ), ); } Future _cancelInvitation() async { final confirmed = await showDialog( context: context, builder: (context) => AlertDialog( title: Text(widget.isOwner ? 'Cancel Invitation' : 'Leave Invitation'), content: Text( widget.isOwner ? 'Are you sure you want to cancel this invitation? This action cannot be undone.' : 'Are you sure you want to leave this invitation?', ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(false), child: Text('No'), ), TextButton( onPressed: () => Navigator.of(context).pop(true), style: TextButton.styleFrom(foregroundColor: Colors.red), child: Text('Yes'), ), ], ), ); if (confirmed == true) { setState(() { _isCancelling = true; }); final result = await InvitationsService.cancelInvitation(widget.invitationId); if (mounted) { setState(() { _isCancelling = false; }); if (result['success']) { Navigator.of(context).pop(true); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(result['message'] ?? 'Action completed successfully'), backgroundColor: Colors.green, ), ); } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(result['message'] ?? 'Failed to complete action'), backgroundColor: Colors.red, ), ); } } } } Future _updateParticipationStatus() async { if (_invitationDetails == null) return; final userResult = await UserService.getCurrentUser(); if (userResult['success'] && userResult['data'] != null) { final currentUserId = userResult['data']['id']; final isParticipant = _invitationDetails!.attendees.any((attendee) => attendee.id == currentUserId); setState(() { _isCurrentlyParticipant = isParticipant; }); } } Future _acceptInvitation() async { setState(() { _isAccepting = true; }); final result = await InvitationsService.acceptInvitation(widget.invitationId); if (mounted) { setState(() { _isAccepting = false; }); if (result['success']) { // Reload invitation details to reflect the new state await _loadInvitationDetails(); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Invitation accepted successfully!'), backgroundColor: Colors.green, ), ); } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(result['message'] ?? 'Failed to accept invitation'), backgroundColor: Colors.red, ), ); } } } @override Widget build(BuildContext context) { return PopScope( canPop: true, // Allow back navigation to go back to invitations page only child: Scaffold( backgroundColor: Colors.grey[50], appBar: AppBar( title: Text( 'Invitation Details', 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]), ), ), body: _isLoading ? Center( child: CircularProgressIndicator(color: Color(0xFF6A4C93)), ) : _errorMessage != null ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.error_outline, size: 80, color: Colors.red[400]), SizedBox(height: 16), Text( _errorMessage!, style: TextStyle(fontSize: 16, color: Colors.red[600]), textAlign: TextAlign.center, ), SizedBox(height: 16), ElevatedButton( onPressed: _loadInvitationDetails, style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF6A4C93), foregroundColor: Colors.white, ), child: Text('Retry'), ), ], ), ) : SingleChildScrollView( padding: EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( width: double.infinity, padding: EdgeInsets.all(24), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.08), blurRadius: 10, offset: Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( padding: EdgeInsets.all(16), decoration: BoxDecoration( color: InvitationUtils.getColorFromHex( _invitationDetails!.tag.colorHex, ).withOpacity(0.1), borderRadius: BorderRadius.circular(16), ), child: Icon( InvitationUtils.getIconFromName( _invitationDetails!.tag.iconName, ), color: InvitationUtils.getColorFromHex( _invitationDetails!.tag.colorHex, ), size: 32, ), ), SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( _invitationDetails!.title, style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: Colors.black87, ), ), Container( margin: EdgeInsets.only(top: 8), padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: InvitationUtils.getColorFromHex( _invitationDetails!.tag.colorHex, ).withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Text( _invitationDetails!.tag.name, style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: InvitationUtils.getColorFromHex( _invitationDetails!.tag.colorHex, ), ), ), ), ], ), ), Container( padding: EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: _invitationDetails!.status == 'Available' ? Colors.green.withOpacity(0.1) : Colors.orange.withOpacity(0.1), borderRadius: BorderRadius.circular(12), ), child: Text( _invitationDetails!.status, style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: _invitationDetails!.status == 'Available' ? Colors.green[700] : Colors.orange[700], ), ), ), ], ), if (_invitationDetails!.description != null && _invitationDetails!.description!.isNotEmpty) ...[ SizedBox(height: 20), Text( 'Description', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: Colors.black87, ), ), SizedBox(height: 8), Text( _invitationDetails!.description!, style: TextStyle( fontSize: 14, color: Colors.grey[700], height: 1.5, ), ), ], SizedBox(height: 20), Row( children: [ if (_invitationDetails!.location != null) ...[ Expanded( child: Row( children: [ Icon(Icons.location_on, size: 20, color: Colors.grey[600]), SizedBox(width: 8), Expanded( child: Text( _invitationDetails!.location!, style: TextStyle(fontSize: 14, color: Colors.grey[700]), overflow: TextOverflow.ellipsis, ), ), ], ), ), ], if (_invitationDetails!.dateTime != null) ...[ if (_invitationDetails!.location != null) SizedBox(width: 16), Expanded( child: Row( children: [ Icon(Icons.schedule, size: 20, color: Colors.grey[600]), SizedBox(width: 8), Expanded( child: Text( InvitationUtils.getRelativeDateTime( _invitationDetails!.dateTime!, ), style: TextStyle(fontSize: 14, color: Colors.grey[700]), overflow: TextOverflow.ellipsis, ), ), ], ), ), ], ], ), SizedBox(height: 16), Row( children: [ Icon(Icons.people, size: 20, color: Colors.grey[600]), SizedBox(width: 8), Text( '${_invitationDetails!.currentAttendees} / ${_invitationDetails!.maxParticipants} participants', style: TextStyle(fontSize: 14, color: Colors.grey[700]), ), ], ), ], ), ), SizedBox(height: 24), Container( width: double.infinity, padding: EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.08), blurRadius: 10, offset: Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text( 'Organizer', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Colors.black87, ), ), SizedBox(width: 8), Container( padding: EdgeInsets.symmetric(horizontal: 8, vertical: 2), decoration: BoxDecoration( color: Color(0xFF6A4C93).withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: Text( 'ORGANIZER', style: TextStyle( fontSize: 10, fontWeight: FontWeight.bold, color: Color(0xFF6A4C93), ), ), ), ], ), SizedBox(height: 12), Row( children: [ _buildAvatarOrInitial( _invitationDetails!.creator.displayName, _invitationDetails!.creator.avatar, ), SizedBox(width: 12), Text( _invitationDetails!.creator.displayName, style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: Colors.black87, ), ), ], ), ], ), ), if (_invitationDetails!.attendees.isNotEmpty) ...[ SizedBox(height: 24), Container( width: double.infinity, padding: EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.08), blurRadius: 10, offset: Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Attendees (${_invitationDetails!.attendees.length})', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Colors.black87, ), ), SizedBox(height: 16), ...List.generate(_invitationDetails!.attendees.length, (index) { final attendee = _invitationDetails!.attendees[index]; return Container( margin: EdgeInsets.only(bottom: index < _invitationDetails!.attendees.length - 1 ? 12 : 0), child: Row( children: [ _buildAvatarOrInitial( attendee.displayName, attendee.avatar, ), SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( attendee.displayName, style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: Colors.black87, ), ), Text( 'Joined ${InvitationUtils.getRelativeTime(attendee.joinedAt)}', style: TextStyle( fontSize: 12, color: Colors.grey[600], ), ), ], ), ), ], ), ); }), ], ), ), ], // Accept button for non-participants who are not owners if (!_isCurrentlyParticipant && !widget.isOwner) ...[ SizedBox(height: 32), Container( width: double.infinity, height: 56, child: ElevatedButton( onPressed: _isAccepting ? null : _acceptInvitation, style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF6A4C93), foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), elevation: 2, ), child: _isAccepting ? SizedBox( height: 20, width: 20, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation(Colors.white), ), ) : Text( 'Accept Invitation', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, ), ), ), ), ], // Cancel/Leave button for participants if (_isCurrentlyParticipant) ...[ SizedBox(height: 32), Container( width: double.infinity, height: 56, child: ElevatedButton( onPressed: _isCancelling ? null : _cancelInvitation, style: ElevatedButton.styleFrom( backgroundColor: Colors.red, foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), elevation: 2, ), child: _isCancelling ? SizedBox( height: 20, width: 20, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation(Colors.white), ), ) : Text( widget.isOwner ? 'Cancel Invitation' : 'Leave Invitation', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, ), ), ), ), ], SizedBox(height: 32), ], ), ), ), ); } }