wesal/frontend/lib/screens/pages/invitation_details_page.dart

550 lines
24 KiB
Dart

import 'package:flutter/material.dart';
import '../../models/invitation_models.dart';
import '../../services/invitations_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<InvitationDetailsPage> {
InvitationDetails? _invitationDetails;
bool _isLoading = true;
bool _isCancelling = false;
String? _errorMessage;
@override
void initState() {
super.initState();
_loadInvitationDetails();
}
Future<void> _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'];
}
});
}
}
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<void> _cancelInvitation() async {
final confirmed = await showDialog<bool>(
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,
),
);
}
}
}
}
@override
Widget build(BuildContext context) {
return 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],
),
),
],
),
),
],
),
);
}),
],
),
),
],
if (widget.isParticipant) ...[
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<Color>(Colors.white),
),
)
: Text(
widget.isOwner ? 'Cancel Invitation' : 'Leave Invitation',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
),
),
],
SizedBox(height: 32),
],
),
),
);
}
}