import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; class RekapanPage extends StatefulWidget { final String token; const RekapanPage({super.key, required this.token}); @override State createState() => _RekapanPageState(); } class _RekapanPageState extends State { List bookings = []; bool isLoading = false; final List namaBulan = [ '', 'Januari', 'Februari', 'Maret', 'April', 'Mei', 'Juni', 'Juli', 'Agustus', 'September', 'Oktober', 'November', 'Desember', ]; final Color primaryColor = const Color(0xFFF06292); final Color backgroundColor = const Color(0xFFFFF6F9); @override void initState() { super.initState(); fetchBookings(); } Future fetchBookings() async { setState(() => isLoading = true); try { final response = await http.get( Uri.parse('http://angeliasalon.my.id/api/bookings'), headers: { 'Authorization': 'Bearer ${widget.token}', 'Accept': 'application/json', }, ); if (response.statusCode == 200) { final data = json.decode(response.body); setState(() { bookings = data; isLoading = false; }); } else { throw Exception('Gagal memuat data booking'); } } catch (e) { setState(() => isLoading = false); _showErrorSnackBar('Error: $e'); } } void _showErrorSnackBar(String message) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(message), backgroundColor: Colors.red.shade600), ); } Map> getYearlyMonthlyRecap() { final now = DateTime.now(); Map> result = {}; for (int month = 1; month <= 12; month++) { final label = '${namaBulan[month]} ${now.year}'; result[label] = {}; } for (var booking in bookings) { // Lewati jika status booking ditolak if (booking['status']?.toLowerCase() == 'ditolak') continue; final tanggalStr = booking['tanggal_booking']; if (tanggalStr == null) continue; DateTime bookingDate; try { bookingDate = DateTime.parse(tanggalStr); } catch (_) { continue; } if (bookingDate.year == now.year) { final label = '${namaBulan[bookingDate.month]} ${bookingDate.year}'; final layanan = booking['service']?['name'] ?? 'Tidak diketahui'; result[label]![layanan] = (result[label]![layanan] ?? 0) + 1; } } return result; } @override Widget build(BuildContext context) { final recapData = getYearlyMonthlyRecap(); return Scaffold( backgroundColor: backgroundColor, appBar: AppBar( title: const Text('Rekapan Booking Tahunan'), backgroundColor: primaryColor, foregroundColor: Colors.white, actions: [ IconButton( onPressed: fetchBookings, icon: const Icon(Icons.refresh), tooltip: 'Refresh', ) ], ), body: isLoading ? const Center(child: CircularProgressIndicator()) : bookings.isEmpty ? const Center(child: Text('Tidak ada data booking')) : RefreshIndicator( onRefresh: fetchBookings, child: ListView.builder( padding: const EdgeInsets.all(16), itemCount: recapData.length, itemBuilder: (context, index) { final key = recapData.keys.elementAt(index); final data = recapData[key]!; return Container( width: double.infinity, margin: const EdgeInsets.only(bottom: 16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.pink.shade100, blurRadius: 6, offset: const Offset(0, 2), ) ], ), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '📅 $key', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: primaryColor, ), ), const SizedBox(height: 12), data.isEmpty ? const Center( child: Padding( padding: EdgeInsets.symmetric(vertical: 16), child: Text( 'Belum ada booking di bulan ini.', style: TextStyle( color: Colors.black54, fontStyle: FontStyle.italic, ), ), ), ) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: data.entries.map((e) { return Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Text( '• ${e.key}: ${e.value} booking', style: const TextStyle( fontSize: 16, color: Colors.black87, ), ), ); }).toList(), ), ], ), ), ); }, ), ), ); } }