import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'dart:math'; import '../models/film.dart'; import '../models/booking.dart'; import '../data/app_data.dart'; import 'ticket_page.dart'; class SeatSelectionPage extends StatefulWidget { final Film film; final String date; final String schedule; final String username; final String cinema; const SeatSelectionPage({ super.key, required this.film, required this.date, required this.schedule, required this.username, required this.cinema, }); @override State createState() => _SeatSelectionPageState(); } class _SeatSelectionPageState extends State { final List _selectedSeats = []; final rows = ['A', 'B', 'C', 'D', 'E']; String _selectedPayment = 'QRIS'; void _toggleSeat(int row, int col) { final seats = widget.film.schedules[widget.cinema]![widget.date]![widget.schedule]!; final seatId = '$row-$col'; if (seats[row][col] == 'booked') return; setState(() { if (_selectedSeats.contains(seatId)) { _selectedSeats.remove(seatId); } else { _selectedSeats.add(seatId); } }); } void _confirmBooking() { if (_selectedSeats.isEmpty) return; showDialog( context: context, builder: (context) => AlertDialog( backgroundColor: const Color(0xFF1F2937), title: const Text('Konfirmasi Pemesanan'), content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildConfirmRow('Film', widget.film.title), _buildConfirmRow( 'Tanggal', DateFormat('dd MMM yyyy').format(DateTime.parse(widget.date)), ), _buildConfirmRow('Jam', widget.schedule), _buildConfirmRow('Bioskop', widget.cinema), _buildConfirmRow( 'Kursi', _selectedSeats.map((s) { final parts = s.split('-'); return '${rows[int.parse(parts[0])]}${int.parse(parts[1]) + 1}'; }).join(', '), ), _buildConfirmRow('Pembayaran', _selectedPayment), const Divider(color: Color(0xFF374151)), _buildConfirmRow( 'Total', 'Rp ${_formatPrice(widget.film.price * _selectedSeats.length)}', isTotal: true, ), ], ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Batal'), ), ElevatedButton( onPressed: () { Navigator.pop(context); _processBooking(); }, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFFFBBF24), foregroundColor: Colors.black, ), child: const Text('Bayar'), ), ], ), ); } void _processBooking() { final bookingCode = 'BK${Random().nextInt(1000000).toString().padLeft(6, '0')}'; final seatNames = _selectedSeats.map((s) { final parts = s.split('-'); return '${rows[int.parse(parts[0])]}${int.parse(parts[1]) + 1}'; }).toList(); final booking = Booking( code: bookingCode, film: widget.film.title, date: widget.date, schedule: widget.schedule, seats: seatNames, price: widget.film.price * _selectedSeats.length, user: widget.username, cinema: widget.cinema, bookingTime: DateTime.now(), paymentMethod: _selectedPayment, ); final seats = widget.film.schedules[widget.cinema]![widget.date]![widget.schedule]!; for (var seat in _selectedSeats) { final parts = seat.split('-'); seats[int.parse(parts[0])][int.parse(parts[1])] = 'booked'; } AppData.bookings.add(booking); Navigator.pushReplacement( context, MaterialPageRoute(builder: (_) => TicketPage(booking: booking)), ); } Widget _buildConfirmRow(String label, String value, {bool isTotal = false}) { return Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( label, style: TextStyle( color: isTotal ? Colors.white : Colors.grey, fontWeight: isTotal ? FontWeight.bold : FontWeight.normal, ), ), Flexible( child: Text( value, style: TextStyle( fontWeight: FontWeight.bold, color: isTotal ? const Color(0xFFFBBF24) : Colors.white, fontSize: isTotal ? 18 : 14, ), textAlign: TextAlign.right, ), ), ], ), ); } @override Widget build(BuildContext context) { final seats = widget.film.schedules[widget.cinema]![widget.date]![widget.schedule]!; return Scaffold( appBar: AppBar( backgroundColor: Colors.black, title: const Text('Pilih Kursi'), actions: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Row( children: [ const Icon(Icons.person, size: 20), const SizedBox(width: 8), Text(widget.username), ], ), ), ], ), body: SingleChildScrollView( padding: const EdgeInsets.all(24), child: Center( child: Container( constraints: const BoxConstraints(maxWidth: 900), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( flex: 2, child: Container( padding: const EdgeInsets.all(32), decoration: BoxDecoration( color: const Color(0xFF1F2937), borderRadius: BorderRadius.circular(16), border: Border.all(color: const Color(0xFF374151)), ), child: Column( children: [ Text( widget.film.title, style: const TextStyle( fontSize: 24, fontWeight: FontWeight.bold), textAlign: TextAlign.center, ), const SizedBox(height: 8), Text( '${DateFormat('EEE, dd MMM yyyy').format(DateTime.parse(widget.date))} • ${widget.schedule}', style: const TextStyle(color: Colors.grey), ), const SizedBox(height: 4), Text( widget.cinema, style: const TextStyle( color: Color(0xFFFBBF24), fontWeight: FontWeight.w600), ), const SizedBox(height: 32), Container( height: 6, decoration: BoxDecoration( gradient: const LinearGradient( colors: [ Colors.transparent, Color(0xFFFBBF24), Colors.transparent ], ), borderRadius: BorderRadius.circular(3), ), ), const SizedBox(height: 8), const Text( 'LAYAR', style: TextStyle( color: Colors.grey, fontSize: 12, letterSpacing: 4), ), const SizedBox(height: 32), ...List.generate(5, (rowIndex) { return Padding( padding: const EdgeInsets.symmetric(vertical: 6), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ SizedBox( width: 32, child: Text( rows[rowIndex], style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 16), textAlign: TextAlign.center, ), ), ...List.generate(8, (colIndex) { final status = seats[rowIndex][colIndex]; final seatId = '$rowIndex-$colIndex'; final isSelected = _selectedSeats.contains(seatId); return Padding( padding: const EdgeInsets.symmetric( horizontal: 4), child: InkWell( onTap: () => _toggleSeat(rowIndex, colIndex), child: Container( width: 45, height: 45, decoration: BoxDecoration( color: status == 'booked' ? Colors.red.shade700 : isSelected ? const Color(0xFFFBBF24) : const Color(0xFF374151), borderRadius: const BorderRadius.vertical( top: Radius.circular(10), ), border: Border.all( color: isSelected ? const Color(0xFFFBBF24) : Colors.transparent, width: 2, ), ), child: Center( child: Text( '${rows[rowIndex]}${colIndex + 1}', style: TextStyle( fontSize: 10, fontWeight: FontWeight.bold, color: status == 'booked' ? Colors.white54 : isSelected ? Colors.black : Colors.grey, ), ), ), ), ), ); }), ], ), ); }), const SizedBox(height: 32), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ _buildLegend(const Color(0xFF374151), 'Tersedia'), const SizedBox(width: 24), _buildLegend(const Color(0xFFFBBF24), 'Dipilih'), const SizedBox(width: 24), _buildLegend(Colors.red.shade700, 'Terisi'), ], ), ], ), ), ), const SizedBox(width: 24), SizedBox( width: 350, child: Column( children: [ Container( padding: const EdgeInsets.all(24), decoration: BoxDecoration( color: const Color(0xFF1F2937), borderRadius: BorderRadius.circular(16), border: Border.all(color: const Color(0xFF374151)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Metode Pembayaran', style: TextStyle( fontWeight: FontWeight.bold, fontSize: 16), ), const SizedBox(height: 12), ...['QRIS', 'GoPay', 'OVO', 'Dana', 'Kartu Kredit'] .map((method) { return RadioListTile( value: method, groupValue: _selectedPayment, onChanged: (value) { setState(() => _selectedPayment = value!); }, title: Text(method), activeColor: const Color(0xFFFBBF24), contentPadding: EdgeInsets.zero, ); }), ], ), ), const SizedBox(height: 16), Container( padding: const EdgeInsets.all(24), decoration: BoxDecoration( color: const Color(0xFF111827), borderRadius: BorderRadius.circular(16), border: Border.all(color: const Color(0xFF374151)), ), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text('Kursi Dipilih', style: TextStyle(color: Colors.grey)), Text( _selectedSeats.isEmpty ? '-' : _selectedSeats.map((s) { final parts = s.split('-'); return '${rows[int.parse(parts[0])]}${int.parse(parts[1]) + 1}'; }).join(', '), style: const TextStyle( fontWeight: FontWeight.bold), ), ], ), const SizedBox(height: 12), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text('Harga per Tiket', style: TextStyle(color: Colors.grey)), Text( 'Rp ${_formatPrice(widget.film.price)}', style: const TextStyle( fontWeight: FontWeight.bold), ), ], ), const Divider(color: Color(0xFF374151), height: 32), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( 'Total Pembayaran', style: TextStyle( fontWeight: FontWeight.bold, fontSize: 16), ), Text( 'Rp ${_formatPrice(widget.film.price * _selectedSeats.length)}', style: const TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: Color(0xFFFBBF24), ), ), ], ), const SizedBox(height: 24), SizedBox( width: double.infinity, child: ElevatedButton( onPressed: _selectedSeats.isEmpty ? null : _confirmBooking, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFFFBBF24), foregroundColor: Colors.black, padding: const EdgeInsets.symmetric(vertical: 16), disabledBackgroundColor: Colors.grey.shade800, ), child: const Text( 'Lanjutkan', style: TextStyle( fontWeight: FontWeight.bold, fontSize: 16), ), ), ), ], ), ), ], ), ), ], ), ), ), ), ); } Widget _buildLegend(Color color, String label) { return Row( children: [ Container( width: 24, height: 24, decoration: BoxDecoration( color: color, borderRadius: BorderRadius.circular(4)), ), const SizedBox(width: 8), Text(label, style: const TextStyle(color: Colors.grey, fontSize: 12)), ], ); } String _formatPrice(int price) { return price.toString().replaceAllMapped( RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'), (m) => '${m[1]}.', ); } }