import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import '../models/lesson.dart'; import '../models/school.dart'; import '../services/lessons_api.dart'; import '../widgets/yogibook_background.dart'; import '../widgets/app_bottom_nav.dart'; /// Schermata "I miei ordini": lista ordini con conteggi. /// Toccando un ordine si apre il dettaglio (box + lezioni). class OrdersDetailPage extends StatefulWidget { final String token; final School school; final String? userFirstName; const OrdersDetailPage({ super.key, required this.token, required this.school, this.userFirstName, }); @override State createState() => _OrdersDetailPageState(); } class _OrdersDetailPageState extends State { bool loading = true; String error = ''; List orders = []; @override void initState() { super.initState(); _load(); } Future _load() async { setState(() { loading = true; error = ''; }); try { final data = await LessonsApi.fetchOrdersDetail(token: widget.token); setState(() => orders = data); } catch (e) { setState(() => error = 'Errore: $e'); } finally { setState(() => loading = false); } } String _fmtDate(String? raw) { if (raw == null || raw.isEmpty) return '-'; try { final dt = DateTime.parse(raw); return DateFormat('dd-MM-yyyy').format(dt); } catch (_) { return raw; } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Colors.transparent, elevation: 0, centerTitle: true, title: const Text( 'I miei ordini', style: TextStyle(fontWeight: FontWeight.w800, fontSize: 18), ), ), bottomNavigationBar: AppBottomNav( currentIndex: 1, token: widget.token, school: widget.school, userFirstName: widget.userFirstName, ), body: YogibookBackground( child: SafeArea( top: false, child: loading ? const Center(child: CircularProgressIndicator()) : error.isNotEmpty ? Center(child: Text(error)) : orders.isEmpty ? const Center(child: Text('Nessun ordine trovato')) : ListView.builder( padding: const EdgeInsets.all(16), itemCount: orders.length, itemBuilder: (_, i) { final o = orders[i]; return Container( margin: const EdgeInsets.only(bottom: 14), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(18), boxShadow: const [ BoxShadow( blurRadius: 18, color: Color(0x12000000), offset: Offset(0, 10), ), ], ), child: InkWell( borderRadius: BorderRadius.circular(18), onTap: () => _openDetail(o), child: Padding( padding: const EdgeInsets.fromLTRB(16, 14, 12, 14), child: Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( o.serviceName.isEmpty ? 'Ordine #${o.orderId}' : o.serviceName, style: const TextStyle( fontWeight: FontWeight.w900, fontSize: 16, ), ), const SizedBox(height: 6), Text( 'Ordine #${o.orderId} • ${o.tickets} lezioni', style: const TextStyle( fontSize: 12, color: Colors.black54, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 4), Text( 'Da programmare: ${o.toSchedule}', style: const TextStyle( fontSize: 12, color: Colors.black54, fontWeight: FontWeight.w600, ), ), const SizedBox(height: 8), // Riga riprogrammazioni + scadenza (chip) Wrap( spacing: 8, runSpacing: 6, children: [ _MiniChip( icon: Icons.autorenew, label: 'Riprog. ${o.reprogrammed}/${o.maxReschedule}', color: const Color(0xFF10B981), ), _MiniChip( icon: Icons.event_busy, label: 'Scad. ${_fmtDate(o.expireOn)}', color: o.isExpired ? const Color(0xFFDC3545) : const Color(0xFFF59E0B), ), ], ), ], ), ), const SizedBox(width: 6), const Icon( Icons.chevron_right, color: Colors.black38, ), ], ), ), ), ); }, ), ), ), ); } void _openDetail(OrderDetail o) { showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.white, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), builder: (_) => _OrderDetailSheet(order: o, fmtDate: _fmtDate), ); } } class _OrderDetailSheet extends StatelessWidget { final OrderDetail order; final String Function(String?) fmtDate; const _OrderDetailSheet({required this.order, required this.fmtDate}); String _statusLabel(String s) { switch (s) { case 'completed': return 'Completata'; case 'booked': return 'Programmata'; case 'lost': return 'Persa'; case 'expired': return 'Scaduta'; case 'pending': return 'Da confermare'; default: return s; } } Color _statusColor(String s) { switch (s) { case 'completed': return const Color(0xFF28A745); case 'booked': return const Color(0xFF007BFF); case 'lost': return const Color(0xFFDC3545); case 'expired': return const Color(0xFFFF8C00); case 'pending': return const Color(0xFFB8860B); default: return Colors.grey; } } String _fmtDateTime(String? raw) { if (raw == null || raw.isEmpty) return '-'; try { final dt = DateTime.parse(raw); return DateFormat('dd-MM-yyyy HH:mm').format(dt); } catch (_) { return raw; } } @override Widget build(BuildContext context) { return DraggableScrollableSheet( expand: false, initialChildSize: 0.75, maxChildSize: 0.95, minChildSize: 0.5, builder: (_, scrollCtrl) => Padding( padding: const EdgeInsets.all(16), child: ListView( controller: scrollCtrl, children: [ Center( child: Container( width: 40, height: 4, decoration: BoxDecoration( color: Colors.black26, borderRadius: BorderRadius.circular(2), ), ), ), const SizedBox(height: 12), Text( 'Ordine #${order.orderId}', style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w900), ), Text( order.serviceName, style: const TextStyle(color: Colors.black54), ), const SizedBox(height: 12), // Box conteggi Wrap( spacing: 8, runSpacing: 8, children: [ _statBox('Totale', order.total, const Color(0xFFD1E7DD)), _statBox('Praticate', order.completed, const Color(0xFFD4EDDA)), _statBox('Perse', order.lost, const Color(0xFFF8D7DA)), _statBox('Scadute', order.expired, const Color(0xFFFFF3CD)), _statBox('Programmate', order.booked, const Color(0xFFCCE5FF)), _statBox( 'Da confermare', order.pending, const Color(0xFFFDE7C4), ), _statBox( 'Da programmare', order.toSchedule, const Color(0xFFE2D3F5), ), ], ), const SizedBox(height: 8), Text( 'Scadenza: ${fmtDate(order.expireOn)}' '${order.isExpired ? " (scaduto)" : ""}', style: const TextStyle(fontSize: 12, color: Colors.black54), ), const SizedBox(height: 4), Text( 'Riprogrammazioni usate: ${order.reprogrammed} / ${order.maxReschedule}', style: const TextStyle( fontSize: 12, color: Colors.black54, fontWeight: FontWeight.w600, ), ), const Divider(height: 24), const Text( 'Lezioni', style: TextStyle(fontWeight: FontWeight.w800), ), const SizedBox(height: 8), if (order.lessons.isEmpty) const Padding( padding: EdgeInsets.symmetric(vertical: 12), child: Text('Nessuna lezione per questo ordine.'), ) else ...order.lessons.map( (l) => Padding( padding: const EdgeInsets.symmetric(vertical: 6), child: Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( l.className, style: const TextStyle( fontWeight: FontWeight.w700, fontSize: 13, ), ), Text( _fmtDateTime(l.datetime), style: const TextStyle( fontSize: 12, color: Colors.black54, ), ), ], ), ), Container( padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 4, ), decoration: BoxDecoration( color: _statusColor(l.status), borderRadius: BorderRadius.circular(999), ), child: Text( _statusLabel(l.status), style: const TextStyle( color: Colors.white, fontSize: 11, fontWeight: FontWeight.w700, ), ), ), ], ), ), ), const SizedBox(height: 20), ], ), ), ); } Widget _statBox(String label, int value, Color bg) { return Container( width: 100, padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: bg, borderRadius: BorderRadius.circular(10), ), child: Column( children: [ Text( '$value', style: const TextStyle(fontSize: 22, fontWeight: FontWeight.w900), ), Text( label, textAlign: TextAlign.center, style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w600), ), ], ), ); } } class _MiniChip extends StatelessWidget { final IconData icon; final String label; final Color color; const _MiniChip({ required this.icon, required this.label, required this.color, }); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), decoration: BoxDecoration( color: color.withValues(alpha: 0.12), borderRadius: BorderRadius.circular(999), border: Border.all(color: color.withValues(alpha: 0.5), width: 1), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(icon, size: 14, color: color), const SizedBox(width: 5), Text( label, style: TextStyle( fontSize: 11, fontWeight: FontWeight.w800, color: color, ), ), ], ), ); } }