import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import 'package:intl/intl.dart'; import '/core/auth_provider.dart'; const _bg = Color(0xFF0D1117); const _card = Color(0xFF161B22); const _blue = Color(0xFF4FC3F7); const _green = Color(0xFF2ECC71); const _amber = Color(0xFFFFB300); const _red = Color(0xFFE74C3C); const _mealNames = ['Pequeno Almoço', 'Almoço', 'Lanche da Tarde', 'Jantar']; const _mealIcons = [Icons.free_breakfast, Icons.lunch_dining, Icons.icecream, Icons.dinner_dining]; const _weekDays = ['Segunda', 'Terça', 'Quarta', 'Quinta', 'Sexta']; class MenuScreen extends ConsumerStatefulWidget { const MenuScreen({super.key}); @override ConsumerState createState() => _State(); } class _State extends ConsumerState with SingleTickerProviderStateMixin { late TabController _tabs; DateTime _selectedWeek = _startOfWeek(DateTime.now()); bool _isAdmin = false; @override void initState() { super.initState(); _tabs = TabController(length: 2, vsync: this); _checkRole(); } @override void dispose() { _tabs.dispose(); super.dispose(); } Future _checkRole() async { final p = await ref.read(currentProfileProvider.future); if (mounted) setState(() => _isAdmin = p?.role == 'principal' || p?.role == 'admin'); } static DateTime _startOfWeek(DateTime d) { final diff = d.weekday - 1; return DateTime(d.year, d.month, d.day - diff); } String get _weekLabel { final end = _selectedWeek.add(const Duration(days: 4)); final fmt = DateFormat('d MMM', 'pt_PT'); return '${fmt.format(_selectedWeek)} – ${fmt.format(end)}'; } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: _bg, appBar: AppBar( backgroundColor: _card, elevation: 0, title: const Text('Cardápio', style: TextStyle(color: _blue, fontWeight: FontWeight.bold)), bottom: TabBar( controller: _tabs, indicatorColor: _blue, labelColor: _blue, unselectedLabelColor: Colors.white38, tabs: const [Tab(text: '📅 Semanal'), Tab(text: '📋 Mensal')], ), actions: [ if (_isAdmin) IconButton( icon: const Icon(Icons.add_circle_outline, color: _amber), tooltip: 'Publicar cardápio', onPressed: () => _showPublishDialog(context), ), ], ), body: TabBarView(controller: _tabs, children: [ _WeeklyMenu(week: _selectedWeek, weekLabel: _weekLabel, onPrev: () => setState(() => _selectedWeek = _selectedWeek.subtract(const Duration(days: 7))), onNext: () => setState(() => _selectedWeek = _selectedWeek.add(const Duration(days: 7)))), const _MonthlyMenu(), ]), ); } void _showPublishDialog(BuildContext ctx) { showModalBottomSheet( context: ctx, isScrollControlled: true, backgroundColor: _card, shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(20))), builder: (_) => _PublishMenuForm(week: _selectedWeek), ); } } // ── Cardápio Semanal ────────────────────────────────────────────── class _WeeklyMenu extends StatelessWidget { final DateTime week; final String weekLabel; final VoidCallback onPrev, onNext; const _WeeklyMenu({required this.week, required this.weekLabel, required this.onPrev, required this.onNext}); @override Widget build(BuildContext context) { final sb = Supabase.instance.client; final weekStr = DateFormat('yyyy-MM-dd').format(week); return Column(children: [ // Navegação de semana Container( color: _card, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), child: Row(children: [ IconButton(onPressed: onPrev, icon: const Icon(Icons.chevron_left, color: _blue)), Expanded(child: Text(weekLabel, textAlign: TextAlign.center, style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 14))), IconButton(onPressed: onNext, icon: const Icon(Icons.chevron_right, color: _blue)), ]), ), Expanded( child: FutureBuilder>>( future: sb.from('menu_items').select() .eq('week_start', weekStr) .order('day_index').order('meal_index'), builder: (ctx, snap) { if (snap.hasError) return _err('Erro: ${snap.error}'); if (!snap.hasData) return const Center(child: CircularProgressIndicator(color: _blue)); final items = snap.data!; if (items.isEmpty) return _emptyMenu(); // Agrupar por dia final Map>> byDay = {}; for (final item in items) { final day = (item['day_index'] as int?) ?? 0; byDay.putIfAbsent(day, () => []).add(item); } return ListView.builder( padding: const EdgeInsets.all(14), itemCount: 5, itemBuilder: (_, i) { final date = week.add(Duration(days: i)); final dayMeals = byDay[i] ?? []; return _DayCard( dayName: _weekDays[i], date: DateFormat('d/MM').format(date), meals: dayMeals, isToday: DateFormat('yyyy-MM-dd').format(DateTime.now()) == DateFormat('yyyy-MM-dd').format(date), ); }, ); }, ), ), ]); } } class _DayCard extends StatelessWidget { final String dayName, date; final List> meals; final bool isToday; const _DayCard({required this.dayName, required this.date, required this.meals, required this.isToday}); @override Widget build(BuildContext context) => Container( margin: const EdgeInsets.only(bottom: 12), decoration: BoxDecoration( color: _card, borderRadius: BorderRadius.circular(16), border: Border.all(color: isToday ? _blue.withOpacity(0.5) : Colors.white.withOpacity(0.07)), boxShadow: isToday ? [BoxShadow(color: _blue.withOpacity(0.08), blurRadius: 12)] : null, ), child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header do dia Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), decoration: BoxDecoration( color: isToday ? _blue.withOpacity(0.12) : Colors.white.withOpacity(0.03), borderRadius: const BorderRadius.vertical(top: Radius.circular(16)), ), child: Row(children: [ Text(dayName, style: TextStyle( color: isToday ? _blue : Colors.white, fontWeight: FontWeight.bold, fontSize: 14)), const SizedBox(width: 8), Text(date, style: TextStyle(color: Colors.white.withOpacity(0.4), fontSize: 12)), if (isToday) ...[ const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), decoration: BoxDecoration(color: _blue.withOpacity(0.2), borderRadius: BorderRadius.circular(10)), child: const Text('Hoje', style: TextStyle(color: _blue, fontSize: 10, fontWeight: FontWeight.bold)), ), ], ]), ), if (meals.isEmpty) Padding( padding: const EdgeInsets.all(14), child: Text('Sem ementa publicada', style: TextStyle(color: Colors.white.withOpacity(0.25), fontSize: 12)), ) else ...meals.map((m) { final mealIdx = (m['meal_index'] as int?) ?? 0; final name = mealIdx < _mealIcons.length ? _mealNames[mealIdx] : 'Refeição'; final icon = mealIdx < _mealIcons.length ? _mealIcons[mealIdx] : Icons.restaurant; return Padding( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 7), child: Row(children: [ Icon(icon, size: 16, color: _amber), const SizedBox(width: 10), Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(name, style: const TextStyle(color: Color(0xFF888888), fontSize: 10)), Text(m['description'] ?? '', style: const TextStyle(color: Colors.white, fontSize: 13)), ]), ]), ); }), const SizedBox(height: 4), ]), ); } // ── Cardápio Mensal ─────────────────────────────────────────────── class _MonthlyMenu extends StatefulWidget { const _MonthlyMenu(); @override State<_MonthlyMenu> createState() => _MonthlyState(); } class _MonthlyState extends State<_MonthlyMenu> { DateTime _month = DateTime(DateTime.now().year, DateTime.now().month); @override Widget build(BuildContext context) { final sb = Supabase.instance.client; final monthStr = DateFormat('yyyy-MM').format(_month); final monthName = DateFormat('MMMM yyyy', 'pt_PT').format(_month); return Column(children: [ Container( color: _card, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), child: Row(children: [ IconButton( onPressed: () => setState(() => _month = DateTime(_month.year, _month.month - 1)), icon: const Icon(Icons.chevron_left, color: _blue)), Expanded(child: Text(monthName.toUpperCase(), textAlign: TextAlign.center, style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 13, letterSpacing: 1))), IconButton( onPressed: () => setState(() => _month = DateTime(_month.year, _month.month + 1)), icon: const Icon(Icons.chevron_right, color: _blue)), ]), ), Expanded( child: FutureBuilder>>( future: sb.from('menu_items').select() .like('week_start', '$monthStr%') .order('week_start').order('day_index'), builder: (ctx, snap) { if (!snap.hasData) return const Center(child: CircularProgressIndicator(color: _blue)); if (snap.data!.isEmpty) return _emptyMenu(); // Agrupa por semana final Map>> byWeek = {}; for (final item in snap.data!) { final w = item['week_start'] as String; byWeek.putIfAbsent(w, () => []).add(item); } return ListView( padding: const EdgeInsets.all(14), children: byWeek.entries.map((e) { final weekDt = DateTime.parse(e.key); final end = weekDt.add(const Duration(days: 4)); return _WeekSummaryCard( label: '${DateFormat('d', 'pt_PT').format(weekDt)}–${DateFormat('d MMM', 'pt_PT').format(end)}', items: e.value, ); }).toList(), ); }, ), ), ]); } } class _WeekSummaryCard extends StatelessWidget { final String label; final List> items; const _WeekSummaryCard({required this.label, required this.items}); @override Widget build(BuildContext context) => Container( margin: const EdgeInsets.only(bottom: 10), padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: _card, borderRadius: BorderRadius.circular(14), border: Border.all(color: Colors.white.withOpacity(0.07))), child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ Row(children: [ const Icon(Icons.calendar_view_week, color: _blue, size: 16), const SizedBox(width: 6), Text('Semana de $label', style: const TextStyle(color: _blue, fontWeight: FontWeight.bold, fontSize: 13)), ]), const SizedBox(height: 8), ...items.take(6).map((m) { final day = (m['day_index'] as int?) ?? 0; final meal = (m['meal_index'] as int?) ?? 0; final dayName = day < _weekDays.length ? _weekDays[day] : ''; final mealName = meal < _mealNames.length ? _mealNames[meal] : ''; return Padding( padding: const EdgeInsets.only(bottom: 3), child: Text('• $dayName – $mealName: ${m['description'] ?? ''}', style: const TextStyle(color: Color(0xFF888888), fontSize: 12)), ); }), if (items.length > 6) Text('+${items.length - 6} itens', style: TextStyle(color: Colors.white.withOpacity(0.2), fontSize: 11)), ]), ); } // ── Formulário publicar cardápio (admin) ────────────────────────── class _PublishMenuForm extends ConsumerStatefulWidget { final DateTime week; const _PublishMenuForm({required this.week}); @override ConsumerState<_PublishMenuForm> createState() => _PublishState(); } class _PublishState extends ConsumerState<_PublishMenuForm> { int _day = 0, _meal = 0; final _descCtrl = TextEditingController(); bool _saving = false; @override void dispose() { _descCtrl.dispose(); super.dispose(); } Future _save() async { if (_descCtrl.text.trim().isEmpty) return; setState(() => _saving = true); try { final sb = Supabase.instance.client; final weekStr = DateFormat('yyyy-MM-dd').format(widget.week); await sb.from('menu_items').upsert({ 'week_start': weekStr, 'day_index': _day, 'meal_index': _meal, 'description': _descCtrl.text.trim(), }, onConflict: 'week_start,day_index,meal_index'); _descCtrl.clear(); if (mounted) { ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text('Publicado! ✓', style: TextStyle(color: Colors.white)), backgroundColor: _green, behavior: SnackBarBehavior.floating)); } } catch (e) { if (mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text('Erro: $e'), backgroundColor: _red, behavior: SnackBarBehavior.floating)); } finally { if (mounted) setState(() => _saving = false); } } @override Widget build(BuildContext context) => Padding( padding: EdgeInsets.only(left: 20, right: 20, top: 20, bottom: MediaQuery.of(context).viewInsets.bottom + 20), child: Column(mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('Publicar Ementa', style: TextStyle(color: Colors.white, fontSize: 17, fontWeight: FontWeight.bold)), Text('Semana: ${DateFormat('d MMM', 'pt_PT').format(widget.week)}', style: const TextStyle(color: Color(0xFF888888), fontSize: 12)), const SizedBox(height: 18), // Dia const Text('Dia', style: TextStyle(color: Color(0xFF888888), fontSize: 12)), const SizedBox(height: 6), SingleChildScrollView(scrollDirection: Axis.horizontal, child: Row(children: List.generate(5, (i) => GestureDetector( onTap: () => setState(() => _day = i), child: Container( margin: const EdgeInsets.only(right: 6), padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8), decoration: BoxDecoration( color: _day == i ? _blue.withOpacity(0.2) : Colors.white.withOpacity(0.05), borderRadius: BorderRadius.circular(20), border: Border.all(color: _day == i ? _blue : Colors.white.withOpacity(0.1))), child: Text(_weekDays[i], style: TextStyle(color: _day == i ? _blue : Colors.white60, fontSize: 12)), ), ))), ), const SizedBox(height: 12), // Refeição const Text('Refeição', style: TextStyle(color: Color(0xFF888888), fontSize: 12)), const SizedBox(height: 6), SingleChildScrollView(scrollDirection: Axis.horizontal, child: Row(children: List.generate(_mealNames.length, (i) => GestureDetector( onTap: () => setState(() => _meal = i), child: Container( margin: const EdgeInsets.only(right: 6), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: _meal == i ? _amber.withOpacity(0.2) : Colors.white.withOpacity(0.05), borderRadius: BorderRadius.circular(20), border: Border.all(color: _meal == i ? _amber : Colors.white.withOpacity(0.1))), child: Row(children: [ Icon(_mealIcons[i], size: 14, color: _meal == i ? _amber : Colors.white38), const SizedBox(width: 4), Text(_mealNames[i], style: TextStyle(color: _meal == i ? _amber : Colors.white60, fontSize: 12)), ]), ), ))), ), const SizedBox(height: 14), TextField( controller: _descCtrl, style: const TextStyle(color: Colors.white), maxLines: 2, decoration: InputDecoration( hintText: 'Ex: Arroz com frango e legumes, sumo natural', hintStyle: const TextStyle(color: Color(0xFF555555), fontSize: 13), filled: true, fillColor: Colors.white.withOpacity(0.04), border: OutlineInputBorder(borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.white.withOpacity(0.09))), contentPadding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12), ), ), const SizedBox(height: 16), GestureDetector( onTap: _saving ? null : _save, child: Container( height: 50, width: double.infinity, decoration: BoxDecoration( gradient: const LinearGradient(colors: [_blue, Color(0xFF0288D1)]), borderRadius: BorderRadius.circular(12)), child: Center(child: _saving ? const SizedBox(width: 20, height: 20, child: CircularProgressIndicator(color: Colors.white, strokeWidth: 2)) : const Text('Publicar', style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 15))), ), ), ]), ); } Widget _emptyMenu() => Center(child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.restaurant_menu, size: 60, color: Colors.white.withOpacity(0.08)), const SizedBox(height: 12), const Text('Sem ementa publicada para esta semana', style: TextStyle(color: Color(0xFF888888), fontSize: 13)), const SizedBox(height: 4), const Text('A diretora ainda não publicou o cardápio.', style: TextStyle(color: Color(0xFF555555), fontSize: 11)), ])); Widget _err(String msg) => Center(child: Text(msg, style: const TextStyle(color: _red)));