Files

464 lines
14 KiB
Dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../../services/admin_service.dart';
import '../../services/subscription_service.dart';
import '../../models/subscription_model.dart';
import '../../widgets/dialog_actions.dart';
/// 관리자 대시보드 화면
class AdminDashboardScreen extends StatefulWidget {
const AdminDashboardScreen({super.key});
@override
State<AdminDashboardScreen> createState() => _AdminDashboardScreenState();
}
class _AdminDashboardScreenState extends State<AdminDashboardScreen> {
Map<String, dynamic>? _analyticsData;
bool _isLoading = false;
@override
void initState() {
super.initState();
_loadData();
}
/// 데이터 로드
Future<void> _loadData() async {
setState(() {
_isLoading = true;
});
try {
final adminService = Provider.of<AdminService>(context, listen: false);
final analyticsData = await adminService.fetchClubAnalytics();
if (!mounted) return;
setState(() {
_analyticsData = analyticsData;
});
} catch (e) {
final messenger = ScaffoldMessenger.of(context);
messenger.showSnackBar(
SnackBar(content: Text('데이터 로드 중 오류가 발생했습니다: $e')),
);
} finally {
if (mounted) {
setState(() {
_isLoading = false;
});
}
}
}
@override
Widget build(BuildContext context) {
final adminService = Provider.of<AdminService>(context);
final subscriptionService = Provider.of<SubscriptionService>(context);
return Scaffold(
appBar: AppBar(
title: const Text('관리자 대시보드'),
actions: [
IconButton(
icon: const Icon(Icons.refresh),
onPressed: _loadData,
),
],
),
body: _isLoading
? const Center(child: CircularProgressIndicator())
: RefreshIndicator(
onRefresh: _loadData,
child: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildPermissionsCard(adminService),
const SizedBox(height: 16),
_buildSubscriptionCard(subscriptionService),
const SizedBox(height: 16),
_buildAnalyticsCard(),
const SizedBox(height: 16),
_buildQuickActionsCard(context, adminService),
],
),
),
),
);
}
/// 권한 정보 카드
Widget _buildPermissionsCard(AdminService adminService) {
return Card(
elevation: 2,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Icon(Icons.admin_panel_settings),
const SizedBox(width: 8),
Text(
'관리자 권한',
style: Theme.of(context).textTheme.titleLarge,
),
],
),
const Divider(),
const SizedBox(height: 8),
_buildPermissionItem(
'회원 관리',
adminService.canManageMembers,
Icons.people,
),
_buildPermissionItem(
'이벤트 관리',
adminService.canManageEvents,
Icons.event,
),
_buildPermissionItem(
'점수 관리',
adminService.canManageScores,
Icons.score,
),
_buildPermissionItem(
'설정 관리',
adminService.canManageSettings,
Icons.settings,
),
_buildPermissionItem(
'구독 관리',
adminService.canManageSubscription,
Icons.subscriptions,
),
_buildPermissionItem(
'역할 관리',
adminService.canManageRoles,
Icons.admin_panel_settings,
),
_buildPermissionItem(
'재무 정보 조회',
adminService.canViewFinancials,
Icons.attach_money,
),
_buildPermissionItem(
'데이터 내보내기',
adminService.canExportData,
Icons.file_download,
),
],
),
),
);
}
/// 권한 항목 위젯
Widget _buildPermissionItem(String title, bool hasPermission, IconData icon) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: Row(
children: [
Icon(
icon,
size: 18,
color: hasPermission ? Colors.green : Colors.grey,
),
const SizedBox(width: 8),
Text(title),
const Spacer(),
Icon(
hasPermission ? Icons.check_circle : Icons.cancel,
color: hasPermission ? Colors.green : Colors.red,
size: 18,
),
],
),
);
}
/// 구독 정보 카드
Widget _buildSubscriptionCard(SubscriptionService subscriptionService) {
final subscription = subscriptionService.currentSubscription;
return Card(
elevation: 2,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Icon(Icons.subscriptions),
const SizedBox(width: 8),
Text(
'구독 정보',
style: Theme.of(context).textTheme.titleLarge,
),
],
),
const Divider(),
const SizedBox(height: 8),
if (subscription != null) ...[
_buildInfoRow('플랜', subscription.planName),
_buildInfoRow('상태', _getStatusText(subscription.status)),
_buildInfoRow('시작일', _formatDate(subscription.startDate)),
_buildInfoRow('만료일', _formatDate(subscription.endDate)),
_buildInfoRow('자동 갱신', subscription.autoRenew ? '활성화' : '비활성화'),
_buildInfoRow('남은 일수', '${subscription.daysLeft}일'),
] else
const Text('활성화된 구독이 없습니다.'),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () {
Navigator.of(context).pushNamed('/subscription/manage');
},
child: const Text('구독 관리'),
),
],
),
),
);
}
/// 분석 데이터 카드
Widget _buildAnalyticsCard() {
return Card(
elevation: 2,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Icon(Icons.analytics),
const SizedBox(width: 8),
Text(
'클럽 통계',
style: Theme.of(context).textTheme.titleLarge,
),
],
),
const Divider(),
const SizedBox(height: 8),
if (_analyticsData != null) ...[
_buildInfoRow('총 회원 수', '${_analyticsData!['totalMembers'] ?? 0}명'),
_buildInfoRow('활성 회원 수', '${_analyticsData!['activeMembers'] ?? 0}명'),
_buildInfoRow('이번 달 신규 회원', '${_analyticsData!['newMembersThisMonth'] ?? 0}명'),
_buildInfoRow('총 이벤트 수', '${_analyticsData!['totalEvents'] ?? 0}개'),
_buildInfoRow('이번 달 이벤트', '${_analyticsData!['eventsThisMonth'] ?? 0}개'),
_buildInfoRow('총 게임 수', '${_analyticsData!['totalGames'] ?? 0}게임'),
_buildInfoRow('평균 점수', '${_analyticsData!['averageScore'] ?? 0}점'),
] else
const Text('통계 데이터를 불러올 수 없습니다.'),
],
),
),
);
}
/// 빠른 작업 카드
Widget _buildQuickActionsCard(BuildContext context, AdminService adminService) {
return Card(
elevation: 2,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Icon(Icons.flash_on),
const SizedBox(width: 8),
Text(
'빠른 작업',
style: Theme.of(context).textTheme.titleLarge,
),
],
),
const Divider(),
const SizedBox(height: 8),
Wrap(
spacing: 8.0,
runSpacing: 8.0,
children: [
if (adminService.canManageMembers)
_buildActionButton(
context,
'회원 관리',
Icons.people,
() => Navigator.of(context).pushNamed('/admin/members'),
),
if (adminService.canManageEvents)
_buildActionButton(
context,
'이벤트 관리',
Icons.event,
() => Navigator.of(context).pushNamed('/admin/events'),
),
if (adminService.canManageSettings)
_buildActionButton(
context,
'클럽 설정',
Icons.settings,
() => Navigator.of(context).pushNamed('/admin/settings'),
),
if (adminService.canManageRoles)
_buildActionButton(
context,
'역할 관리',
Icons.admin_panel_settings,
() => Navigator.of(context).pushNamed('/admin/roles'),
),
if (adminService.canExportData)
_buildActionButton(
context,
'데이터 내보내기',
Icons.file_download,
() => _showExportDialog(context),
),
],
),
],
),
),
);
}
/// 작업 버튼 위젯
Widget _buildActionButton(
BuildContext context,
String label,
IconData icon,
VoidCallback onPressed,
) {
return ElevatedButton.icon(
onPressed: onPressed,
icon: Icon(icon, size: 18),
label: Text(label),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
),
);
}
/// 데이터 내보내기 다이얼로그
void _showExportDialog(BuildContext context) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('데이터 내보내기'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
leading: const Icon(Icons.people),
title: const Text('회원 데이터'),
onTap: () => _exportData(context, 'members'),
),
ListTile(
leading: const Icon(Icons.event),
title: const Text('이벤트 데이터'),
onTap: () => _exportData(context, 'events'),
),
ListTile(
leading: const Icon(Icons.score),
title: const Text('점수 데이터'),
onTap: () => _exportData(context, 'scores'),
),
ListTile(
leading: const Icon(Icons.analytics),
title: const Text('통계 데이터'),
onTap: () => _exportData(context, 'analytics'),
),
],
),
actions: DialogActions.confirm(
onCancel: () => Navigator.of(context).pop(),
onConfirm: () => Navigator.of(context).pop(),
confirmText: '닫기',
),
),
);
}
/// 데이터 내보내기 처리
Future<void> _exportData(BuildContext context, String dataType) async {
final navigator = Navigator.of(context);
final messenger = ScaffoldMessenger.of(context);
navigator.pop(); // 다이얼로그 닫기
setState(() {
_isLoading = true;
});
try {
final adminService = Provider.of<AdminService>(context, listen: false);
final downloadUrl = await adminService.exportClubData(dataType);
if (downloadUrl != null) {
messenger.showSnackBar(
SnackBar(content: Text('데이터 내보내기 성공: $downloadUrl')),
);
} else {
messenger.showSnackBar(
const SnackBar(content: Text('데이터 내보내기에 실패했습니다.')),
);
}
} catch (e) {
messenger.showSnackBar(
SnackBar(content: Text('데이터 내보내기 중 오류가 발생했습니다: $e')),
);
} finally {
if (mounted) {
setState(() {
_isLoading = false;
});
}
}
}
/// 정보 행 위젯
Widget _buildInfoRow(String label, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'$label:',
style: const TextStyle(fontWeight: FontWeight.bold),
),
Text(value),
],
),
);
}
/// 날짜 포맷팅
String _formatDate(DateTime date) {
return '${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}';
}
/// 구독 상태 텍스트
String _getStatusText(SubscriptionStatus status) {
switch (status) {
case SubscriptionStatus.active:
return '활성화';
case SubscriptionStatus.canceled:
return '취소됨';
case SubscriptionStatus.expired:
return '만료됨';
case SubscriptionStatus.pending:
return '대기 중';
case SubscriptionStatus.failed:
return '실패';
}
}
}