import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:go_router/go_router.dart'; import 'package:lucide_icons/lucide_icons.dart'; import 'package:intl/intl.dart'; import '../../../app/theme/app_colors.dart'; import '../../../core/widgets/progress_bar.dart'; import '../../../data/local/hive_boxes.dart'; import '../../../data/local/models/app_settings.dart'; import '../../../data/local/models/daily_worship_log.dart'; import '../../../data/local/models/shalat_log.dart'; import '../../../data/local/models/tilawah_log.dart'; import '../../../data/local/models/dzikir_log.dart'; import '../../../data/local/models/puasa_log.dart'; class ChecklistScreen extends ConsumerStatefulWidget { const ChecklistScreen({super.key}); @override ConsumerState createState() => _ChecklistScreenState(); } class _ChecklistScreenState extends ConsumerState { late String _todayKey; late Box _logBox; late Box _settingsBox; late AppSettings _settings; final List _fardhuPrayers = ['Subuh', 'Dzuhur', 'Ashar', 'Maghrib', 'Isya']; @override void initState() { super.initState(); _todayKey = DateFormat('yyyy-MM-dd').format(DateTime.now()); _logBox = Hive.box(HiveBoxes.worshipLogs); _settingsBox = Hive.box(HiveBoxes.settings); _settings = _settingsBox.get('default') ?? AppSettings(); _ensureLogExists(); } void _ensureLogExists() { if (!_logBox.containsKey(_todayKey)) { final shalatLogs = {}; for (final p in _fardhuPrayers) { shalatLogs[p.toLowerCase()] = ShalatLog(); } _logBox.put( _todayKey, DailyWorshipLog( date: _todayKey, shalatLogs: shalatLogs, tilawahLog: TilawahLog( targetValue: _settings.tilawahTargetValue, targetUnit: _settings.tilawahTargetUnit, autoSync: _settings.tilawahAutoSync, ), dzikirLog: _settings.trackDzikir ? DzikirLog() : null, puasaLog: _settings.trackPuasa ? PuasaLog() : null, ), ); } } DailyWorshipLog get _todayLog => _logBox.get(_todayKey)!; void _recalculateProgress() { final log = _todayLog; // Lazily attach Dzikir and Puasa if user toggles them mid-day if (_settings.trackDzikir && log.dzikirLog == null) log.dzikirLog = DzikirLog(); if (_settings.trackPuasa && log.puasaLog == null) log.puasaLog = PuasaLog(); int total = 0; int completed = 0; // Shalat for (final p in _fardhuPrayers) { final pKey = p.toLowerCase(); final sLog = log.shalatLogs[pKey]; if (sLog != null) { total++; if (sLog.completed) completed++; if (hasQabliyah(pKey, _settings.rawatibLevel)) { total++; if (sLog.qabliyah == true) completed++; } if (hasBadiyah(pKey, _settings.rawatibLevel)) { total++; if (sLog.badiyah == true) completed++; } } } // Tilawah if (log.tilawahLog != null) { total++; if (log.tilawahLog!.isCompleted) completed++; } // Dzikir if (_settings.trackDzikir && log.dzikirLog != null) { total += 2; if (log.dzikirLog!.pagi) completed++; if (log.dzikirLog!.petang) completed++; } // Puasa if (_settings.trackPuasa && log.puasaLog != null) { total++; if (log.puasaLog!.completed) completed++; } log.totalItems = total; log.completedCount = completed; log.completionPercent = total > 0 ? completed / total : 0.0; log.save(); setState(() {}); } bool hasQabliyah(String prayer, int level) { if (level == 0) return false; if (prayer == 'subuh') return true; if (prayer == 'dzuhur') return true; if (prayer == 'ashar') return level == 2; // Ghairu Muakkad if (prayer == 'maghrib') return level == 2; // Ghairu Muakkad if (prayer == 'isya') return level == 2; // Ghairu Muakkad return false; } bool hasBadiyah(String prayer, int level) { if (level == 0) return false; if (prayer == 'subuh') return false; if (prayer == 'dzuhur') return true; if (prayer == 'ashar') return false; if (prayer == 'maghrib') return true; if (prayer == 'isya') return true; return false; } @override Widget build(BuildContext context) { final theme = Theme.of(context); final isDark = theme.brightness == Brightness.dark; final log = _todayLog; return Scaffold( appBar: AppBar( title: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('Ibadah Harian'), Text( DateFormat('EEEE, d MMM yyyy').format(DateTime.now()), style: theme.textTheme.bodySmall?.copyWith( color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight, ), ), ], ), centerTitle: false, actions: [ IconButton( onPressed: () {}, icon: const Icon(LucideIcons.bell), ), IconButton( onPressed: () => context.push('/settings'), icon: const Icon(LucideIcons.settings), ), const SizedBox(width: 8), ], ), body: ListView( padding: const EdgeInsets.symmetric(horizontal: 16), children: [ const SizedBox(height: 12), if (!_settings.simpleMode) ...[ _buildProgressCard(log, isDark), const SizedBox(height: 24), ], _sectionLabel('SHOLAT FARDHU & RAWATIB'), const SizedBox(height: 12), ..._fardhuPrayers.map((p) => _buildShalatCard(p, isDark)).toList(), const SizedBox(height: 24), _sectionLabel('TILAWAH AL-QURAN'), const SizedBox(height: 12), _buildTilawahCard(isDark), if (_settings.trackDzikir || _settings.trackPuasa) ...[ const SizedBox(height: 24), _sectionLabel('AMALAN TAMBAHAN'), const SizedBox(height: 12), ], if (_settings.trackDzikir) _buildDzikirCard(isDark), if (_settings.trackPuasa) _buildPuasaCard(isDark), const SizedBox(height: 32), ], ), ); } Widget _sectionLabel(String text) { return Text( text, style: const TextStyle( fontSize: 11, fontWeight: FontWeight.w700, letterSpacing: 1.5, color: AppColors.sage, ), ); } Widget _buildProgressCard(DailyWorshipLog log, bool isDark) { final percent = log.completionPercent; final remaining = log.totalItems - log.completedCount; return Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: isDark ? AppColors.surfaceDark : const Color(0xFF2B3441), borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.1), blurRadius: 16, offset: const Offset(0, 8), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( "POIN HARI INI", style: TextStyle( fontSize: 10, fontWeight: FontWeight.w700, letterSpacing: 1.5, color: AppColors.primary.withValues(alpha: 0.8), ), ), Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), decoration: BoxDecoration( color: AppColors.primary.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(12), ), child: Row( children: [ const Icon(LucideIcons.star, color: AppColors.primary, size: 14), const SizedBox(width: 4), Text( '${log.totalPoints} pts', style: const TextStyle( fontSize: 13, fontWeight: FontWeight.w700, color: AppColors.primary, ), ), ], ), ), ], ), const SizedBox(height: 12), Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( '${(percent * 100).round()}%', style: const TextStyle( fontSize: 42, fontWeight: FontWeight.w800, color: Colors.white, height: 1.1, ), ), const SizedBox(width: 8), const Padding( padding: EdgeInsets.only(bottom: 8), child: Text( 'Selesai', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w400, color: Colors.white70, ), ), ), ], ), const SizedBox(height: 16), AppProgressBar( value: percent, height: 8, backgroundColor: Colors.white.withValues(alpha: 0.15), fillColor: AppColors.primary, ), const SizedBox(height: 12), Text( remaining == 0 && log.totalItems > 0 ? 'MasyaAllah! Poin maksimal tercapai hari ini! 🎉' : 'Kumpulkan poin lebih banyak dengan Sholat di Masjid dan amalan sunnah lainnya!', style: TextStyle( fontSize: 13, color: Colors.white.withValues(alpha: 0.7), ), ), ], ), ); } Widget _buildShalatCard(String prayerName, bool isDark) { final pKey = prayerName.toLowerCase(); final log = _todayLog.shalatLogs[pKey]; if (log == null) return const SizedBox.shrink(); final hasQab = hasQabliyah(pKey, _settings.rawatibLevel); final hasBad = hasBadiyah(pKey, _settings.rawatibLevel); final isCompleted = log.completed; return Container( margin: const EdgeInsets.only(bottom: 12), decoration: BoxDecoration( color: isDark ? AppColors.surfaceDark : AppColors.surfaceLight, borderRadius: BorderRadius.circular(16), border: Border.all( color: isCompleted ? AppColors.primary.withValues(alpha: 0.3) : (isDark ? AppColors.primary.withValues(alpha: 0.08) : AppColors.cream), ), ), child: Theme( data: Theme.of(context).copyWith(dividerColor: Colors.transparent), child: ExpansionTile( tilePadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), leading: Container( width: 44, height: 44, decoration: BoxDecoration( color: isCompleted ? AppColors.primary.withValues(alpha: 0.15) : (isDark ? AppColors.primary.withValues(alpha: 0.08) : AppColors.cream.withValues(alpha: 0.5)), borderRadius: BorderRadius.circular(12), ), child: Icon(LucideIcons.building, size: 22, color: isCompleted ? AppColors.primary : AppColors.sage), ), title: Text( 'Sholat $prayerName', style: TextStyle( fontWeight: FontWeight.w600, fontSize: 16, color: isCompleted && isDark ? AppColors.textSecondaryDark : null, decoration: isCompleted ? TextDecoration.lineThrough : null, ), ), subtitle: log.location != null ? Text('Di ${log.location}', style: const TextStyle(fontSize: 12, color: AppColors.primary)) : null, trailing: _CustomCheckbox( value: isCompleted, onChanged: (v) { log.completed = v ?? false; _recalculateProgress(); }, ), childrenPadding: const EdgeInsets.only(left: 16, right: 16, bottom: 16), children: [ const Divider(), const SizedBox(height: 8), // Location Radio Row( children: [ const Text('Pelaksanaan:', style: TextStyle(fontSize: 13, fontWeight: FontWeight.w600)), const SizedBox(width: 16), _radioOption('Masjid', log, () { log.location = 'Masjid'; log.completed = true; // Auto-check parent _recalculateProgress(); }), const SizedBox(width: 16), _radioOption('Rumah', log, () { log.location = 'Rumah'; log.completed = true; // Auto-check parent _recalculateProgress(); }), ], ), if (hasQab || hasBad) const SizedBox(height: 12), if (hasQab) _sunnahRow('Qabliyah $prayerName', log.qabliyah ?? false, (v) { log.qabliyah = v; _recalculateProgress(); }), if (hasBad) _sunnahRow('Ba\'diyah $prayerName', log.badiyah ?? false, (v) { log.badiyah = v; _recalculateProgress(); }), ], ), ), ); } Widget _radioOption(String title, ShalatLog log, VoidCallback onTap) { final selected = log.location == title; return GestureDetector( onTap: onTap, child: Row( children: [ Icon( selected ? LucideIcons.checkCircle2 : LucideIcons.circle, size: 18, color: selected ? AppColors.primary : Colors.grey, ), const SizedBox(width: 4), Text(title, style: TextStyle(fontSize: 13, color: selected ? AppColors.primary : null)), ], ), ); } Widget _sunnahRow(String title, bool value, ValueChanged onChanged) { return Padding( padding: const EdgeInsets.only(top: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(title, style: const TextStyle(fontSize: 14)), _CustomCheckbox(value: value, onChanged: onChanged), ], ), ); } Widget _buildTilawahCard(bool isDark) { final log = _todayLog.tilawahLog; if (log == null) return const SizedBox.shrink(); return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: isDark ? AppColors.surfaceDark : AppColors.surfaceLight, borderRadius: BorderRadius.circular(16), border: Border.all( color: log.isCompleted ? AppColors.primary.withValues(alpha: 0.3) : (isDark ? AppColors.primary.withValues(alpha: 0.08) : AppColors.cream), ), ), child: Column( children: [ // ── Row 1: Target + Checkbox ── Row( children: [ Container( width: 44, height: 44, decoration: BoxDecoration( color: log.isCompleted ? AppColors.primary.withValues(alpha: 0.15) : (isDark ? AppColors.primary.withValues(alpha: 0.08) : AppColors.cream.withValues(alpha: 0.5)), borderRadius: BorderRadius.circular(12), ), child: Icon(LucideIcons.bookOpen, size: 22, color: log.isCompleted ? AppColors.primary : AppColors.sage), ), const SizedBox(width: 14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Tilawah', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: log.isCompleted && isDark ? AppColors.textSecondaryDark : null, decoration: log.isCompleted ? TextDecoration.lineThrough : null, ), ), Text( 'Target: ${log.targetValue} ${log.targetUnit}', style: const TextStyle(fontSize: 12, color: AppColors.primary), ), ], ), ), _CustomCheckbox( value: log.targetCompleted, onChanged: (v) { log.targetCompleted = v ?? false; _recalculateProgress(); }, ), ], ), const SizedBox(height: 12), const Divider(height: 1), const SizedBox(height: 12), // ── Row 2: Ayat Tracker ── Row( children: [ Icon(LucideIcons.bookOpen, size: 18, color: AppColors.sage), const SizedBox(width: 8), Expanded( child: Text( 'Sudah Baca: ${log.rawAyatRead} Ayat', style: TextStyle( fontSize: 13, fontWeight: FontWeight.w600, color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight, ), ), ), if (log.autoSync) Tooltip( message: 'Sinkron dari Al-Quran', child: Icon(LucideIcons.refreshCw, size: 16, color: AppColors.primary), ), IconButton( icon: const Icon(LucideIcons.minusCircle, size: 20), visualDensity: VisualDensity.compact, onPressed: log.rawAyatRead > 0 ? () { log.rawAyatRead--; _recalculateProgress(); } : null, ), IconButton( icon: const Icon(LucideIcons.plusCircle, size: 20, color: AppColors.primary), visualDensity: VisualDensity.compact, onPressed: () { log.rawAyatRead++; _recalculateProgress(); }, ), ], ), ], ), ); } Widget _buildDzikirCard(bool isDark) { final log = _todayLog.dzikirLog; if (log == null) return const SizedBox.shrink(); return Container( margin: const EdgeInsets.only(bottom: 12), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: isDark ? AppColors.surfaceDark : AppColors.surfaceLight, borderRadius: BorderRadius.circular(16), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(LucideIcons.sparkles, size: 20, color: AppColors.sage), const SizedBox(width: 8), const Text('Dzikir Harian', style: TextStyle(fontWeight: FontWeight.w600, fontSize: 15)), ], ), const SizedBox(height: 12), _sunnahRow('Dzikir Pagi', log.pagi, (v) { log.pagi = v ?? false; _recalculateProgress(); }), _sunnahRow('Dzikir Petang', log.petang, (v) { log.petang = v ?? false; _recalculateProgress(); }), ], ), ); } Widget _buildPuasaCard(bool isDark) { final log = _todayLog.puasaLog; if (log == null) return const SizedBox.shrink(); return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: isDark ? AppColors.surfaceDark : AppColors.surfaceLight, borderRadius: BorderRadius.circular(16), ), child: Row( children: [ const Icon(LucideIcons.moonStar, size: 20, color: AppColors.sage), const SizedBox(width: 8), const Expanded(child: Text('Puasa Sunnah', style: TextStyle(fontWeight: FontWeight.w600, fontSize: 15))), DropdownButton( value: log.jenisPuasa, hint: const Text('Jenis', style: TextStyle(fontSize: 12)), underline: const SizedBox(), items: ['Senin', 'Kamis', 'Ayyamul Bidh', 'Daud', 'Lainnya'] .map((e) => DropdownMenuItem(value: e, child: Text(e, style: const TextStyle(fontSize: 13)))) .toList(), onChanged: (v) { log.jenisPuasa = v; _recalculateProgress(); }, ), const SizedBox(width: 8), _CustomCheckbox( value: log.completed, onChanged: (v) { log.completed = v ?? false; _recalculateProgress(); }, ), ], ), ); } } class _CustomCheckbox extends StatelessWidget { final bool value; final ValueChanged onChanged; const _CustomCheckbox({required this.value, required this.onChanged}); @override Widget build(BuildContext context) { return GestureDetector( onTap: () => onChanged(!value), child: Container( width: 24, height: 24, decoration: BoxDecoration( color: value ? AppColors.primary : Colors.transparent, borderRadius: BorderRadius.circular(6), border: value ? null : Border.all(color: Colors.grey, width: 2), ), child: value ? const Icon(LucideIcons.check, size: 16, color: Colors.white) : null, ), ); } }