Polish navigation, Quran flows, and sharing UX

This commit is contained in:
Dwindi Ramadhana
2026-03-18 00:07:10 +07:00
parent a049129a35
commit 2d09b5b356
59 changed files with 11835 additions and 3184 deletions

View File

@@ -5,11 +5,10 @@ import 'package:hive_flutter/hive_flutter.dart';
import 'package:intl/intl.dart';
import 'package:lucide_icons/lucide_icons.dart';
import '../../../app/theme/app_colors.dart';
import '../../../core/widgets/progress_bar.dart';
import '../../../core/widgets/notification_bell_button.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/checklist_item.dart';
class LaporanScreen extends ConsumerStatefulWidget {
const LaporanScreen({super.key});
@@ -74,48 +73,60 @@ class _LaporanScreenState extends ConsumerState<LaporanScreen>
final date = now.subtract(Duration(days: i));
final key = DateFormat('yyyy-MM-dd').format(date);
final log = logBox.get(key);
if (log != null && log.totalItems > 0) {
daysChecked++;
// Fardhu
totalCounts['fardhu'] = (totalCounts['fardhu'] ?? 0) + 5;
int completedFardhu = log.shalatLogs.values.where((l) => l.completed).length;
completionCounts['fardhu'] = (completionCounts['fardhu'] ?? 0) + completedFardhu;
int completedFardhu =
log.shalatLogs.values.where((l) => l.completed).length;
completionCounts['fardhu'] =
(completionCounts['fardhu'] ?? 0) + completedFardhu;
// Rawatib
int rawatibTotal = 0;
int rawatibCompleted = 0;
for (var sLog in log.shalatLogs.values) {
if (sLog.qabliyah != null) { rawatibTotal++; if (sLog.qabliyah!) rawatibCompleted++; }
if (sLog.badiyah != null) { rawatibTotal++; if (sLog.badiyah!) rawatibCompleted++; }
if (sLog.qabliyah != null) {
rawatibTotal++;
if (sLog.qabliyah!) rawatibCompleted++;
}
if (sLog.badiyah != null) {
rawatibTotal++;
if (sLog.badiyah!) rawatibCompleted++;
}
}
if (rawatibTotal > 0) {
totalCounts['rawatib'] = (totalCounts['rawatib'] ?? 0) + rawatibTotal;
completionCounts['rawatib'] = (completionCounts['rawatib'] ?? 0) + rawatibCompleted;
totalCounts['rawatib'] = (totalCounts['rawatib'] ?? 0) + rawatibTotal;
completionCounts['rawatib'] =
(completionCounts['rawatib'] ?? 0) + rawatibCompleted;
}
// Tilawah
if (log.tilawahLog != null) {
totalCounts['tilawah'] = (totalCounts['tilawah'] ?? 0) + 1;
if (log.tilawahLog!.isCompleted) {
completionCounts['tilawah'] = (completionCounts['tilawah'] ?? 0) + 1;
}
totalCounts['tilawah'] = (totalCounts['tilawah'] ?? 0) + 1;
if (log.tilawahLog!.isCompleted) {
completionCounts['tilawah'] =
(completionCounts['tilawah'] ?? 0) + 1;
}
}
// Dzikir
if (log.dzikirLog != null) {
totalCounts['dzikir'] = (totalCounts['dzikir'] ?? 0) + 2;
int dCompleted = (log.dzikirLog!.pagi ? 1 : 0) + (log.dzikirLog!.petang ? 1 : 0);
completionCounts['dzikir'] = (completionCounts['dzikir'] ?? 0) + dCompleted;
totalCounts['dzikir'] = (totalCounts['dzikir'] ?? 0) + 2;
int dCompleted =
(log.dzikirLog!.pagi ? 1 : 0) + (log.dzikirLog!.petang ? 1 : 0);
completionCounts['dzikir'] =
(completionCounts['dzikir'] ?? 0) + dCompleted;
}
// Puasa
if (log.puasaLog != null) {
totalCounts['puasa'] = (totalCounts['puasa'] ?? 0) + 1;
if (log.puasaLog!.completed) {
completionCounts['puasa'] = (completionCounts['puasa'] ?? 0) + 1;
}
totalCounts['puasa'] = (totalCounts['puasa'] ?? 0) + 1;
if (log.puasaLog!.completed) {
completionCounts['puasa'] = (completionCounts['puasa'] ?? 0) + 1;
}
}
}
}
@@ -170,7 +181,7 @@ class _LaporanScreenState extends ConsumerState<LaporanScreen>
Widget build(BuildContext context) {
final theme = Theme.of(context);
final isDark = theme.brightness == Brightness.dark;
final settingsBox = Hive.box<AppSettings>(HiveBoxes.settings);
final isSimpleMode = settingsBox.get('default')?.simpleMode ?? false;
@@ -180,10 +191,7 @@ class _LaporanScreenState extends ConsumerState<LaporanScreen>
title: const Text('Riwayat Ibadah'),
centerTitle: false,
actions: [
IconButton(
onPressed: () {},
icon: const Icon(LucideIcons.bell),
),
const NotificationBellButton(),
IconButton(
onPressed: () => context.push('/settings'),
icon: const Icon(LucideIcons.settings),
@@ -204,10 +212,7 @@ class _LaporanScreenState extends ConsumerState<LaporanScreen>
title: const Text('Laporan Kualitas Ibadah'),
centerTitle: false,
actions: [
IconButton(
onPressed: () {},
icon: const Icon(LucideIcons.bell),
),
const NotificationBellButton(),
IconButton(
onPressed: () => context.push('/settings'),
icon: const Icon(LucideIcons.settings),
@@ -253,7 +258,8 @@ class _LaporanScreenState extends ConsumerState<LaporanScreen>
child: TabBarView(
controller: _tabController,
children: [
_buildWeeklyView(context, isDark, weekData, avgPercent, insights),
_buildWeeklyView(
context, isDark, weekData, avgPercent, insights),
_buildComingSoon(context, 'Bulanan'),
_buildComingSoon(context, 'Tahunan'),
],
@@ -332,57 +338,71 @@ class _LaporanScreenState extends ConsumerState<LaporanScreen>
const SizedBox(height: 20),
// ── Bar Chart ──
SizedBox(
height: 140,
child: Builder(
builder: (context) {
final maxPts = weekData.map((d) => d.value).fold<double>(0.0, (a, b) => a > b ? a : b).clamp(50.0, 300.0);
return Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: weekData.map((d) {
final ratio = (d.value / maxPts).clamp(0.05, 1.0);
return Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Flexible(
child: Container(
width: double.infinity,
height: 120 * ratio,
decoration: BoxDecoration(
color: d.isToday
? AppColors.primary
: AppColors.primary
.withValues(alpha: 0.3 + ratio * 0.4),
borderRadius: BorderRadius.circular(6),
),
),
height: 162,
child: Builder(builder: (context) {
final maxPts = weekData
.map((d) => d.value)
.fold<double>(0.0, (a, b) => a > b ? a : b)
.clamp(50.0, 300.0);
return Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: weekData.map((d) {
final ratio = (d.value / maxPts).clamp(0.05, 1.0);
return Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
'${d.value.round()}',
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.w700,
color: d.isToday
? AppColors.primary
: (isDark
? AppColors.textSecondaryDark
: AppColors.textSecondaryLight),
),
const SizedBox(height: 8),
Text(
d.label,
style: TextStyle(
fontSize: 10,
fontWeight: d.isToday
? FontWeight.w700
: FontWeight.w400,
),
const SizedBox(height: 8),
Flexible(
child: Container(
width: double.infinity,
height: 120 * ratio,
decoration: BoxDecoration(
color: d.isToday
? AppColors.primary
: (isDark
? AppColors.textSecondaryDark
: AppColors.textSecondaryLight),
: AppColors.primary.withValues(
alpha: 0.3 + ratio * 0.4),
borderRadius: BorderRadius.circular(6),
),
),
],
),
),
const SizedBox(height: 8),
Text(
d.label,
style: TextStyle(
fontSize: 10,
fontWeight: d.isToday
? FontWeight.w700
: FontWeight.w400,
color: d.isToday
? AppColors.primary
: (isDark
? AppColors.textSecondaryDark
: AppColors.textSecondaryLight),
),
),
],
),
);
}).toList(),
);
}
),
),
);
}).toList(),
);
}),
),
],
),
@@ -587,9 +607,11 @@ class _LaporanScreenState extends ConsumerState<LaporanScreen>
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(LucideIcons.history, size: 64, color: AppColors.sage.withValues(alpha: 0.5)),
Icon(LucideIcons.history,
size: 64, color: AppColors.sage.withValues(alpha: 0.5)),
const SizedBox(height: 16),
const Text('Belum ada riwayat ibadah', style: TextStyle(color: AppColors.sage)),
const Text('Belum ada riwayat ibadah',
style: TextStyle(color: AppColors.sage)),
],
),
);
@@ -602,10 +624,11 @@ class _LaporanScreenState extends ConsumerState<LaporanScreen>
itemBuilder: (context, index) {
final log = logs[index];
final isToday = log.date == DateFormat('yyyy-MM-dd').format(now);
// Build summary text
final List<String> finished = [];
int fardhuCount = log.shalatLogs.values.where((l) => l.completed).length;
int fardhuCount =
log.shalatLogs.values.where((l) => l.completed).length;
if (fardhuCount > 0) finished.add('$fardhuCount Fardhu');
if (log.tilawahLog?.isCompleted == true) finished.add('Tilawah');
if (log.dzikirLog != null) {
@@ -635,7 +658,8 @@ class _LaporanScreenState extends ConsumerState<LaporanScreen>
color: AppColors.primary.withValues(alpha: 0.15),
borderRadius: BorderRadius.circular(12),
),
child: const Icon(LucideIcons.checkCircle2, color: AppColors.primary),
child: const Icon(LucideIcons.checkCircle2,
color: AppColors.primary),
),
const SizedBox(width: 16),
Expanded(
@@ -643,7 +667,10 @@ class _LaporanScreenState extends ConsumerState<LaporanScreen>
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
isToday ? 'Hari Ini' : DateFormat('EEEE, d MMM yyyy').format(DateTime.parse(log.date)),
isToday
? 'Hari Ini'
: DateFormat('EEEE, d MMM yyyy')
.format(DateTime.parse(log.date)),
style: const TextStyle(
fontWeight: FontWeight.w700,
fontSize: 15,
@@ -651,10 +678,14 @@ class _LaporanScreenState extends ConsumerState<LaporanScreen>
),
const SizedBox(height: 4),
Text(
finished.isNotEmpty ? finished.join('') : 'Belum ada aktivitas',
finished.isNotEmpty
? finished.join('')
: 'Belum ada aktivitas',
style: TextStyle(
fontSize: 13,
color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight,
color: isDark
? AppColors.textSecondaryDark
: AppColors.textSecondaryLight,
height: 1.4,
),
),