diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index af19bac..7d1f5df 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -292,210 +292,18 @@ class _HomeScreenState extends State { } Future _showCreateCategoryDialog([Category? category]) async { - final TextEditingController controller = TextEditingController(text: category?.name ?? ''); - Color? selectedColor = category != null && category.colorValue != null - ? Color(category.colorValue!) - : null; - IconData? selectedIcon; - - final List palette = [ - Colors.amber, - Colors.blue, - Colors.green, - Colors.purple, - Colors.red, - Colors.teal, - Colors.orange, - Colors.grey, - ]; - - final List icons = [ - Icons.folder, - Icons.work, - Icons.star, - Icons.home, - Icons.school, - Icons.book, - Icons.music_note, - Icons.lightbulb, - ]; - - if (category != null && category.iconCodePoint != null) { - selectedIcon = icons.firstWhere( - (i) => i.codePoint == category.iconCodePoint, - orElse: () => icons.first, - ); - } - - final bool? result = await showDialog( + await showDialog( context: context, builder: (BuildContext context) { - return StatefulBuilder( - builder: (BuildContext ctx, StateSetter setState) { - return AlertDialog( - title: Text(category == null ? 'Crear categoría' : 'Editar categoría'), - content: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - TextField( - controller: controller, - decoration: const InputDecoration( - hintText: 'Nombre de la categoría', - ), - ), - const SizedBox(height: 12), - Align( - alignment: Alignment.centerLeft, - child: Text( - 'Color', - style: const TextStyle( - fontSize: 14, - color: Colors.white70, - ), - ), - ), - const SizedBox(height: 8), - Wrap( - spacing: 8, - children: palette.map((Color color) { - final bool isSelected = selectedColor == color; - return GestureDetector( - onTap: () => setState(() => selectedColor = color), - child: Container( - width: 36, - height: 36, - decoration: BoxDecoration( - color: color, - borderRadius: BorderRadius.circular(6), - border: isSelected - ? Border.all(color: Colors.white, width: 2) - : null, - ), - ), - ); - }).toList(), - ), - const SizedBox(height: 12), - Align( - alignment: Alignment.centerLeft, - child: Text( - 'Icono', - style: const TextStyle( - fontSize: 14, - color: Colors.white70, - ), - ), - ), - const SizedBox(height: 8), - Wrap( - spacing: 8, - children: icons.map((IconData icon) { - final bool isSelected = selectedIcon == icon; - return GestureDetector( - onTap: () => setState(() => selectedIcon = icon), - child: Container( - width: 40, - height: 40, - decoration: BoxDecoration( - color: isSelected - ? Colors.white10 - : Colors.transparent, - borderRadius: BorderRadius.circular(6), - ), - child: Icon( - icon, - color: isSelected ? Colors.white : Colors.white70, - ), - ), - ); - }).toList(), - ), - ], - ), - ), - actions: [ - if (category != null) - TextButton( - onPressed: () async { - final bool? confirm = await showDialog( - context: context, - builder: (context) => AlertDialog( - title: const Text('Borrar categoría'), - content: const Text('¿Seguro que quieres borrar esta categoría?'), - actions: [ - TextButton(onPressed: () => Navigator.pop(context, false), child: const Text('Cancelar')), - TextButton(onPressed: () => Navigator.pop(context, true), child: const Text('Borrar')), - ], - ), - ); - - if (confirm == true) { - try { - await widget.repository.deleteCategory(category.id); - await _loadCategories(); - if (mounted) { - ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Categoría borrada'))); - } - try { - await widget.onRequestSync(); - } catch (_) {} - } catch (e) { - if (mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Error al borrar categoría: $e'))); - } - Navigator.pop(context, false); - } - }, - child: const Text('Borrar', style: TextStyle(color: Colors.red)), - ), - TextButton( - onPressed: () => Navigator.pop(context, false), - child: const Text('Cancelar'), - ), - TextButton( - onPressed: () => Navigator.pop(context, true), - child: Text(category == null ? 'Crear' : 'Guardar'), - ), - ], - ); - }, + return _CategoryDialog( + category: category, + repository: widget.repository, + onCategoriesChanged: _loadCategories, + onRequestSync: widget.onRequestSync, + onCategoryDeleted: () => _handleMenuItemTapped('all_notes'), ); }, ); - - if (result != true || controller.text.trim().isEmpty) { - controller.dispose(); - return; - } - - try { - final Category newCategory = Category( - id: category?.id, - name: controller.text.trim(), - updatedAt: DateTime.now(), - colorValue: selectedColor?.toARGB32(), - iconCodePoint: selectedIcon?.codePoint, - ); - if (category == null) { - debugPrint('Creating category: ${newCategory.name}, color: ${newCategory.colorValue}, icon: ${newCategory.iconCodePoint}'); - } else { - debugPrint('Updating category: ${newCategory.name}, color: ${newCategory.colorValue}, icon: ${newCategory.iconCodePoint}'); - } - await widget.repository.createCategory(newCategory); - await _loadCategories(); - - try { - await widget.onRequestSync(); - } catch (_) {} - } catch (e) { - debugPrint('ERROR creating category: $e'); - if (mounted) { - ScaffoldMessenger.of( - context, - ).showSnackBar(SnackBar(content: Text('Error al crear categoría: $e'))); - } - } - controller.dispose(); } @override @@ -919,7 +727,8 @@ class _HomeScreenState extends State { ? 'deleted_notes' : 'all_notes'), categories: _categories, - onEditCategory: (Category c) => _showCreateCategoryDialog(c), + onEditCategory: (Category c) => + _showCreateCategoryDialog(c), onCreateCategory: _showCreateCategoryDialog, ), ), @@ -995,3 +804,252 @@ class _EmptyState extends StatelessWidget { ); } } + +class _CategoryDialog extends StatefulWidget { + const _CategoryDialog({ + required this.category, + required this.repository, + required this.onCategoriesChanged, + required this.onRequestSync, + required this.onCategoryDeleted, + }); + + final Category? category; + final NoteRepository repository; + final Future Function() onCategoriesChanged; + final Future Function() onRequestSync; + final Future Function() onCategoryDeleted; + + @override + State<_CategoryDialog> createState() => _CategoryDialogState(); +} + +class _CategoryDialogState extends State<_CategoryDialog> { + late final TextEditingController _controller; + Color? _selectedColor; + IconData? _selectedIcon; + + final List _palette = [ + Colors.amber, + Colors.blue, + Colors.green, + Colors.purple, + Colors.red, + Colors.teal, + Colors.orange, + Colors.grey, + ]; + + final List _icons = [ + Icons.folder, + Icons.work, + Icons.star, + Icons.home, + Icons.school, + Icons.book, + Icons.music_note, + Icons.lightbulb, + ]; + + @override + void initState() { + super.initState(); + _controller = TextEditingController(text: widget.category?.name ?? ''); + _selectedColor = + widget.category != null && widget.category!.colorValue != null + ? Color(widget.category!.colorValue!) + : null; + if (widget.category != null && widget.category!.iconCodePoint != null) { + _selectedIcon = _icons.firstWhere( + (IconData icon) => icon.codePoint == widget.category!.iconCodePoint, + orElse: () => _icons.first, + ); + } + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + Future _saveCategory() async { + final String name = _controller.text.trim(); + if (name.isEmpty) { + return; + } + + try { + final Category newCategory = Category( + id: widget.category?.id, + name: name, + updatedAt: DateTime.now(), + colorValue: _selectedColor?.toARGB32(), + iconCodePoint: _selectedIcon?.codePoint, + ); + if (widget.category == null) { + debugPrint( + 'Creating category: ${newCategory.name}, color: ${newCategory.colorValue}, icon: ${newCategory.iconCodePoint}', + ); + } else { + debugPrint( + 'Updating category: ${newCategory.name}, color: ${newCategory.colorValue}, icon: ${newCategory.iconCodePoint}', + ); + } + await widget.repository.createCategory(newCategory); + await widget.onCategoriesChanged(); + + try { + await widget.onRequestSync(); + } catch (_) {} + if (mounted) { + await widget.onCategoryDeleted(); + Navigator.pop(context); + } + } catch (e) { + debugPrint('ERROR creating category: $e'); + if (mounted) { + ScaffoldMessenger.of( + context, + ).showSnackBar(SnackBar(content: Text('Error al crear categoría: $e'))); + } + } + } + + Future _deleteCategory() async { + final bool? confirm = await showDialog( + context: context, + builder: (BuildContext context) => AlertDialog( + title: const Text('Borrar categoría'), + content: const Text('¿Seguro que quieres borrar esta categoría?'), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context, false), + child: const Text('Cancelar'), + ), + TextButton( + onPressed: () => Navigator.pop(context, true), + child: const Text('Borrar'), + ), + ], + ), + ); + + if (confirm != true) { + return; + } + + try { + await widget.repository.deleteCategory(widget.category!.id); + await widget.onCategoriesChanged(); + try { + await widget.onRequestSync(); + } catch (_) {} + if (mounted) { + Navigator.pop(context); + } + } catch (e) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('Error al borrar categoría: $e')), + ); + } + } + } + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text( + widget.category == null ? 'Crear categoría' : 'Editar categoría', + ), + content: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + controller: _controller, + decoration: const InputDecoration( + hintText: 'Nombre de la categoría', + ), + ), + const SizedBox(height: 12), + Align( + alignment: Alignment.centerLeft, + child: Text( + 'Color', + style: const TextStyle(fontSize: 14, color: Colors.white70), + ), + ), + const SizedBox(height: 8), + Wrap( + spacing: 8, + children: _palette.map((Color color) { + final bool isSelected = _selectedColor == color; + return GestureDetector( + onTap: () => setState(() => _selectedColor = color), + child: Container( + width: 36, + height: 36, + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(6), + border: isSelected + ? Border.all(color: Colors.white, width: 2) + : null, + ), + ), + ); + }).toList(), + ), + const SizedBox(height: 12), + Align( + alignment: Alignment.centerLeft, + child: Text( + 'Icono', + style: const TextStyle(fontSize: 14, color: Colors.white70), + ), + ), + const SizedBox(height: 8), + Wrap( + spacing: 8, + children: _icons.map((IconData icon) { + final bool isSelected = _selectedIcon == icon; + return GestureDetector( + onTap: () => setState(() => _selectedIcon = icon), + child: Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: isSelected ? Colors.white10 : Colors.transparent, + borderRadius: BorderRadius.circular(6), + ), + child: Icon( + icon, + color: isSelected ? Colors.white : Colors.white70, + ), + ), + ); + }).toList(), + ), + ], + ), + ), + actions: [ + if (widget.category != null) + TextButton( + onPressed: _deleteCategory, + child: const Text('Borrar', style: TextStyle(color: Colors.red)), + ), + TextButton( + onPressed: () => Navigator.pop(context), + child: const Text('Cancelar'), + ), + TextButton( + onPressed: _saveCategory, + child: Text(widget.category == null ? 'Crear' : 'Guardar'), + ), + ], + ); + } +}