import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import '../models/lesson.dart'; import '../services/lessons_api.dart'; import '../widgets/yogibook_background.dart'; /// Schermata per scegliere uno slot. /// Due modalità: /// - RIPROGRAMMA: passa bookingId (+ serviceId della lezione) /// - PRENOTA: passa orderId (+ serviceId dell'ordine) class ReschedulePage extends StatefulWidget { final String token; final int serviceId; final String className; // Modalità riprogramma final int? bookingId; // Modalità prenota da ticket final int? orderId; const ReschedulePage({ super.key, required this.token, required this.serviceId, required this.className, this.bookingId, this.orderId, }); bool get isBooking => orderId != null; @override State createState() => _ReschedulePageState(); } class _ReschedulePageState extends State { bool loading = true; String error = ''; String currentMonth = DateFormat('yyyy-MM').format(DateTime.now()); List slots = []; @override void initState() { super.initState(); _load(); } Future _load() async { setState(() { loading = true; error = ''; }); try { final data = await LessonsApi.fetchAvailableSlots( token: widget.token, serviceId: widget.serviceId, month: currentMonth, ); setState(() => slots = data); } catch (e) { setState(() => error = 'Errore: $e'); } finally { setState(() => loading = false); } } String _shiftMonth(String yyyyMm, int delta) { final dt = DateFormat('yyyy-MM').parse(yyyyMm); final shifted = DateTime(dt.year, dt.month + delta, 1); return DateFormat('yyyy-MM').format(shifted); } String _monthLabel(String yyyyMm) { final dt = DateFormat('yyyy-MM').parse(yyyyMm); return DateFormat('MMMM yyyy', 'it_IT').format(dt); } String _weekdayLabel(String ymd) { final dt = DateFormat('yyyy-MM-dd').parse(ymd); final s = DateFormat('EEEE', 'it_IT').format(dt); return s[0].toUpperCase() + s.substring(1); } String _dateLabel(String ymd) { final dt = DateFormat('yyyy-MM-dd').parse(ymd); return DateFormat('dd/MM/yyyy').format(dt); } @override Widget build(BuildContext context) { const green = Color(0xFF10B981); final titolo = widget.isBooking ? 'Prenota lezione' : 'Riprogramma'; return Scaffold( appBar: AppBar( backgroundColor: Colors.transparent, elevation: 0, title: Column( children: [ Text( titolo, style: const TextStyle(fontWeight: FontWeight.w800, fontSize: 18), ), Text( widget.className, style: const TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: Colors.black54, ), ), ], ), centerTitle: true, ), body: YogibookBackground( child: SafeArea( top: false, child: Column( children: [ Padding( padding: const EdgeInsets.fromLTRB(16, 8, 16, 10), child: Container( padding: const EdgeInsets.symmetric( horizontal: 6, vertical: 6, ), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), ), child: Row( children: [ IconButton( icon: const Icon(Icons.chevron_left), color: green, onPressed: () { setState( () => currentMonth = _shiftMonth(currentMonth, -1), ); _load(); }, ), Expanded( child: Text( _monthLabel(currentMonth), textAlign: TextAlign.center, style: const TextStyle( fontSize: 15, fontWeight: FontWeight.w900, ), ), ), IconButton( icon: const Icon(Icons.chevron_right), color: green, onPressed: () { setState( () => currentMonth = _shiftMonth(currentMonth, 1), ); _load(); }, ), ], ), ), ), Expanded( child: loading ? const Center(child: CircularProgressIndicator()) : error.isNotEmpty ? Center(child: Text(error)) : slots.isEmpty ? const Center( child: Text('Nessuno slot disponibile questo mese'), ) : ListView.builder( padding: const EdgeInsets.fromLTRB(16, 0, 16, 16), itemCount: slots.length, itemBuilder: (_, i) { final s = slots[i]; return _SlotCard( slot: s, weekday: _weekdayLabel(s.date), dateLabel: _dateLabel(s.date), onChoose: () => _confirm(s), ); }, ), ), ], ), ), ), ); } Future _confirm(AvailableSlot s) async { final azione = widget.isBooking ? 'prenotare' : 'spostare'; final ok = await showDialog( context: context, builder: (dialogCtx) => AlertDialog( title: Text('Confermi?'), content: Text( 'Vuoi $azione "${s.className}" del ${s.date} alle ${s.time}?', ), actions: [ TextButton( onPressed: () => Navigator.pop(dialogCtx, false), child: const Text('Annulla'), ), ElevatedButton( onPressed: () => Navigator.pop(dialogCtx, true), child: const Text('Conferma'), ), ], ), ); if (ok != true) return; try { final String msg; if (widget.isBooking) { msg = await LessonsApi.bookFromTicket( token: widget.token, orderId: widget.orderId!, newScheduleId: s.scheduleId, ); } else { msg = await LessonsApi.reschedule( token: widget.token, bookingId: widget.bookingId!, newScheduleId: s.scheduleId, ); } if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(msg))); Navigator.pop(context); } catch (e) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('$e'))); } } } class _SlotCard extends StatelessWidget { final AvailableSlot slot; final String weekday; final String dateLabel; final VoidCallback onChoose; const _SlotCard({ required this.slot, required this.weekday, required this.dateLabel, required this.onChoose, }); @override Widget build(BuildContext context) { const green = Color(0xFF10B981); const orange = Color(0xFFF59E0B); // Determina lo stato dello slot final bool isBooked = slot.alreadyBooked; final bool isFull = !slot.bookable && !isBooked; final bool isSelectable = slot.bookable && !isBooked; // Colori in base allo stato Color bgColor; Color borderColor; if (isBooked) { bgColor = const Color(0xFFFFF4E5); // arancio chiaro borderColor = orange; } else if (isFull) { bgColor = const Color(0xFFF3F3F5); // grigio chiaro borderColor = const Color(0xFFD9D9E0); } else { bgColor = Colors.white; borderColor = const Color(0x14000000); } // Etichetta / badge a destra Widget trailing; if (isSelectable) { trailing = ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: green, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), onPressed: onChoose, child: const Text('Scegli'), ); } else if (isBooked) { trailing = _StatusBadge(text: 'Già prenotato', color: orange); } else { trailing = _StatusBadge( text: 'Al completo', color: const Color(0xFF9AA0A6), ); } return Container( margin: const EdgeInsets.only(bottom: 10), padding: const EdgeInsets.fromLTRB(14, 12, 12, 12), decoration: BoxDecoration( color: bgColor, borderRadius: BorderRadius.circular(16), border: Border.all(color: borderColor, width: 1.5), ), child: Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( slot.className, style: const TextStyle( fontWeight: FontWeight.w800, fontSize: 15, ), ), const SizedBox(height: 4), Text( '$weekday $dateLabel • ${slot.time}', style: const TextStyle( fontSize: 12, color: Colors.black54, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 2), Text( isBooked ? 'Già prenotato' : isFull ? 'Non disponibile' : 'Disponibile', style: TextStyle( fontSize: 12, color: isFull ? const Color(0xFF9AA0A6) : isBooked ? orange : green, fontWeight: FontWeight.w700, ), ), ], ), ), const SizedBox(width: 10), trailing, ], ), ); } } class _StatusBadge extends StatelessWidget { final String text; final Color color; const _StatusBadge({required this.text, required this.color}); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: color.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(999), border: Border.all(color: color, width: 1.5), ), child: Text( text, style: TextStyle( color: color, fontWeight: FontWeight.w800, fontSize: 12, ), ), ); } }