import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../../models/bible_verse.dart'; import '../../models/family_member.dart'; import '../../models/photo.dart'; import '../../models/announcement.dart'; import '../../models/schedule_item.dart'; import '../../services/bible_verse_service.dart'; import '../../services/family_service.dart'; import '../../services/photo_service.dart'; import '../../services/announcement_service.dart'; import '../../services/schedule_service.dart'; class AdminScreen extends StatefulWidget { const AdminScreen({super.key}); @override State createState() => _AdminScreenState(); } class _AdminScreenState extends State { @override Widget build(BuildContext context) { return DefaultTabController( length: 5, child: Scaffold( appBar: AppBar( title: const Text('Admin Settings'), bottom: const TabBar( isScrollable: true, tabs: [ Tab(text: 'Family Members'), Tab(text: 'Photos'), Tab(text: 'Bible Verses'), Tab(text: 'Announcements'), Tab(text: 'Schedules'), ], ), ), body: const TabBarView( children: [ FamilyManagerTab(), PhotoManagerTab(), BibleVerseManagerTab(), AnnouncementManagerTab(), ScheduleManagerTab(), ], ), ), ); } } class FamilyManagerTab extends StatefulWidget { const FamilyManagerTab({super.key}); @override State createState() => _FamilyManagerTabState(); } class _FamilyManagerTabState extends State { @override Widget build(BuildContext context) { return Scaffold( floatingActionButton: FloatingActionButton( onPressed: () => _showAddMemberDialog(context), child: const Icon(Icons.add), ), body: FutureBuilder>( future: Provider.of(context).fetchFamilyMembers(), builder: (context, snapshot) { if (!snapshot.hasData) return const Center(child: CircularProgressIndicator()); final members = snapshot.data!; return ListView.builder( itemCount: members.length, itemBuilder: (context, index) { final member = members[index]; Color memberColor; try { memberColor = Color( int.parse(member.color.replaceAll('#', '0xFF')), ); } catch (_) { memberColor = Colors.grey; } return ListTile( leading: CircleAvatar( backgroundColor: memberColor, child: Text( member.emoji, style: const TextStyle(fontSize: 20), ), ), title: Text(member.name), subtitle: Text('Order: ${member.order}'), trailing: IconButton( icon: const Icon(Icons.delete, color: Colors.red), onPressed: () async { await Provider.of( context, listen: false, ).deleteFamilyMember(member.id); setState(() {}); }, ), ); }, ); }, ), ); } void _showAddMemberDialog(BuildContext context) { final nameController = TextEditingController(); final emojiController = TextEditingController(text: '👤'); final colorController = TextEditingController(text: '0xFFFFD700'); final orderController = TextEditingController(text: '1'); showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Add Family Member'), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ TextField( controller: nameController, decoration: const InputDecoration(labelText: 'Name'), ), TextField( controller: emojiController, decoration: const InputDecoration(labelText: 'Emoji'), ), TextField( controller: colorController, decoration: const InputDecoration( labelText: 'Color (Hex 0xAARRGGBB)', ), ), TextField( controller: orderController, decoration: const InputDecoration(labelText: 'Order'), keyboardType: TextInputType.number, ), ], ), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Cancel'), ), TextButton( onPressed: () async { if (nameController.text.isNotEmpty) { await Provider.of( context, listen: false, ).createFamilyMember( FamilyMember( id: '', name: nameController.text, emoji: emojiController.text, color: colorController.text.replaceFirst( '0xFF', '#', ), // Simple conversion order: int.tryParse(orderController.text) ?? 1, ), ); if (mounted) { Navigator.pop(context); setState(() {}); } } }, child: const Text('Add'), ), ], ), ); } } class PhotoManagerTab extends StatefulWidget { const PhotoManagerTab({super.key}); @override State createState() => _PhotoManagerTabState(); } class _PhotoManagerTabState extends State { @override Widget build(BuildContext context) { return Scaffold( floatingActionButton: FloatingActionButton( onPressed: () => _showAddPhotoDialog(context), child: const Icon(Icons.add_a_photo), ), body: FutureBuilder>( future: Provider.of(context).fetchPhotos(), builder: (context, snapshot) { if (!snapshot.hasData) return const Center(child: CircularProgressIndicator()); final photos = snapshot.data!; return GridView.builder( padding: const EdgeInsets.all(8), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, crossAxisSpacing: 8, mainAxisSpacing: 8, ), itemCount: photos.length, itemBuilder: (context, index) { final photo = photos[index]; return GridTile( footer: GridTileBar( backgroundColor: Colors.black54, title: Text(photo.caption), trailing: IconButton( icon: const Icon(Icons.delete, color: Colors.white), onPressed: () async { await Provider.of( context, listen: false, ).deletePhoto(photo.id); setState(() {}); }, ), ), child: Image.network( photo.url, fit: BoxFit.cover, errorBuilder: (_, __, ___) => const Center(child: Icon(Icons.broken_image)), ), ); }, ); }, ), ); } void _showAddPhotoDialog(BuildContext context) { final urlController = TextEditingController(); final captionController = TextEditingController(); showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Add Photo URL'), content: Column( mainAxisSize: MainAxisSize.min, children: [ TextField( controller: urlController, decoration: const InputDecoration(labelText: 'Image URL'), ), TextField( controller: captionController, decoration: const InputDecoration(labelText: 'Caption'), ), ], ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Cancel'), ), TextButton( onPressed: () async { if (urlController.text.isNotEmpty) { await Provider.of( context, listen: false, ).createPhoto( Photo( id: '', url: urlController.text, caption: captionController.text, active: true, ), ); if (mounted) { Navigator.pop(context); setState(() {}); } } }, child: const Text('Add'), ), ], ), ); } } class BibleVerseManagerTab extends StatefulWidget { const BibleVerseManagerTab({super.key}); @override State createState() => _BibleVerseManagerTabState(); } class _BibleVerseManagerTabState extends State { @override Widget build(BuildContext context) { return Scaffold( floatingActionButton: FloatingActionButton( onPressed: () => _showAddVerseDialog(context), child: const Icon(Icons.menu_book), ), body: FutureBuilder>( future: Provider.of(context).fetchVerses(), builder: (context, snapshot) { if (!snapshot.hasData) { return const Center(child: CircularProgressIndicator()); } final verses = snapshot.data!; if (verses.isEmpty) { return const Center( child: Text( 'No verses added yet', style: TextStyle(color: Colors.grey), ), ); } return ListView.builder( itemCount: verses.length, itemBuilder: (context, index) { final verse = verses[index]; return ListTile( title: Text(verse.reference), subtitle: Text( verse.text, maxLines: 1, overflow: TextOverflow.ellipsis, ), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ if (verse.date != null && verse.date!.isNotEmpty) Text( verse.date!, style: const TextStyle(fontSize: 12, color: Colors.grey), ), IconButton( icon: const Icon(Icons.delete, color: Colors.red), onPressed: () async { await Provider.of( context, listen: false, ).deleteVerse(verse.id); setState(() {}); }, ), ], ), ); }, ); }, ), ); } void _showAddVerseDialog(BuildContext context) { final textController = TextEditingController(); final referenceController = TextEditingController(); final dateController = TextEditingController(); bool isActive = true; showDialog( context: context, builder: (context) => StatefulBuilder( builder: (context, setDialogState) => AlertDialog( title: const Text('Add Bible Verse'), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ TextField( controller: referenceController, decoration: const InputDecoration( labelText: 'Reference (e.g., Psalms 23:1)', ), ), const SizedBox(height: 8), TextField( controller: textController, decoration: const InputDecoration( labelText: 'Verse Text (Korean)', ), maxLines: 3, ), const SizedBox(height: 8), TextField( controller: dateController, decoration: const InputDecoration( labelText: 'Date (YYYY-MM-DD) - Optional', hintText: '2024-01-01', ), ), const SizedBox(height: 8), SwitchListTile( title: const Text('Active'), value: isActive, onChanged: (value) { setDialogState(() { isActive = value; }); }, ), ], ), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Cancel'), ), TextButton( onPressed: () async { if (textController.text.isNotEmpty && referenceController.text.isNotEmpty) { await Provider.of( context, listen: false, ).createVerse( BibleVerse( id: '', text: textController.text, reference: referenceController.text, date: dateController.text.isEmpty ? null : dateController.text, active: isActive, ), ); if (mounted) { Navigator.pop(context); setState(() {}); } } }, child: const Text('Add'), ), ], ), ), ); } } class AnnouncementManagerTab extends StatefulWidget { const AnnouncementManagerTab({super.key}); @override State createState() => _AnnouncementManagerTabState(); } class _AnnouncementManagerTabState extends State { @override Widget build(BuildContext context) { return Scaffold( floatingActionButton: FloatingActionButton( onPressed: () => _showAddAnnouncementDialog(context), child: const Icon(Icons.campaign), ), body: FutureBuilder>( future: Provider.of(context).fetchAnnouncements(), builder: (context, snapshot) { if (!snapshot.hasData) { return const Center(child: CircularProgressIndicator()); } final announcements = snapshot.data!; if (announcements.isEmpty) { return const Center( child: Text( 'No announcements added yet', style: TextStyle(color: Colors.grey), ), ); } return ListView.builder( itemCount: announcements.length, itemBuilder: (context, index) { final announcement = announcements[index]; return ListTile( title: Text(announcement.title), subtitle: Text( announcement.content, maxLines: 1, overflow: TextOverflow.ellipsis, ), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ if (announcement.priority > 0) Container( padding: const EdgeInsets.symmetric( horizontal: 6, vertical: 2), decoration: BoxDecoration( color: Colors.orangeAccent, borderRadius: BorderRadius.circular(4), ), child: Text( 'P${announcement.priority}', style: const TextStyle( color: Colors.white, fontSize: 10), ), ), const SizedBox(width: 8), Icon( announcement.active ? Icons.check_circle : Icons.cancel, color: announcement.active ? Colors.green : Colors.grey, size: 16, ), IconButton( icon: const Icon(Icons.delete, color: Colors.red), onPressed: () async { await Provider.of( context, listen: false, ).deleteAnnouncement(announcement.id); setState(() {}); }, ), ], ), ); }, ); }, ), ); } void _showAddAnnouncementDialog(BuildContext context) { final titleController = TextEditingController(); final contentController = TextEditingController(); final priorityController = TextEditingController(text: '0'); bool isActive = true; showDialog( context: context, builder: (context) => StatefulBuilder( builder: (context, setDialogState) => AlertDialog( title: const Text('Add Announcement'), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ TextField( controller: titleController, decoration: const InputDecoration(labelText: 'Title'), ), TextField( controller: contentController, decoration: const InputDecoration(labelText: 'Content'), maxLines: 3, ), TextField( controller: priorityController, decoration: const InputDecoration(labelText: 'Priority (0-10)'), keyboardType: TextInputType.number, ), SwitchListTile( title: const Text('Active'), value: isActive, onChanged: (value) { setDialogState(() { isActive = value; }); }, ), ], ), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Cancel'), ), TextButton( onPressed: () async { if (titleController.text.isNotEmpty) { await Provider.of( context, listen: false, ).createAnnouncement( Announcement( id: '', title: titleController.text, content: contentController.text, priority: int.tryParse(priorityController.text) ?? 0, active: isActive, ), ); if (mounted) { Navigator.pop(context); setState(() {}); } } }, child: const Text('Add'), ), ], ), ), ); } } class ScheduleManagerTab extends StatefulWidget { const ScheduleManagerTab({super.key}); @override State createState() => _ScheduleManagerTabState(); } class _ScheduleManagerTabState extends State { @override Widget build(BuildContext context) { return Scaffold( floatingActionButton: FloatingActionButton( onPressed: () => _showAddScheduleDialog(context), child: const Icon(Icons.calendar_today), ), body: FutureBuilder>( future: Provider.of(context).fetchSchedules(), builder: (context, snapshot) { if (!snapshot.hasData) { return const Center(child: CircularProgressIndicator()); } final schedules = snapshot.data!; if (schedules.isEmpty) { return const Center( child: Text( 'No schedules added yet', style: TextStyle(color: Colors.grey), ), ); } return ListView.builder( itemCount: schedules.length, itemBuilder: (context, index) { final schedule = schedules[index]; return ListTile( title: Text(schedule.title), subtitle: Text( '${schedule.description}\n${schedule.startDate.toIso8601String().split('T')[0]} ~ ${schedule.endDate.toIso8601String().split('T')[0]}', maxLines: 2, overflow: TextOverflow.ellipsis, ), isThreeLine: true, trailing: IconButton( icon: const Icon(Icons.delete, color: Colors.red), onPressed: () async { await Provider.of( context, listen: false, ).deleteSchedule(schedule.id); setState(() {}); }, ), ); }, ); }, ), ); } void _showAddScheduleDialog(BuildContext context) { final titleController = TextEditingController(); final descriptionController = TextEditingController(); final startController = TextEditingController( text: DateTime.now().toIso8601String().split('T')[0], ); final endController = TextEditingController( text: DateTime.now().toIso8601String().split('T')[0], ); bool isAllDay = true; String? selectedFamilyMemberId; showDialog( context: context, builder: (context) => StatefulBuilder( builder: (context, setDialogState) => AlertDialog( title: const Text('Add Schedule'), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, children: [ TextField( controller: titleController, decoration: const InputDecoration(labelText: 'Title'), ), TextField( controller: descriptionController, decoration: const InputDecoration(labelText: 'Description'), ), TextField( controller: startController, decoration: const InputDecoration( labelText: 'Start Date (YYYY-MM-DD)', ), ), TextField( controller: endController, decoration: const InputDecoration( labelText: 'End Date (YYYY-MM-DD)', ), ), SwitchListTile( title: const Text('All Day'), value: isAllDay, onChanged: (value) { setDialogState(() { isAllDay = value; }); }, ), FutureBuilder>( future: Provider.of(context, listen: false) .fetchFamilyMembers(), builder: (context, snapshot) { if (!snapshot.hasData) return const SizedBox(); final members = snapshot.data!; return DropdownButtonFormField( value: selectedFamilyMemberId, decoration: const InputDecoration( labelText: 'Family Member', ), items: members.map((member) { return DropdownMenuItem( value: member.id, child: Text(member.name), ); }).toList(), onChanged: (value) { setDialogState(() { selectedFamilyMemberId = value; }); }, ); }, ), ], ), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Cancel'), ), TextButton( onPressed: () async { if (titleController.text.isNotEmpty) { final startDate = DateTime.tryParse(startController.text) ?? DateTime.now(); final endDate = DateTime.tryParse(endController.text) ?? DateTime.now(); await Provider.of( context, listen: false, ).createSchedule( ScheduleItem( id: '', title: titleController.text, description: descriptionController.text, startDate: startDate, endDate: endDate, familyMemberId: selectedFamilyMemberId ?? '', isAllDay: isAllDay, ), ); if (mounted) { Navigator.pop(context); setState(() {}); } } }, child: const Text('Add'), ), ], ), ), ); } }