import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:lanebow/models/event_model.dart'; import 'package:lanebow/models/score_model.dart'; import 'package:lanebow/models/participant_model.dart'; import 'package:lanebow/models/member_model.dart'; import 'package:lanebow/utils/tie_breaker_util.dart'; import 'package:lanebow/models/tie_breaker_option.dart'; /// 테스트용 이벤트 상세 화면 위젯 /// 실제 EventDetailsScreen의 핵심 기능만 구현하여 테스트에 사용 class TestEventDetailsScreen extends StatefulWidget { final Event event; final List scores; final List participants; const TestEventDetailsScreen({ Key? key, required this.event, this.scores = const [], this.participants = const [], }) : super(key: key); @override State createState() => _TestEventDetailsScreenState(); } class _TestEventDetailsScreenState extends State { bool _includeHandicap = false; List _members = []; // 점수에 따른 색상 반환 Color _getScoreColor(int score) { if (score == 10) { return Colors.red.shade700; // 스트라이크 } else if (score >= 7) { return Colors.orange.shade700; // 좋은 점수 } else if (score >= 5) { return Colors.green.shade700; // 평균 점수 } else { return Colors.blue.shade700; // 낮은 점수 } } // 프레임 표시 문자열 반환 String _getFrameDisplay(int frameIndex, int score, List frames) { // 스트라이크 표시 if (score == 10) { return 'X'; } // 스페어 표시 (이전 프레임과 합쳐서 10점이면 스페어) if (frameIndex > 0 && frames[frameIndex - 1] + score == 10) { return '/'; } // 일반 점수 표시 return score.toString(); } @override void initState() { super.initState(); // 테스트용 멤버 데이터 초기화 _members = widget.scores.map((score) { return Member( id: score.memberId, clubId: score.clubId, name: score.participantName ?? '알 수 없음', birthDate: DateTime(1990, 1, 1), // 테스트용 기본 생년월일 userId: 'test_user_${score.memberId}', email: 'test_${score.memberId}@example.com', isActive: true); }).toList(); } // 점수 탭 위젯 구현 Widget _buildScoresTab() { if (widget.scores.isEmpty) { return const Center(child: Text('등록된 점수가 없습니다')); } // TieBreakerUtil을 사용하여 점수 정렬 및 순위 계산 final tieBreakerOptions = [ TieBreakerOption.lowerHandicap, // 핸디캡이 적은 사람 우선 TieBreakerOption.olderAge, // 연장자 우선 TieBreakerOption.lowerScoreGap // 점수 격차가 적은 사람 우선 ]; final sortedScores = TieBreakerUtil.sortScoresByOptions( widget.scores, tieBreakerOptions, _members, _includeHandicap ); // 순위 계산 final ranks = TieBreakerUtil.calculateRanks(sortedScores, _includeHandicap); return Column( children: [ // 핸디캡 포함 여부 토글 Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text('핸디캡 포함:', style: TextStyle(fontWeight: FontWeight.bold)), Switch( value: _includeHandicap, onChanged: (value) { setState(() { _includeHandicap = value; }); }, ), ], ), ), Expanded( child: ListView.builder( itemCount: sortedScores.length, itemBuilder: (context, index) { final score = sortedScores[index]; final rank = ranks[score.id] ?? (index + 1); final participant = widget.participants.firstWhere( (p) => p.memberId == score.memberId, orElse: () => Participant(id: 'unknown_${score.memberId}', eventId: widget.event.id, memberId: score.memberId, name: '알 수 없음'), ); return Card( margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( width: 24, height: 24, decoration: BoxDecoration( color: Colors.blue.shade700, shape: BoxShape.circle, ), child: Center( child: Text( '$rank', style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 14, ), ), ), ), const SizedBox(width: 8), Text( participant.name ?? '알 수 없음', style: const TextStyle(fontWeight: FontWeight.bold), ), const Spacer(), Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: Colors.blue.shade100, borderRadius: BorderRadius.circular(12), ), child: Text( _includeHandicap ? '총점: ${score.totalScore + (score.handicap ?? 0)} (${score.totalScore}+${score.handicap ?? 0})' : '총점: ${score.totalScore}', style: const TextStyle(fontWeight: FontWeight.bold), ), ), ], ), const SizedBox(height: 4), SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: score.frames.asMap().entries.map((entry) { return Container( width: 30, height: 30, margin: const EdgeInsets.only(right: 4), decoration: BoxDecoration( color: _getScoreColor(entry.value), borderRadius: BorderRadius.circular(4), ), child: Center( child: Text( _getFrameDisplay(entry.key, entry.value, score.frames), style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, ), ), ), ); }).toList(), ), ), ], ), ), ); }, ), ), ], ); } // 참가자 탭 위젯 구현 Widget _buildParticipantsTab() { if (widget.participants.isEmpty) { return const Center(child: Text('등록된 참가자가 없습니다')); } return ListView.builder( itemCount: widget.participants.length, itemBuilder: (context, index) { final participant = widget.participants[index]; return ListTile( title: Text(participant.name ?? '알 수 없음'), subtitle: Text('회원 ID: ${participant.memberId}'), trailing: const Icon(Icons.chevron_right), ); }, ); } // 팀 탭 위젯 구현 Widget _buildTeamsTab() { // 팀 생성 옵션 상태 관리 final teamSizeController = TextEditingController(text: '3'); final teamCountController = TextEditingController(text: '2'); int selectedOption = 0; // 0: 팀 인원수로 나누기, 1: 팀 개수로 나누기 return StatefulBuilder( builder: (context, setState) { return Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 팀 생성 옵션 카드 Card( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '팀 생성 옵션', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 16), // 팀 인원수로 나누기 옵션 Row( children: [ Radio( value: 0, groupValue: selectedOption, onChanged: (value) { setState(() { selectedOption = value!; }); }, ), const Text('팀 인원수로 나누기'), ], ), // 팀 개수로 나누기 옵션 Row( children: [ Radio( value: 1, groupValue: selectedOption, onChanged: (value) { setState(() { selectedOption = value!; }); }, ), const Text('팀 개수로 나누기'), ], ), const SizedBox(height: 16), // 선택된 옵션에 따른 입력 필드 if (selectedOption == 0) ...[ // 팀 인원수로 나누기 Row( children: [ const Text('인원수:'), const SizedBox(width: 8), SizedBox( width: 60, child: TextField( controller: teamSizeController, keyboardType: TextInputType.number, decoration: const InputDecoration( border: OutlineInputBorder(), contentPadding: EdgeInsets.symmetric(horizontal: 8, vertical: 8), ), ), ), ], ), ] else ...[ // 팀 개수로 나누기 Row( children: [ const Text('팀 수:'), const SizedBox(width: 8), SizedBox( width: 60, child: TextField( controller: teamCountController, keyboardType: TextInputType.number, decoration: const InputDecoration( border: OutlineInputBorder(), contentPadding: EdgeInsets.symmetric(horizontal: 8, vertical: 8), ), ), ), ], ), ], const SizedBox(height: 8), // 참가자 수 표시 Text('총 ${widget.participants.length}명 참가자'), const SizedBox(height: 16), // 팀 생성 버튼 ElevatedButton( onPressed: () { // 실제 팀 생성 로직은 생략하고 스낵바만 표시 ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('팀이 생성되었습니다'), duration: Duration(seconds: 2), ), ); }, child: const Text('팀 생성하기'), ), ], ), ), ), const SizedBox(height: 16), // 팀 목록 표시 (테스트용 더미 데이터) const Text( '팀 목록', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 8), Expanded( child: ListView( children: [ // 팀 A 카드 Card( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const Text( '팀 A', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), const Spacer(), const Text('팀 에버리지: 180'), ], ), const SizedBox(height: 8), const Text('팀원: 참가자 1, 참가자 2, 참가자 3'), ], ), ), ), // 미배정 참가자 섹션 Card( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '미배정 참가자', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), const SizedBox(height: 8), const Text('참가자 4'), const Text('참가자 5'), const Text('참가자 6'), ], ), ), ), ], ), ), ], ), ); }, ); } @override Widget build(BuildContext context) { return DefaultTabController( length: 4, child: Scaffold( appBar: AppBar( title: Text(widget.event.title), bottom: const TabBar( tabs: [ Tab(text: '기본 정보'), Tab(text: '참가자'), Tab(text: '점수'), Tab(text: '팀'), ], ), ), body: TabBarView( children: [ // 기본 정보 탭 SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 이벤트 유형 및 상태 Row( children: [ Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: Colors.blue, borderRadius: BorderRadius.circular(16), ), child: Text( widget.event.type ?? '기타', style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold), ), ), const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: Colors.green, borderRadius: BorderRadius.circular(16), ), child: Text( widget.event.status ?? '상태 없음', style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold), ), ), ], ), const SizedBox(height: 16), // 공개 URL Container( width: double.infinity, padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.blue.withOpacity(0.05), borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.blue.withOpacity(0.3)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const Icon(Icons.link, size: 18, color: Colors.blue), const SizedBox(width: 8), const Text( '공개 URL', style: TextStyle(fontWeight: FontWeight.bold), ), const Spacer(), ElevatedButton.icon( onPressed: () { final publicUrl = 'https://bowling.example.com/events/${widget.event.publicHash}'; Clipboard.setData(ClipboardData(text: publicUrl)); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: const Text('공개 URL이 클립보드에 복사되었습니다'), action: SnackBarAction( label: '확인', onPressed: () {}, ), duration: const Duration(seconds: 2), ), ); }, icon: const Icon(Icons.copy, size: 16), label: const Text('복사'), style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 0), minimumSize: const Size(0, 32), ), ), ], ), const SizedBox(height: 8), Text( 'https://lanebow.app/events/${widget.event.publicHash}', style: const TextStyle(color: Colors.blue), ), ], ), ), const SizedBox(height: 12), // 접근 비밀번호 if (widget.event.accessPassword != null && widget.event.accessPassword!.isNotEmpty) ...[ Container( width: double.infinity, padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.orange.withOpacity(0.05), borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.orange.withOpacity(0.3)), ), child: Row( children: [ const Icon(Icons.lock, size: 18, color: Colors.orange), const SizedBox(width: 8), const Text( '접근 비밀번호: ', style: TextStyle(fontWeight: FontWeight.bold), ), Text( widget.event.accessPassword ?? '비밀번호 없음', ), const Spacer(), IconButton( icon: const Icon(Icons.copy, size: 18), onPressed: () { Clipboard.setData(ClipboardData(text: widget.event.accessPassword!)); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: const Text('비밀번호가 클립보드에 복사되었습니다'), action: SnackBarAction( label: '확인', onPressed: () {}, ), duration: const Duration(seconds: 2), ), ); }, tooltip: '비밀번호 복사', color: Colors.orange, padding: EdgeInsets.zero, constraints: const BoxConstraints(), ), ], ), ), ], // 공유 버튼 const SizedBox(height: 16), ElevatedButton.icon( onPressed: () { showModalBottomSheet( context: context, builder: (context) => Column( mainAxisSize: MainAxisSize.min, children: [ Padding( padding: const EdgeInsets.all(16.0), child: Row( children: [ const Text( '이벤트 공유', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), const Spacer(), IconButton( icon: const Icon(Icons.close), onPressed: () => Navigator.pop(context), ), ], ), ), const Divider(height: 1), ListTile( title: const Text('기본 정보만 공유'), onTap: () { Navigator.pop(context); }, ), ListTile( title: const Text('참가 링크 포함 공유'), onTap: () { Navigator.pop(context); }, ), ListTile( title: const Text('상세 정보 포함 공유'), onTap: () { Navigator.pop(context); }, ), const SizedBox(height: 16), ], ), ); }, icon: const Icon(Icons.share), label: const Text('이벤트 공유'), style: ElevatedButton.styleFrom( backgroundColor: Colors.green, foregroundColor: Colors.white, ), ), ], ), ), // 참가자 탭 _buildParticipantsTab(), // 점수 탭 _buildScoresTab(), // 팀 탭 _buildTeamsTab(), ], ), ), ); } }