219 lines
7.0 KiB
Dart
219 lines
7.0 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:http/http.dart' as http;
|
|
import 'dart:convert';
|
|
|
|
class RiwayatBookingsKaryawanPage extends StatefulWidget {
|
|
final String token;
|
|
|
|
const RiwayatBookingsKaryawanPage({Key? key, required this.token}) : super(key: key);
|
|
|
|
@override
|
|
State<RiwayatBookingsKaryawanPage> createState() => _RiwayatBookingsKaryawanPageState();
|
|
}
|
|
|
|
class _RiwayatBookingsKaryawanPageState extends State<RiwayatBookingsKaryawanPage> {
|
|
Map<String, Map<String, List>> groupedBookings = {};
|
|
bool loading = true;
|
|
String? error;
|
|
|
|
final Color primaryColor = const Color(0xFFF06292);
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
fetchAcceptedBookings();
|
|
}
|
|
|
|
Future<void> fetchAcceptedBookings() async {
|
|
setState(() {
|
|
loading = true;
|
|
error = null;
|
|
});
|
|
|
|
try {
|
|
final response = await http.get(
|
|
Uri.parse('http://angeliasalon.my.id/api/bookings/accepted'),
|
|
headers: {
|
|
'Authorization': 'Bearer ${widget.token}',
|
|
'Accept': 'application/json',
|
|
},
|
|
);
|
|
|
|
if (response.statusCode == 200) {
|
|
final List data = json.decode(response.body);
|
|
|
|
final Map<String, Map<String, List>> tempGrouped = {};
|
|
|
|
for (var booking in data) {
|
|
final pelangganName = booking['user']?['name'] ?? 'Tidak diketahui';
|
|
final tanggalBooking = booking['tanggal_booking'] ?? '-';
|
|
|
|
tempGrouped.putIfAbsent(pelangganName, () => {});
|
|
tempGrouped[pelangganName]!.putIfAbsent(tanggalBooking, () => []);
|
|
tempGrouped[pelangganName]![tanggalBooking]!.add(booking);
|
|
}
|
|
|
|
final sortedPelanggan = tempGrouped.keys.toList()..sort();
|
|
|
|
final Map<String, Map<String, List>> sortedGrouped = {};
|
|
|
|
for (var pelanggan in sortedPelanggan) {
|
|
final tanggalMap = tempGrouped[pelanggan]!;
|
|
final sortedTanggal = tanggalMap.keys.toList()..sort();
|
|
|
|
sortedGrouped[pelanggan] = {
|
|
for (var tgl in sortedTanggal) tgl: tanggalMap[tgl]!,
|
|
};
|
|
}
|
|
|
|
setState(() {
|
|
groupedBookings = sortedGrouped;
|
|
loading = false;
|
|
});
|
|
} else {
|
|
setState(() {
|
|
error = 'Failed to load accepted bookings, status code: ${response.statusCode}';
|
|
loading = false;
|
|
});
|
|
}
|
|
} catch (e) {
|
|
setState(() {
|
|
error = 'Error fetching accepted bookings: $e';
|
|
loading = false;
|
|
});
|
|
}
|
|
}
|
|
|
|
Widget bookingItem(Map booking) {
|
|
final service = booking['service'];
|
|
final jam = booking['jam'] ?? '-';
|
|
final status = booking['status'] ?? '-';
|
|
|
|
Color statusColor;
|
|
switch (status.toLowerCase()) {
|
|
case 'diterima':
|
|
statusColor = Colors.green.shade700;
|
|
break;
|
|
case 'menunggu':
|
|
statusColor = Colors.orange.shade700;
|
|
break;
|
|
case 'ditolak':
|
|
statusColor = Colors.red.shade700;
|
|
break;
|
|
default:
|
|
statusColor = Colors.grey.shade600;
|
|
}
|
|
|
|
return ListTile(
|
|
contentPadding: const EdgeInsets.symmetric(vertical: 4, horizontal: 0),
|
|
leading: const Icon(Icons.content_cut, color: Colors.pinkAccent),
|
|
title: Text(
|
|
service?['name'] ?? 'Layanan tidak diketahui',
|
|
style: const TextStyle(fontWeight: FontWeight.w600),
|
|
),
|
|
subtitle: Row(
|
|
children: [
|
|
Text('Jam: $jam', style: const TextStyle(fontSize: 13)),
|
|
const SizedBox(width: 16),
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
|
decoration: BoxDecoration(
|
|
color: statusColor,
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
child: Text(
|
|
status.toUpperCase(),
|
|
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w600, fontSize: 12),
|
|
),
|
|
)
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget bookingGroupCard(String pelangganName, Map<String, List> tanggalMap) {
|
|
return Card(
|
|
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
|
elevation: 4,
|
|
shadowColor: Colors.black26,
|
|
child: ExpansionTile(
|
|
tilePadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
|
childrenPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
|
expandedCrossAxisAlignment: CrossAxisAlignment.start,
|
|
title: Text(
|
|
pelangganName,
|
|
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18, color: primaryColor),
|
|
),
|
|
children: tanggalMap.entries.map((entry) {
|
|
final tanggal = entry.key;
|
|
final bookings = entry.value;
|
|
|
|
return Padding(
|
|
padding: const EdgeInsets.only(left: 16.0, bottom: 10),
|
|
child: Card(
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
|
|
elevation: 2,
|
|
shadowColor: Colors.grey.withOpacity(0.2),
|
|
child: ExpansionTile(
|
|
tilePadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
|
childrenPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
|
title: Text(
|
|
'Tanggal: $tanggal',
|
|
style: TextStyle(
|
|
fontWeight: FontWeight.w600,
|
|
color: Colors.pink.shade400,
|
|
),
|
|
),
|
|
children: bookings.map((b) => bookingItem(b)).toList(),
|
|
),
|
|
),
|
|
);
|
|
}).toList(),
|
|
),
|
|
);
|
|
}
|
|
|
|
void openRiwayat() {
|
|
// optional navigation or leave empty
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: const Text('Riwayat Booking Karyawan'),
|
|
backgroundColor: primaryColor,
|
|
elevation: 5,
|
|
),
|
|
body: loading
|
|
? Center(child: CircularProgressIndicator(color: primaryColor, strokeWidth: 3))
|
|
: error != null
|
|
? Center(
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
|
child: Text(error!,
|
|
style: TextStyle(fontSize: 16, color: Colors.red.shade700),
|
|
textAlign: TextAlign.center),
|
|
),
|
|
)
|
|
: groupedBookings.isEmpty
|
|
? const Center(
|
|
child: Text(
|
|
'Belum ada riwayat booking',
|
|
style: TextStyle(fontSize: 18, color: Colors.black54),
|
|
),
|
|
)
|
|
: RefreshIndicator(
|
|
onRefresh: fetchAcceptedBookings,
|
|
child: ListView(
|
|
physics: const AlwaysScrollableScrollPhysics(),
|
|
children: groupedBookings.entries
|
|
.map((e) => bookingGroupCard(e.key, e.value))
|
|
.toList(),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|