import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import '../models/lesson.dart'; import '../models/school.dart'; import '../services/vanguard_api.dart'; import 'select_school_page.dart'; import 'home_page.dart'; import 'meditation_page.dart'; class LessonsPage extends StatefulWidget { final String token; final School school; final String? userFirstName; const LessonsPage({ super.key, required this.token, required this.school, this.userFirstName, }); @override State createState() => _LessonsPageState(); } class _LessonsPageState extends State { bool loading = true; String error = ''; String currentMonth = DateFormat('yyyy-MM').format(DateTime.now()); String? prevMonth; String? nextMonth; String? schoolAddress; List lessons = []; int bottomIndex = 1; @override void initState() { super.initState(); _load(); } Future _load() async { setState(() { loading = true; error = ''; }); try { final data = await VanguardApi.getMyLessons( token: widget.token, schoolId: widget.school.id, month: currentMonth, ); prevMonth = data['prev_month']?.toString(); nextMonth = data['next_month']?.toString(); schoolAddress = (data['school'] as Map?)?['address_full'] ?.toString(); final list = (data['lessons'] as List? ?? []) .map((e) => Lesson.fromJson(e as Map)) .toList(); setState(() => lessons = list); } catch (e) { setState(() => error = 'Errore: $e'); } finally { setState(() => loading = false); } } 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 _dayNum(String ymd) { final dt = DateFormat('yyyy-MM-dd').parse(ymd); return DateFormat('dd').format(dt); } String get _name => (widget.userFirstName ?? '').trim(); String get _avatarLetter => _name.isNotEmpty ? _name[0].toUpperCase() : 'U'; void _handleBottomNav(int i) { if (i == bottomIndex) return; setState(() => bottomIndex = i); if (i == 0) { Navigator.pushReplacement( context, MaterialPageRoute( builder: (_) => HomePage( token: widget.token, school: widget.school, userFirstName: widget.userFirstName, ), ), ); return; } if (i == 1) return; // sei già su Lezioni if (i == 2) { ScaffoldMessenger.of( context, ).showSnackBar(const SnackBar(content: Text('TODO: vai ad Account'))); return; } if (i == 3) { Navigator.pushReplacement( context, MaterialPageRoute( builder: (_) => MeditationPage( token: widget.token, school: widget.school, userFirstName: widget.userFirstName, ), ), ); return; } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFFF6F6FB), drawer: _AppDrawer( token: widget.token, onLogout: () { Navigator.of(context).pop(); ScaffoldMessenger.of( context, ).showSnackBar(const SnackBar(content: Text('Logout (TODO)'))); }, ), appBar: AppBar( backgroundColor: const Color(0xFFF6F6FB), elevation: 0, centerTitle: true, title: Column( children: [ const Text( 'Le mie lezioni', style: TextStyle(fontWeight: FontWeight.w800, fontSize: 18), ), const SizedBox(height: 2), Text( widget.school.name, style: const TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: Colors.black54, ), ), ], ), actions: [ Padding( padding: const EdgeInsets.only(right: 12), child: CircleAvatar( radius: 16, backgroundColor: const Color(0xFF10B981), child: Text( _avatarLetter, style: const TextStyle( color: Colors.white, fontWeight: FontWeight.w900, fontSize: 12, ), ), ), ), ], ), bottomNavigationBar: BottomNavigationBar( type: BottomNavigationBarType.fixed, // con 4 icone serve backgroundColor: Colors.white, selectedItemColor: const Color(0xFF10B981), unselectedItemColor: Colors.black54, currentIndex: bottomIndex, onTap: _handleBottomNav, items: const [ BottomNavigationBarItem( icon: Icon(Icons.home_rounded), label: 'Home', ), BottomNavigationBarItem( icon: Icon(Icons.event_note_rounded), label: 'Lezioni', ), BottomNavigationBarItem( icon: Icon(Icons.person_rounded), label: 'Account', ), BottomNavigationBarItem( icon: Icon(Icons.self_improvement_rounded), label: 'Meditazione', ), ], ), body: SafeArea( top: false, child: Column( children: [ // Address small (optional) if (schoolAddress != null && schoolAddress!.trim().isNotEmpty) Padding( padding: const EdgeInsets.fromLTRB(16, 6, 16, 6), child: Text( schoolAddress!, textAlign: TextAlign.center, style: const TextStyle( fontSize: 12, color: Colors.black45, fontWeight: FontWeight.w500, ), ), ), // Month selector only Padding( padding: const EdgeInsets.fromLTRB(16, 6, 16, 10), child: _MonthPillCompact( label: _monthLabel(currentMonth), onPrev: prevMonth == null ? null : () { setState(() => currentMonth = prevMonth!); _load(); }, onNext: nextMonth == null ? null : () { setState(() => currentMonth = nextMonth!); _load(); }, ), ), Expanded( child: loading ? const Center(child: CircularProgressIndicator()) : error.isNotEmpty ? _ErrorBox(message: error, onRetry: _load) : lessons.isEmpty ? _EmptyState( onBrowse: () { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('TODO: vedi corsi')), ); }, ) : ListView.builder( padding: const EdgeInsets.fromLTRB(16, 0, 16, 16), itemCount: lessons.length + 1, itemBuilder: (_, i) { if (i == lessons.length) { return Padding( padding: const EdgeInsets.only(top: 8, bottom: 6), child: Center( child: Image.asset( 'assets/images/yogibook_logo.png', height: 56, ), ), ); } final l = lessons[i]; return _LessonGreenCardCompact( lesson: l, weekday: _weekdayLabel(l.date), dayNum: _dayNum(l.date), onReschedule: l.canModify ? () => ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Riprogramma: API dopo'), ), ) : null, onCancel: l.canModify ? () => ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Cancella: API dopo'), ), ) : null, ); }, ), ), ], ), ), ); } } class _AppDrawer extends StatelessWidget { final String token; final VoidCallback onLogout; const _AppDrawer({required this.token, required this.onLogout}); @override Widget build(BuildContext context) { return Drawer( child: SafeArea( child: ListView( padding: EdgeInsets.zero, children: [ const DrawerHeader( child: Align( alignment: Alignment.bottomLeft, child: Text( 'Menu', style: TextStyle(fontSize: 22, fontWeight: FontWeight.w900), ), ), ), ListTile( leading: const Icon(Icons.swap_horiz), title: const Text('Cambia scuola'), onTap: () { Navigator.of(context).pop(); // chiude drawer Navigator.pushReplacement( context, MaterialPageRoute( builder: (_) => SelectSchoolPage(token: token), ), ); }, ), const Divider(height: 1), ListTile( leading: const Icon(Icons.logout), title: const Text('Logout'), onTap: onLogout, ), ], ), ), ); } } class _MonthPillCompact extends StatelessWidget { final String label; final VoidCallback? onPrev; final VoidCallback? onNext; const _MonthPillCompact({ required this.label, required this.onPrev, required this.onNext, }); @override Widget build(BuildContext context) { const green = Color(0xFF10B981); return Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 6), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: const [ BoxShadow( blurRadius: 14, color: Color(0x11000000), offset: Offset(0, 8), ), ], ), child: Row( children: [ IconButton( onPressed: onPrev, icon: const Icon(Icons.chevron_left), iconSize: 22, padding: EdgeInsets.zero, constraints: const BoxConstraints(minWidth: 40, minHeight: 40), color: onPrev == null ? Colors.black26 : green, ), Expanded( child: Text( label, textAlign: TextAlign.center, style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w900), ), ), IconButton( onPressed: onNext, icon: const Icon(Icons.chevron_right), iconSize: 22, padding: EdgeInsets.zero, constraints: const BoxConstraints(minWidth: 40, minHeight: 40), color: onNext == null ? Colors.black26 : green, ), ], ), ); } } class _LessonGreenCardCompact extends StatelessWidget { final Lesson lesson; final String weekday; final String dayNum; final VoidCallback? onReschedule; final VoidCallback? onCancel; const _LessonGreenCardCompact({ required this.lesson, required this.weekday, required this.dayNum, required this.onReschedule, required this.onCancel, }); @override Widget build(BuildContext context) { const green = Color(0xFF10B981); const darkGreen = Color(0xFF065F46); final level = (lesson.level ?? '').trim(); final time = _shortTime(lesson.startTime); return Container( margin: const EdgeInsets.only(bottom: 12), decoration: BoxDecoration( color: const Color(0xFFE7F8F1), borderRadius: BorderRadius.circular(18), boxShadow: const [ BoxShadow( blurRadius: 18, color: Color(0x12000000), offset: Offset(0, 10), ), ], ), child: ClipRRect( borderRadius: BorderRadius.circular(18), child: Row( children: [ // left panel smaller Container( width: 92, color: const Color(0xFFBFF3DE), padding: const EdgeInsets.fromLTRB(10, 12, 10, 12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( dayNum, style: const TextStyle( fontSize: 28, height: 1, fontWeight: FontWeight.w900, color: darkGreen, ), ), const SizedBox(height: 4), Text( weekday, style: const TextStyle( fontSize: 11, fontWeight: FontWeight.w800, color: darkGreen, ), ), const SizedBox(height: 8), Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 6, ), decoration: BoxDecoration( color: Colors.white.withOpacity(0.6), borderRadius: BorderRadius.circular(12), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ const Icon( Icons.schedule, size: 14, color: Colors.black54, ), const SizedBox(width: 6), FittedBox( fit: BoxFit.scaleDown, child: Text( time, style: const TextStyle( fontSize: 11, fontWeight: FontWeight.w800, ), ), ), ], ), ), ], ), ), // right body tighter Expanded( child: Container( padding: const EdgeInsets.fromLTRB(10, 10, 10, 10), color: const Color(0xFFE7F8F1), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( lesson.className, style: const TextStyle( fontSize: 15, fontWeight: FontWeight.w900, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 6), Row( children: [ const Icon( Icons.meeting_room_outlined, size: 16, color: Colors.black54, ), const SizedBox(width: 6), Expanded( child: Text( (lesson.roomName ?? '').trim().isEmpty ? 'Sala da definire' : lesson.roomName!, style: const TextStyle( fontSize: 12, color: Color(0xFF404040), fontWeight: FontWeight.w600, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), if (level.isNotEmpty) ...[ const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 4, ), decoration: BoxDecoration( color: const Color(0xFF10B981), borderRadius: BorderRadius.circular(999), ), child: Text( _capitalize(level), style: const TextStyle( fontWeight: FontWeight.w900, fontSize: 10, color: Colors.white, ), ), ), ], ], ), const SizedBox(height: 8), // Buttons compact (stessa riga, più piccoli) Row( children: [ Expanded( child: SizedBox( height: 30, child: ElevatedButton( onPressed: onReschedule, style: ElevatedButton.styleFrom( backgroundColor: green, padding: EdgeInsets.zero, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), child: const Text( 'Riprogramma', style: TextStyle( fontSize: 12, fontWeight: FontWeight.w800, ), ), ), ), ), const SizedBox(width: 8), Expanded( child: SizedBox( height: 30, child: OutlinedButton( onPressed: onCancel, style: OutlinedButton.styleFrom( foregroundColor: green, side: const BorderSide(color: green, width: 2), padding: EdgeInsets.zero, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), child: const Text( 'Cancella', style: TextStyle( fontSize: 12, fontWeight: FontWeight.w800, ), ), ), ), ), ], ), if (!lesson.canModify) ...[ const SizedBox(height: 6), const Text( 'Non modificabile (entro 24 ore)', style: TextStyle(fontSize: 11, color: Colors.black54), ), ], ], ), ), ), ], ), ), ); } static Widget _metaRowCompact(IconData icon, String text) { return Row( children: [ Icon(icon, size: 16, color: Colors.black54), const SizedBox(width: 6), Expanded( child: Text( text, style: const TextStyle( fontSize: 12, color: Color(0xFF404040), fontWeight: FontWeight.w600, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), ], ); } static String _shortTime(String t) => t.length >= 5 ? t.substring(0, 5) : t; static String _capitalize(String s) => s.isEmpty ? s : s[0].toUpperCase() + s.substring(1); } class _EmptyState extends StatelessWidget { final VoidCallback onBrowse; const _EmptyState({required this.onBrowse}); @override Widget build(BuildContext context) { return Center( child: Padding( padding: const EdgeInsets.all(18), child: Column( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.event_busy, size: 64, color: Colors.black26), const SizedBox(height: 14), const Text( 'Nessuna lezione prenotata', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w800, color: Colors.black54, ), ), const SizedBox(height: 6), const Text( 'Le tue lezioni appariranno qui', style: TextStyle(color: Colors.black45), ), const SizedBox(height: 14), ElevatedButton( onPressed: onBrowse, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF10B981), padding: const EdgeInsets.symmetric( horizontal: 18, vertical: 12, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(14), ), ), child: const Text('Vedi i corsi'), ), ], ), ), ); } } class _ErrorBox extends StatelessWidget { final String message; final VoidCallback onRetry; const _ErrorBox({required this.message, required this.onRetry}); @override Widget build(BuildContext context) { return Center( child: Padding( padding: const EdgeInsets.all(18), child: Column( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.error_outline, size: 58, color: Colors.redAccent), const SizedBox(height: 10), Text(message, textAlign: TextAlign.center), const SizedBox(height: 12), ElevatedButton(onPressed: onRetry, child: const Text('Riprova')), ], ), ), ); } }