MIF_E31221388/salonbooking/lib/pemilik/data_pelanggan_page.dart

270 lines
10 KiB
Dart

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class DataPelangganPage extends StatefulWidget {
final String token;
const DataPelangganPage({super.key, required this.token});
@override
State<DataPelangganPage> createState() => _DataPelangganPageState();
}
class _DataPelangganPageState extends State<DataPelangganPage> {
List<dynamic> bookings = [];
bool isLoading = false;
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),
);
}
Icon getServiceIcon(String serviceName) {
final name = serviceName.toLowerCase();
if (name.contains('make')) {
return const Icon(Icons.brush, color: Colors.pinkAccent);
} else if (name.contains('rambut') ||
name.contains('hair') ||
name.contains('cut') ||
name.contains('blow')) {
return const Icon(Icons.content_cut, color: Colors.pinkAccent);
} else {
return const Icon(Icons.content_cut, color: Colors.pinkAccent);
}
}
String getInitials(String name) {
final parts = name.trim().split(' ');
if (parts.length == 1) return parts[0][0].toUpperCase();
return '${parts[0][0]}${parts[1][0]}'.toUpperCase();
}
Map<String, List<dynamic>> getBookingsByPelanggan() {
Map<String, List<dynamic>> result = {};
for (var booking in bookings) {
final pelangganName = booking['user']['name'] ?? 'Tidak diketahui';
result.putIfAbsent(pelangganName, () => []);
result[pelangganName]!.add(booking);
}
return result;
}
Map<String, List<dynamic>> getBookingsByTanggal(List<dynamic> bookings) {
Map<String, List<dynamic>> result = {};
for (var booking in bookings) {
final tanggal = booking['tanggal_booking'] ?? 'Tidak diketahui';
result.putIfAbsent(tanggal, () => []);
result[tanggal]!.add(booking);
}
return result;
}
int getTotalHarga(List<dynamic> bookingsPerTanggal) {
int total = 0;
for (var booking in bookingsPerTanggal) {
final harga = (booking['service']?['price'] ?? 0) as int;
total += harga;
}
return total;
}
String formatTanggal(String tanggal) {
try {
final parts = tanggal.split('-');
if (parts.length == 3) {
return '${parts[2]}-${parts[1]}-${parts[0]}';
}
return tanggal;
} catch (e) {
return tanggal;
}
}
String formatRupiah(int angka) {
return 'Rp ${angka.toString().replaceAllMapped(
RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'),
(Match m) => '${m[1]}.',
)}';
}
@override
Widget build(BuildContext context) {
final pelangganMap = getBookingsByPelanggan();
return Scaffold(
backgroundColor: backgroundColor,
appBar: AppBar(
title: const Text('Riwayat Booking Pelanggan'),
backgroundColor: primaryColor,
elevation: 0,
),
body: isLoading
? const Center(child: CircularProgressIndicator())
: pelangganMap.isEmpty
? const Center(child: Text('Tidak ada data booking'))
: RefreshIndicator(
onRefresh: fetchBookings,
child: ListView(
padding: const EdgeInsets.all(12),
children: pelangganMap.entries.map((pelangganEntry) {
final namaPelanggan = pelangganEntry.key;
final bookingsList = pelangganEntry.value;
final bookingsByTanggal =
getBookingsByTanggal(bookingsList);
return Card(
color: Colors.white,
margin: const EdgeInsets.symmetric(vertical: 8),
elevation: 5,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: ExpansionTile(
leading: CircleAvatar(
backgroundColor: primaryColor,
child: Text(
getInitials(namaPelanggan),
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
title: Text(
namaPelanggan,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
color: Colors.black87,
),
),
children: bookingsByTanggal.entries.map((tanggalEntry) {
final tanggal = tanggalEntry.key;
final bookingsPerTanggal = tanggalEntry.value;
final totalHarga = getTotalHarga(bookingsPerTanggal);
return ExpansionTile(
tilePadding:
const EdgeInsets.symmetric(horizontal: 16),
title: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
'Tanggal: ${formatTanggal(tanggal)}',
style: TextStyle(
color: primaryColor,
fontWeight: FontWeight.w600,
),
),
Text(
formatRupiah(totalHarga),
style: const TextStyle(
color: Colors.black87,
fontWeight: FontWeight.w500,
),
),
],
),
children: bookingsPerTanggal.map((booking) {
final harga = booking['service']?['price'] ?? 0;
return ListTile(
leading: CircleAvatar(
backgroundColor: Colors.pink.shade100,
child: getServiceIcon(
booking['service']?['name'] ?? '',
),
),
title: Text(
booking['service']?['name'] ?? 'Layanan tidak diketahui',
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 4),
Row(
children: [
const Icon(Icons.access_time, size: 14, color: Colors.grey),
const SizedBox(width: 4),
Text('Jam: ${booking['jam']}'),
],
),
const SizedBox(height: 2),
Row(
children: [
const Icon(Icons.monetization_on, size: 14, color: Colors.grey),
const SizedBox(width: 4),
Text('Harga: ${formatRupiah(harga as int)}'),
],
),
],
),
trailing: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
decoration: BoxDecoration(
color: booking['status'] == 'diterima'
? Colors.green
: booking['status'] == 'ditolak'
? Colors.red
: Colors.grey,
borderRadius: BorderRadius.circular(20),
),
child: Text(
booking['status'].toString().toUpperCase(),
style: const TextStyle(color: Colors.white, fontSize: 12),
),
),
);
}).toList(),
);
}).toList(),
),
);
}).toList(),
),
),
);
}
}