import 'package:flutter/material.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import '../constants/item_categories.dart'; class WeekScreen extends StatefulWidget { const WeekScreen({super.key}); @override State createState() => _WeekScreenState(); } class _WeekScreenState extends State { DateTime _selectedDay = DateTime.now(); List> _dayItems = []; bool _isLoading = false; static const _weekdayNames = [ 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb', 'Dom', ]; static const _weekdayNamesLong = [ 'Segunda', 'Terça', 'Quarta', 'Quinta', 'Sexta', 'Sábado', 'Domingo', ]; @override void initState() { super.initState(); _loadDayItems(); } DateTime get _startOfWeek { final now = DateTime.now(); return DateTime( now.year, now.month, now.day, ).subtract(Duration(days: now.weekday - 1)); } String _dateKey(DateTime d) => '${d.year.toString().padLeft(4, '0')}-' '${d.month.toString().padLeft(2, '0')}-' '${d.day.toString().padLeft(2, '0')}'; Future _getOrCreatePlanId(DateTime day) async { final user = Supabase.instance.client.auth.currentUser!; final dateStr = _dateKey(day); final existing = await Supabase.instance.client .from('plans') .select('id') .eq('user_id', user.id) .eq('data', dateStr) .maybeSingle(); if (existing != null) return existing['id'] as int; final created = await Supabase.instance.client .from('plans') .insert({'user_id': user.id, 'data': dateStr}) .select() .single(); return created['id'] as int; } Future _loadDayItems() async { setState(() => _isLoading = true); try { final user = Supabase.instance.client.auth.currentUser; if (user == null) return; final dateStr = _dateKey(_selectedDay); final plan = await Supabase.instance.client .from('plans') .select('id, plan_items(item_id, items(*, item_images(image_url)))') .eq('user_id', user.id) .eq('data', dateStr) .maybeSingle(); if (plan == null) { setState(() { _dayItems = []; _isLoading = false; }); return; } final planItems = plan['plan_items'] as List? ?? []; final items = planItems .where((pi) => pi['items'] != null) .map>( (pi) => Map.from(pi['items']), ) .toList(); setState(() { _dayItems = items; _isLoading = false; }); } catch (e) { debugPrint('Error loading day items: $e'); setState(() => _isLoading = false); } } Future _addItemsToDay() async { final user = Supabase.instance.client.auth.currentUser; if (user == null) return; final allItems = await Supabase.instance.client .from('items') .select('*, item_images(image_url)') .eq('user_id', user.id); final available = List>.from(allItems); final existingIds = _dayItems.map((i) => i['id']).toSet(); final toShow = available .where((i) => !existingIds.contains(i['id'])) .toList(); if (!mounted) return; final selected = await showModalBottomSheet>( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, builder: (ctx) => _ItemPickerSheet(items: toShow), ); if (selected == null || selected.isEmpty) return; try { final planId = await _getOrCreatePlanId(_selectedDay); final rows = selected .map((id) => {'plan_id': planId, 'item_id': id}) .toList(); await Supabase.instance.client.from('plan_items').insert(rows); _loadDayItems(); } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Erro: $e'), backgroundColor: Colors.red), ); } } } Future _removeItem(Map item) async { try { final planId = await _getOrCreatePlanId(_selectedDay); await Supabase.instance.client .from('plan_items') .delete() .eq('plan_id', planId) .eq('item_id', item['id']); _loadDayItems(); } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Erro: $e'), backgroundColor: Colors.red), ); } } } @override Widget build(BuildContext context) { final start = _startOfWeek; final days = List.generate(7, (i) => start.add(Duration(days: i))); return Scaffold( backgroundColor: const Color(0xFFFFE5CC), appBar: AppBar( backgroundColor: const Color(0xFF0066CC), elevation: 0, title: const Text( 'Minha Semana', style: TextStyle(color: Colors.white, fontSize: 20), ), centerTitle: true, ), body: Column( children: [ // Week selector Container( padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 8), child: Row( children: days.map((day) { final isSelected = day.year == _selectedDay.year && day.month == _selectedDay.month && day.day == _selectedDay.day; final isToday = day.year == DateTime.now().year && day.month == DateTime.now().month && day.day == DateTime.now().day; return Expanded( child: GestureDetector( onTap: () { setState(() => _selectedDay = day); _loadDayItems(); }, child: Container( margin: const EdgeInsets.symmetric(horizontal: 3), padding: const EdgeInsets.symmetric(vertical: 10), decoration: BoxDecoration( color: isSelected ? const Color(0xFF0066CC) : Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all( color: isToday ? const Color(0xFF0066CC) : Colors.transparent, width: 2, ), ), child: Column( children: [ Text( _weekdayNames[day.weekday - 1], style: TextStyle( fontSize: 12, color: isSelected ? Colors.white : const Color(0xFF666666), ), ), const SizedBox(height: 4), Text( '${day.day}', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: isSelected ? Colors.white : const Color(0xFF333333), ), ), ], ), ), ), ); }).toList(), ), ), // Day title Padding( padding: const EdgeInsets.fromLTRB(20, 8, 20, 12), child: Row( children: [ Text( '${_weekdayNamesLong[_selectedDay.weekday - 1]}, ' '${_selectedDay.day}/${_selectedDay.month}', style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Color(0xFF333333), ), ), const Spacer(), Text( '${_dayItems.length} ${_dayItems.length == 1 ? "item" : "itens"}', style: const TextStyle( color: Color(0xFF666666), fontSize: 13, ), ), ], ), ), // Items list Expanded( child: _isLoading ? const Center(child: CircularProgressIndicator()) : _dayItems.isEmpty ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( Icons.calendar_today_outlined, size: 56, color: Colors.grey[400], ), const SizedBox(height: 12), const Text( 'Nenhum item para este dia', style: TextStyle( color: Color(0xFF666666), fontSize: 16, ), ), const SizedBox(height: 4), const Text( 'Toque em + para adicionar', style: TextStyle( color: Color(0xFF999999), fontSize: 13, ), ), ], ), ) : ListView.builder( padding: const EdgeInsets.symmetric(horizontal: 16), itemCount: _dayItems.length, itemBuilder: (_, i) => _buildItemTile(_dayItems[i]), ), ), ], ), floatingActionButton: FloatingActionButton( backgroundColor: const Color(0xFF0066CC), onPressed: _addItemsToDay, child: const Icon(Icons.add, color: Colors.white), ), ); } Widget _buildItemTile(Map item) { final images = item['item_images'] as List?; final imageUrl = (images != null && images.isNotEmpty) ? images.first['image_url'] : null; final category = itemCategories.firstWhere( (c) => c.id == item['categoria'], orElse: () => itemCategories.last, ); return Card( margin: const EdgeInsets.only(bottom: 10), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)), child: ListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), leading: ClipRRect( borderRadius: BorderRadius.circular(10), child: imageUrl != null ? Image.network( imageUrl, width: 56, height: 56, fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) => _icon(category.icon), ) : _icon(category.icon), ), title: Text( item['nome'] ?? 'Sem nome', style: const TextStyle(fontWeight: FontWeight.bold), ), subtitle: Text(category.name), trailing: IconButton( icon: const Icon(Icons.close, color: Colors.red), onPressed: () => _removeItem(item), ), ), ); } Widget _icon(String icon) { return Container( width: 56, height: 56, color: const Color(0xFF0066CC).withValues(alpha: 0.1), alignment: Alignment.center, child: Text(icon, style: const TextStyle(fontSize: 26)), ); } } // ============================================= // Bottom sheet para escolher itens // ============================================= class _ItemPickerSheet extends StatefulWidget { final List> items; const _ItemPickerSheet({required this.items}); @override State<_ItemPickerSheet> createState() => _ItemPickerSheetState(); } class _ItemPickerSheetState extends State<_ItemPickerSheet> { final Set _selected = {}; String _query = ''; @override Widget build(BuildContext context) { final filtered = widget.items .where( (i) => (i['nome'] ?? '').toString().toLowerCase().contains( _query.toLowerCase(), ), ) .toList(); return DraggableScrollableSheet( initialChildSize: 0.85, maxChildSize: 0.95, minChildSize: 0.5, expand: false, builder: (_, scrollController) => Container( decoration: const BoxDecoration( color: Color(0xFFFFE5CC), borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), child: Column( children: [ Container( margin: const EdgeInsets.symmetric(vertical: 8), width: 40, height: 4, decoration: BoxDecoration( color: Colors.grey[400], borderRadius: BorderRadius.circular(2), ), ), const Padding( padding: EdgeInsets.symmetric(horizontal: 16, vertical: 4), child: Text( 'Adicionar itens ao dia', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), ), Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(14), ), child: TextField( onChanged: (v) => setState(() => _query = v), decoration: const InputDecoration( prefixIcon: Icon(Icons.search), hintText: 'Pesquisar...', border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 12), ), ), ), ), Expanded( child: filtered.isEmpty ? const Center(child: Text('Nenhum item disponível')) : ListView.builder( controller: scrollController, itemCount: filtered.length, itemBuilder: (_, i) { final item = filtered[i]; final id = item['id'] as int; final selected = _selected.contains(id); final images = item['item_images'] as List?; final imageUrl = (images != null && images.isNotEmpty) ? images.first['image_url'] : null; final category = itemCategories.firstWhere( (c) => c.id == item['categoria'], orElse: () => itemCategories.last, ); return CheckboxListTile( value: selected, activeColor: const Color(0xFF0066CC), onChanged: (v) { setState(() { if (v == true) { _selected.add(id); } else { _selected.remove(id); } }); }, title: Text(item['nome'] ?? ''), subtitle: Text(category.name), secondary: ClipRRect( borderRadius: BorderRadius.circular(8), child: imageUrl != null ? Image.network( imageUrl, width: 48, height: 48, fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) => Container( width: 48, height: 48, color: const Color( 0xFF0066CC, ).withValues(alpha: 0.1), alignment: Alignment.center, child: Text(category.icon), ), ) : Container( width: 48, height: 48, color: const Color( 0xFF0066CC, ).withValues(alpha: 0.1), alignment: Alignment.center, child: Text(category.icon), ), ), ); }, ), ), SafeArea( child: Padding( padding: const EdgeInsets.all(16), child: SizedBox( width: double.infinity, height: 50, child: ElevatedButton( onPressed: _selected.isEmpty ? null : () => Navigator.pop(context, _selected.toList()), style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF0066CC), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), ), child: Text( 'Adicionar (${_selected.length})', style: const TextStyle(color: Colors.white, fontSize: 16), ), ), ), ), ), ], ), ), ); } }