import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; class KeranjangPage extends StatefulWidget { final String token; final Function onCartUpdated; const KeranjangPage({ super.key, required this.token, required this.onCartUpdated, }); @override State createState() => _KeranjangPageState(); } class _KeranjangPageState extends State { List cartItems = []; bool loading = true; DateTime? selectedDate; TimeOfDay? selectedTime; @override void initState() { super.initState(); fetchCart(); } Future fetchCart() async { setState(() => loading = true); try { final response = await http.get( Uri.parse('http://angeliasalon.my.id/api/cart'), headers: { 'Authorization': 'Bearer ${widget.token}', 'Accept': 'application/json', }, ); if (response.statusCode == 200) { final data = json.decode(response.body); setState(() { cartItems = data is List ? data : data['cart'] ?? []; }); widget.onCartUpdated(); // Update jumlah cart } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Gagal mengambil keranjang')), ); } } catch (e) { print(e); } setState(() => loading = false); } Future deleteItem(int id) async { try { final response = await http.delete( Uri.parse('http://angeliasalon.my.id/api/cart/$id'), headers: { 'Authorization': 'Bearer ${widget.token}', 'Accept': 'application/json', }, ); if (response.statusCode == 200) { await fetchCart(); // Panggil fetchCart agar jumlah update } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Gagal menghapus item')), ); } } catch (e) { print(e); } } int getTotalHarga() { return cartItems.fold( 0, (total, item) => total + (item['service']['price'] as int), ); } Future pickDate() async { final picked = await showDatePicker( context: context, initialDate: selectedDate ?? DateTime.now(), firstDate: DateTime.now(), lastDate: DateTime.now().add(const Duration(days: 365)), ); if (picked != null) setState(() => selectedDate = picked); } Future pickTime() async { final picked = await showTimePicker( context: context, initialTime: selectedTime ?? TimeOfDay.now(), builder: (context, child) { return MediaQuery( data: MediaQuery.of(context).copyWith(alwaysUse24HourFormat: true), child: child!, ); }, ); if (picked != null) setState(() => selectedTime = picked); } Future checkout() async { if (selectedDate == null || selectedTime == null) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text("Harap pilih tanggal dan waktu booking")), ); return; } final tanggalBooking = "${selectedDate!.year}-${selectedDate!.month.toString().padLeft(2, '0')}-${selectedDate!.day.toString().padLeft(2, '0')}"; final jamBooking = "${selectedTime!.hour.toString().padLeft(2, '0')}:${selectedTime!.minute.toString().padLeft(2, '0')}"; try { final response = await http.post( Uri.parse('http://angeliasalon.my.id/api/cart/checkout'), headers: { 'Authorization': 'Bearer ${widget.token}', 'Accept': 'application/json', 'Content-Type': 'application/json', }, body: json.encode({ 'tanggal_booking': tanggalBooking, 'jam': jamBooking, }), ); if (response.statusCode == 200 || response.statusCode == 201) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text("Booking berhasil!")), ); setState(() { cartItems.clear(); selectedDate = null; selectedTime = null; }); widget.onCartUpdated(); // Update jumlah cart setelah checkout } else if (response.statusCode == 409) { final message = jsonDecode(response.body)['message']; ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("Gagal booking: $message")), ); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text("Terjadi kesalahan saat booking")), ); } } catch (e) { print(e); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text("Terjadi error jaringan")), ); } } @override Widget build(BuildContext context) { final primaryColor = const Color(0xFFF06292); return Scaffold( appBar: AppBar( title: const Text("Keranjang Anda"), backgroundColor: primaryColor, ), body: loading ? const Center(child: CircularProgressIndicator()) : cartItems.isEmpty ? const Center(child: Text("Keranjang kosong")) : Column( children: [ Expanded( child: ListView.builder( itemCount: cartItems.length, itemBuilder: (context, index) { final item = cartItems[index]; final service = item['service']; return Card( margin: const EdgeInsets.symmetric( horizontal: 12, vertical: 6), child: ListTile( title: Text(service['name'] ?? 'Layanan'), subtitle: Text( "Harga: Rp ${service['price'] ?? 0}"), trailing: IconButton( icon: const Icon(Icons.delete, color: Colors.red), onPressed: () => deleteItem(item['id']), ), ), ); }, ), ), const Divider(), Padding( padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 8), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text( "Total: Rp ${getTotalHarga()}", style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 8), ElevatedButton.icon( onPressed: pickDate, icon: const Icon(Icons.date_range), label: Text(selectedDate == null ? 'Pilih Tanggal Booking' : 'Tanggal: ${selectedDate!.year}-${selectedDate!.month.toString().padLeft(2, '0')}-${selectedDate!.day.toString().padLeft(2, '0')}'), ), const SizedBox(height: 8), ElevatedButton.icon( onPressed: pickTime, icon: const Icon(Icons.access_time), label: Text(selectedTime == null ? 'Pilih Waktu Booking' : 'Waktu: ${selectedTime!.hour.toString().padLeft(2, '0')}:${selectedTime!.minute.toString().padLeft(2, '0')}'), ), const SizedBox(height: 16), ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: primaryColor, padding: const EdgeInsets.symmetric(vertical: 14), ), onPressed: checkout, child: const Text("Checkout", style: TextStyle(fontSize: 16)), ) ], ), ), ], ), ); } }