import 'package:flutter/material.dart'; import 'package:supabase_flutter/supabase_flutter.dart'; import '../constants/item_categories.dart'; import 'add_item_screen.dart'; class ItemScreen extends StatefulWidget { const ItemScreen({super.key}); @override State createState() => _ItemScreenState(); } class _ItemScreenState extends State { List> _items = []; bool _isLoading = true; String _searchQuery = ''; String? _selectedCategoryFilter; List> get _filteredItems { return _items.where((item) { final name = (item['nome'] ?? '').toString().toLowerCase(); final tags = List.from( item['tags'] ?? [], ).join(' ').toLowerCase(); final matchesSearch = _searchQuery.isEmpty || name.contains(_searchQuery.toLowerCase()) || tags.contains(_searchQuery.toLowerCase()); final matchesCategory = _selectedCategoryFilter == null || item['categoria'] == _selectedCategoryFilter; return matchesSearch && matchesCategory; }).toList(); } @override void initState() { super.initState(); _loadItems(); } Future _loadItems() async { setState(() => _isLoading = true); try { final user = Supabase.instance.client.auth.currentUser; if (user == null) return; final response = await Supabase.instance.client .from('items') .select('*, item_images(image_url)') .eq('user_id', user.id) .order('id', ascending: false); setState(() { _items = List>.from(response); _isLoading = false; }); } catch (e) { print('Error loading items: $e'); setState(() => _isLoading = false); } } String? _imageUrl(Map item) { final images = item['item_images'] as List?; if (images != null && images.isNotEmpty) { return images.first['image_url'] as String?; } return null; } String _categoryName(String? id) { if (id == null) return 'Outros'; return ITEM_CATEGORIES .firstWhere((c) => c.id == id, orElse: () => ITEM_CATEGORIES.last) .name; } String _categoryIcon(String? id) { if (id == null) return '📦'; return ITEM_CATEGORIES .firstWhere((c) => c.id == id, orElse: () => ITEM_CATEGORIES.last) .icon; } Future _deleteItem(Map item) async { final confirmed = await showDialog( context: context, builder: (ctx) => AlertDialog( title: const Text('Apagar item'), content: Text('Tem certeza que deseja apagar "${item['nome']}"?'), actions: [ TextButton( onPressed: () => Navigator.pop(ctx, false), child: const Text('Cancelar'), ), TextButton( onPressed: () => Navigator.pop(ctx, true), style: TextButton.styleFrom(foregroundColor: Colors.red), child: const Text('Apagar'), ), ], ), ); if (confirmed != true) return; try { await Supabase.instance.client .from('item_images') .delete() .eq('item_id', item['id']); await Supabase.instance.client .from('items') .delete() .eq('id', item['id']); _loadItems(); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Item apagado'), backgroundColor: Colors.green, ), ); } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Erro: $e'), backgroundColor: Colors.red), ); } } } void _viewItem(Map item) { Navigator.push( context, MaterialPageRoute( builder: (_) => ItemDetailScreen(item: item, imageUrl: _imageUrl(item)), ), ).then((_) => _loadItems()); } void _editItem(Map item) { Navigator.push( context, MaterialPageRoute(builder: (_) => EditItemScreen(item: item)), ).then((_) => _loadItems()); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFFFFE5CC), appBar: AppBar( backgroundColor: const Color(0xFF0066CC), elevation: 0, title: const Text( 'Meus Itens', style: TextStyle(color: Colors.white, fontSize: 20), ), centerTitle: true, ), body: _isLoading ? const Center(child: CircularProgressIndicator()) : Column( children: [ _buildSearchAndFilters(), Expanded( child: _items.isEmpty ? _buildEmpty() : _filteredItems.isEmpty ? const Center( child: Text( 'Nenhum item encontrado', style: TextStyle(color: Color(0xFF666666)), ), ) : RefreshIndicator( onRefresh: _loadItems, child: ListView.builder( padding: const EdgeInsets.all(16), itemCount: _filteredItems.length, itemBuilder: (context, i) => _buildItemCard(_filteredItems[i]), ), ), ), ], ), floatingActionButton: FloatingActionButton( backgroundColor: const Color(0xFF0066CC), onPressed: () { Navigator.push( context, MaterialPageRoute(builder: (_) => const AddItemScreen()), ).then((_) => _loadItems()); }, child: const Icon(Icons.add, color: Colors.white), ), ); } Widget _buildSearchAndFilters() { return Container( padding: const EdgeInsets.fromLTRB(16, 12, 16, 8), color: const Color(0xFFFFE5CC), child: Column( children: [ Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), ), child: TextField( onChanged: (v) => setState(() => _searchQuery = v), decoration: const InputDecoration( prefixIcon: Icon(Icons.search, color: Color(0xFF666666)), hintText: 'Pesquisar por nome ou tag...', border: InputBorder.none, contentPadding: EdgeInsets.symmetric( horizontal: 12, vertical: 14, ), ), ), ), const SizedBox(height: 8), SizedBox( height: 38, child: ListView( scrollDirection: Axis.horizontal, children: [ _categoryChip(null, 'Todos', '🗂'), ...ITEM_CATEGORIES.map( (c) => _categoryChip(c.id, c.name, c.icon), ), ], ), ), ], ), ); } Widget _categoryChip(String? id, String name, String icon) { final selected = _selectedCategoryFilter == id; return Padding( padding: const EdgeInsets.only(right: 8), child: GestureDetector( onTap: () => setState(() => _selectedCategoryFilter = id), child: Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: selected ? const Color(0xFF0066CC) : Colors.white, borderRadius: BorderRadius.circular(20), border: Border.all( color: selected ? const Color(0xFF0066CC) : const Color(0xFFE0E0E0), ), ), child: Row( children: [ Text(icon, style: const TextStyle(fontSize: 14)), const SizedBox(width: 6), Text( name, style: TextStyle( color: selected ? Colors.white : const Color(0xFF333333), fontWeight: FontWeight.w500, ), ), ], ), ), ), ); } Widget _buildEmpty() { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.inventory_2_outlined, size: 64, color: Colors.grey[400]), const SizedBox(height: 16), const Text( 'Nenhum item ainda', style: TextStyle(fontSize: 18, color: Color(0xFF666666)), ), const SizedBox(height: 8), const Text( 'Toque no + para adicionar', style: TextStyle(fontSize: 14, color: Color(0xFF999999)), ), ], ), ); } Widget _buildItemCard(Map item) { final categoryName = _categoryName(item['categoria']); final categoryIcon = _categoryIcon(item['categoria']); final tags = List.from(item['tags'] ?? []); final imageUrl = _imageUrl(item); return Card( margin: const EdgeInsets.only(bottom: 12), elevation: 2, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), child: InkWell( borderRadius: BorderRadius.circular(16), onTap: () => _viewItem(item), child: Padding( padding: const EdgeInsets.all(12), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ ClipRRect( borderRadius: BorderRadius.circular(12), child: imageUrl != null ? Image.network( imageUrl, width: 72, height: 72, fit: BoxFit.cover, errorBuilder: (_, __, ___) => _iconPlaceholder(categoryIcon), ) : _iconPlaceholder(categoryIcon), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( item['nome'] ?? 'Sem nome', style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Color(0xFF333333), ), ), const SizedBox(height: 2), Text( categoryName, style: const TextStyle( fontSize: 13, color: Color(0xFF666666), ), ), if (tags.isNotEmpty) ...[ const SizedBox(height: 6), Wrap( spacing: 4, runSpacing: 4, children: tags.take(3).map((tag) { return Container( padding: const EdgeInsets.symmetric( horizontal: 6, vertical: 2, ), decoration: BoxDecoration( color: const Color(0xFF0066CC).withOpacity(0.1), borderRadius: BorderRadius.circular(6), ), child: Text( tag, style: const TextStyle( fontSize: 10, color: Color(0xFF0066CC), ), ), ); }).toList(), ), ], ], ), ), PopupMenuButton( icon: const Icon(Icons.more_vert, color: Color(0xFF666666)), onSelected: (value) { if (value == 'view') _viewItem(item); if (value == 'edit') _editItem(item); if (value == 'delete') _deleteItem(item); }, itemBuilder: (_) => [ const PopupMenuItem( value: 'view', child: Row( children: [ Icon(Icons.visibility_outlined, size: 18), SizedBox(width: 8), Text('Ver'), ], ), ), const PopupMenuItem( value: 'edit', child: Row( children: [ Icon(Icons.edit_outlined, size: 18), SizedBox(width: 8), Text('Editar'), ], ), ), const PopupMenuItem( value: 'delete', child: Row( children: [ Icon(Icons.delete_outline, size: 18, color: Colors.red), SizedBox(width: 8), Text('Apagar', style: TextStyle(color: Colors.red)), ], ), ), ], ), ], ), ), ), ); } Widget _iconPlaceholder(String icon) { return Container( width: 72, height: 72, color: const Color(0xFF0066CC).withOpacity(0.1), alignment: Alignment.center, child: Text(icon, style: const TextStyle(fontSize: 32)), ); } } // ============================================= // Detalhe do item // ============================================= class ItemDetailScreen extends StatelessWidget { final Map item; final String? imageUrl; const ItemDetailScreen({super.key, required this.item, this.imageUrl}); @override Widget build(BuildContext context) { final categoryId = item['categoria'] as String?; final categoryName = categoryId == null ? 'Outros' : ITEM_CATEGORIES .firstWhere( (c) => c.id == categoryId, orElse: () => ITEM_CATEGORIES.last, ) .name; final tags = List.from(item['tags'] ?? []); return Scaffold( backgroundColor: const Color(0xFFFFE5CC), appBar: AppBar( backgroundColor: const Color(0xFF0066CC), title: Text( item['nome'] ?? 'Item', style: const TextStyle(color: Colors.white), ), iconTheme: const IconThemeData(color: Colors.white), ), body: SingleChildScrollView( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (imageUrl != null) ClipRRect( borderRadius: BorderRadius.circular(16), child: Image.network( imageUrl!, width: double.infinity, height: 250, fit: BoxFit.cover, errorBuilder: (_, __, ___) => const SizedBox.shrink(), ), ), const SizedBox(height: 20), _label('Nome'), _value(item['nome'] ?? ''), const SizedBox(height: 16), _label('Categoria'), _value(categoryName), if (tags.isNotEmpty) ...[ const SizedBox(height: 16), _label('Tags'), const SizedBox(height: 8), Wrap( spacing: 8, runSpacing: 8, children: tags .map( (t) => Chip( label: Text(t), backgroundColor: const Color( 0xFF0066CC, ).withOpacity(0.1), labelStyle: const TextStyle(color: Color(0xFF0066CC)), ), ) .toList(), ), ], ], ), ), ); } Widget _label(String t) => Text( t, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.bold, color: Color(0xFF666666), ), ); Widget _value(String t) => Container( width: double.infinity, margin: const EdgeInsets.only(top: 6), padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), ), child: Text(t, style: const TextStyle(fontSize: 16)), ); } // ============================================= // Editar item // ============================================= class EditItemScreen extends StatefulWidget { final Map item; const EditItemScreen({super.key, required this.item}); @override State createState() => _EditItemScreenState(); } class _EditItemScreenState extends State { late TextEditingController _nameController; ItemCategory? _selectedCategory; final Set _selectedTags = {}; bool _isLoading = false; @override void initState() { super.initState(); _nameController = TextEditingController(text: widget.item['nome'] ?? ''); final catId = widget.item['categoria'] as String?; if (catId != null) { _selectedCategory = ITEM_CATEGORIES.firstWhere( (c) => c.id == catId, orElse: () => ITEM_CATEGORIES.last, ); } final tags = List.from(widget.item['tags'] ?? []); _selectedTags.addAll(tags); } @override void dispose() { _nameController.dispose(); super.dispose(); } Future _save() async { if (_nameController.text.trim().isEmpty) { _snack('Nome não pode ser vazio', Colors.red); return; } setState(() => _isLoading = true); try { await Supabase.instance.client .from('items') .update({ 'nome': _nameController.text.trim(), 'categoria': _selectedCategory?.id, 'tags': _selectedTags.toList(), }) .eq('id', widget.item['id']); _snack('Item atualizado!', Colors.green); if (mounted) Navigator.pop(context); } catch (e) { _snack('Erro: $e', Colors.red); } finally { if (mounted) setState(() => _isLoading = false); } } void _snack(String msg, Color color) { ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text(msg), backgroundColor: color)); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFFFFE5CC), appBar: AppBar( backgroundColor: const Color(0xFF0066CC), title: const Text('Editar Item', style: TextStyle(color: Colors.white)), iconTheme: const IconThemeData(color: Colors.white), ), body: SingleChildScrollView( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), ), child: TextField( controller: _nameController, decoration: const InputDecoration( labelText: 'Nome do item', border: InputBorder.none, contentPadding: EdgeInsets.symmetric( horizontal: 16, vertical: 16, ), ), ), ), const SizedBox(height: 16), const Text( 'Categoria', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), ), const SizedBox(height: 8), Container( padding: const EdgeInsets.symmetric(horizontal: 12), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), ), child: DropdownButtonHideUnderline( child: DropdownButton( value: _selectedCategory, isExpanded: true, items: ITEM_CATEGORIES .map( (c) => DropdownMenuItem( value: c, child: Text('${c.icon} ${c.name}'), ), ) .toList(), onChanged: (v) => setState(() => _selectedCategory = v), ), ), ), const SizedBox(height: 16), const Text( 'Tags', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), ), const SizedBox(height: 8), Wrap( spacing: 8, runSpacing: 8, children: CONTEXT_TAGS.map((tag) { final selected = _selectedTags.contains(tag.id); return FilterChip( label: Text(tag.name), selected: selected, onSelected: (_) { setState(() { if (selected) { _selectedTags.remove(tag.id); } else { _selectedTags.add(tag.id); } }); }, selectedColor: const Color(0xFF0066CC).withOpacity(0.2), checkmarkColor: const Color(0xFF0066CC), backgroundColor: Colors.white, ); }).toList(), ), const SizedBox(height: 24), SizedBox( width: double.infinity, height: 52, child: ElevatedButton( onPressed: _isLoading ? null : _save, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF0066CC), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), ), child: _isLoading ? const CircularProgressIndicator(color: Colors.white) : const Text( 'Guardar alterações', style: TextStyle(color: Colors.white, fontSize: 16), ), ), ), ], ), ), ); } }