MIF_E31221388/salonbooking/lib/pemilik/rekapan.dart

196 lines
6.6 KiB
Dart

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<RekapanPage> createState() => _RekapanPageState();
}
class _RekapanPageState extends State<RekapanPage> {
List<dynamic> bookings = [];
bool isLoading = false;
final List<String> 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<void> 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<String, Map<String, int>> getYearlyMonthlyRecap() {
final now = DateTime.now();
Map<String, Map<String, int>> 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(),
),
],
),
),
);
},
),
),
);
}
}