import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:nethive_neo/providers/nethive/componentes_provider.dart'; import 'package:nethive_neo/pages/infrastructure/widgets/edit_componente_dialog.dart'; import 'package:nethive_neo/theme/theme.dart'; class ComponentesCardsView extends StatefulWidget { const ComponentesCardsView({Key? key}) : super(key: key); @override State createState() => _ComponentesCardsViewState(); } class _ComponentesCardsViewState extends State with TickerProviderStateMixin { late AnimationController _animationController; late Animation _fadeAnimation; @override void initState() { super.initState(); _animationController = AnimationController( duration: const Duration(milliseconds: 800), vsync: this, ); _fadeAnimation = Tween( begin: 0.0, end: 1.0, ).animate(CurvedAnimation( parent: _animationController, curve: Curves.easeInOut, )); _animationController.forward(); } @override void dispose() { _animationController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return FadeTransition( opacity: _fadeAnimation, child: Consumer( builder: (context, componentesProvider, child) { if (componentesProvider.componentes.isEmpty) { return _buildEmptyState(); } return Container( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header con filtros _buildMobileHeader(componentesProvider), const SizedBox(height: 16), // Lista de tarjetas Expanded( child: ListView.builder( itemCount: componentesProvider.componentes.length, itemBuilder: (context, index) { final componente = componentesProvider.componentes[index]; return TweenAnimationBuilder( duration: Duration(milliseconds: 300 + (index * 100)), tween: Tween(begin: 0.0, end: 1.0), builder: (context, value, child) { return Transform.translate( offset: Offset(0, 30 * (1 - value)), child: Opacity( opacity: value, child: _buildComponenteCard( componente, componentesProvider), ), ); }, ); }, ), ), ], ), ); }, ), ); } Widget _buildMobileHeader(ComponentesProvider componentesProvider) { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( gradient: AppTheme.of(context).primaryGradient, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: AppTheme.of(context).primaryColor.withOpacity(0.3), blurRadius: 10, offset: const Offset(0, 4), ), ], ), child: Column( children: [ Row( children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: Colors.white.withOpacity(0.2), borderRadius: BorderRadius.circular(8), ), child: const Icon( Icons.inventory_2, color: Colors.white, size: 20, ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Inventario MDF/IDF', style: TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold, ), ), Text( '${componentesProvider.componentes.length} componentes', style: TextStyle( color: Colors.white.withOpacity(0.9), fontSize: 12, ), ), ], ), ), Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: Colors.white.withOpacity(0.2), borderRadius: BorderRadius.circular(12), ), child: const Text( 'Móvil', style: TextStyle( color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold, ), ), ), ], ), const SizedBox(height: 12), // Buscador móvil Container( decoration: BoxDecoration( color: Colors.white.withOpacity(0.2), borderRadius: BorderRadius.circular(8), ), child: TextField( style: const TextStyle(color: Colors.white), decoration: InputDecoration( hintText: 'Buscar componentes...', hintStyle: TextStyle(color: Colors.white.withOpacity(0.7)), prefixIcon: Icon( Icons.search, color: Colors.white.withOpacity(0.8), size: 20, ), border: InputBorder.none, contentPadding: const EdgeInsets.symmetric( horizontal: 16, vertical: 12, ), ), onChanged: (value) { componentesProvider.buscarComponentes(value); }, ), ), ], ), ); } Widget _buildComponenteCard( dynamic componente, ComponentesProvider componentesProvider) { // Buscar la categoría del componente final categoria = componentesProvider.categorias .where((cat) => cat.id == componente.categoriaId) .firstOrNull; return Container( margin: const EdgeInsets.only(bottom: 12), decoration: BoxDecoration( color: AppTheme.of(context).secondaryBackground, borderRadius: BorderRadius.circular(12), border: Border.all( color: AppTheme.of(context).primaryColor.withOpacity(0.2), ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 8, offset: const Offset(0, 2), ), ], ), child: Material( color: Colors.transparent, child: InkWell( onTap: () { _showComponenteDetails(componente, categoria); }, borderRadius: BorderRadius.circular(12), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Header de la tarjeta Row( children: [ // Imagen del componente Container( width: 50, height: 50, decoration: BoxDecoration( color: AppTheme.of(context).primaryColor.withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: componente.imagenUrl != null && componente.imagenUrl!.isNotEmpty ? ClipRRect( borderRadius: BorderRadius.circular(8), child: Image.network( componente.imagenUrl!, fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { return Icon( Icons.devices, color: AppTheme.of(context).primaryColor, size: 24, ); }, ), ) : Icon( Icons.devices, color: AppTheme.of(context).primaryColor, size: 24, ), ), const SizedBox(width: 12), // Info principal Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( componente.nombre, style: TextStyle( color: AppTheme.of(context).primaryText, fontSize: 16, fontWeight: FontWeight.bold, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), const SizedBox(height: 4), if (categoria != null) Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 2), decoration: BoxDecoration( color: AppTheme.of(context) .primaryColor .withOpacity(0.1), borderRadius: BorderRadius.circular(6), ), child: Text( categoria.nombre, style: TextStyle( color: AppTheme.of(context).primaryColor, fontSize: 10, fontWeight: FontWeight.w600, ), ), ), ], ), ), // Estados Column( children: [ Container( padding: const EdgeInsets.symmetric( horizontal: 6, vertical: 2), decoration: BoxDecoration( color: (componente.activo ? Colors.green : Colors.red) .withOpacity(0.1), borderRadius: BorderRadius.circular(6), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( componente.activo ? Icons.check_circle : Icons.cancel, color: componente.activo ? Colors.green : Colors.red, size: 10, ), const SizedBox(width: 2), Text( componente.activo ? 'Activo' : 'Inactivo', style: TextStyle( color: componente.activo ? Colors.green : Colors.red, fontSize: 9, fontWeight: FontWeight.bold, ), ), ], ), ), const SizedBox(height: 4), Container( padding: const EdgeInsets.symmetric( horizontal: 6, vertical: 2), decoration: BoxDecoration( color: (componente.enUso ? Colors.orange : Colors.grey) .withOpacity(0.1), borderRadius: BorderRadius.circular(6), ), child: Text( componente.enUso ? 'En Uso' : 'Libre', style: TextStyle( color: componente.enUso ? Colors.orange : Colors.grey, fontSize: 9, fontWeight: FontWeight.bold, ), ), ), ], ), ], ), const SizedBox(height: 12), // Información adicional if (componente.descripcion != null && componente.descripcion!.isNotEmpty) Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: AppTheme.of(context).tertiaryBackground, borderRadius: BorderRadius.circular(6), ), child: Text( componente.descripcion!, style: TextStyle( color: AppTheme.of(context).secondaryText, fontSize: 12, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), ), const SizedBox(height: 8), // Footer con ubicación y acciones Row( children: [ if (componente.ubicacion != null && componente.ubicacion!.isNotEmpty) ...[ Icon( Icons.location_on, color: AppTheme.of(context).primaryColor, size: 14, ), const SizedBox(width: 4), Expanded( child: Text( componente.ubicacion!, style: TextStyle( color: AppTheme.of(context).secondaryText, fontSize: 11, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ), ] else Expanded( child: Text( 'Sin ubicación específica', style: TextStyle( color: AppTheme.of(context).secondaryText, fontSize: 11, fontStyle: FontStyle.italic, ), ), ), // Botones de acción Row( mainAxisSize: MainAxisSize.min, children: [ _buildActionButton( icon: Icons.visibility, color: Colors.blue, onTap: () => _showComponenteDetails(componente, categoria), ), const SizedBox(width: 4), _buildActionButton( icon: Icons.edit, color: AppTheme.of(context).primaryColor, onTap: () { _showEditComponenteDialog(componente); }, ), const SizedBox(width: 4), _buildActionButton( icon: Icons.delete, color: Colors.red, onTap: () => _confirmDelete(componente), ), ], ), ], ), ], ), ), ), ), ); } Widget _buildActionButton({ required IconData icon, required Color color, required VoidCallback onTap, }) { return InkWell( onTap: onTap, borderRadius: BorderRadius.circular(4), child: Container( padding: const EdgeInsets.all(6), decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(4), ), child: Icon( icon, color: color, size: 14, ), ), ); } Widget _buildEmptyState() { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: AppTheme.of(context).primaryColor.withOpacity(0.1), shape: BoxShape.circle, ), child: Icon( Icons.inventory_2, color: AppTheme.of(context).primaryColor, size: 48, ), ), const SizedBox(height: 16), Text( 'No hay componentes', style: TextStyle( color: AppTheme.of(context).primaryText, fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 8), Text( 'No se encontraron componentes\npara este negocio', textAlign: TextAlign.center, style: TextStyle( color: AppTheme.of(context).secondaryText, fontSize: 14, ), ), ], ), ); } void _showComponenteDetails(dynamic componente, dynamic categoria) { showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, builder: (context) => Container( height: MediaQuery.of(context).size.height * 0.8, decoration: BoxDecoration( color: AppTheme.of(context).primaryBackground, borderRadius: const BorderRadius.only( topLeft: Radius.circular(20), topRight: Radius.circular(20), ), ), child: Column( children: [ // Handle Container( margin: const EdgeInsets.only(top: 8), width: 40, height: 4, decoration: BoxDecoration( color: Colors.grey.withOpacity(0.5), borderRadius: BorderRadius.circular(2), ), ), // Header Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( gradient: AppTheme.of(context).primaryGradient, borderRadius: const BorderRadius.only( topLeft: Radius.circular(20), topRight: Radius.circular(20), ), ), child: Row( children: [ Container( width: 60, height: 60, decoration: BoxDecoration( color: Colors.white.withOpacity(0.2), borderRadius: BorderRadius.circular(12), ), child: componente.imagenUrl != null && componente.imagenUrl!.isNotEmpty ? ClipRRect( borderRadius: BorderRadius.circular(12), child: Image.network( componente.imagenUrl!, fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { return const Icon( Icons.devices, color: Colors.white, size: 30, ); }, ), ) : const Icon( Icons.devices, color: Colors.white, size: 30, ), ), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( componente.nombre, style: const TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold, ), ), if (categoria != null) Text( categoria.nombre, style: TextStyle( color: Colors.white.withOpacity(0.9), fontSize: 14, ), ), ], ), ), IconButton( onPressed: () => Navigator.pop(context), icon: const Icon(Icons.close, color: Colors.white), ), ], ), ), // Contenido Expanded( child: SingleChildScrollView( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildDetailRow( 'ID', componente.id.substring(0, 8) + '...'), _buildDetailRow( 'Estado', componente.activo ? 'Activo' : 'Inactivo'), _buildDetailRow('En Uso', componente.enUso ? 'Sí' : 'No'), if (componente.ubicacion != null && componente.ubicacion!.isNotEmpty) _buildDetailRow('Ubicación', componente.ubicacion!), if (componente.descripcion != null && componente.descripcion!.isNotEmpty) _buildDetailRow('Descripción', componente.descripcion!), _buildDetailRow( 'Fecha de Registro', componente.fechaRegistro?.toString().split(' ')[0] ?? 'No disponible'), ], ), ), ), ], ), ), ); } Widget _buildDetailRow(String label, String value) { return Container( margin: const EdgeInsets.only(bottom: 12), padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: AppTheme.of(context).secondaryBackground, borderRadius: BorderRadius.circular(8), border: Border.all( color: AppTheme.of(context).primaryColor.withOpacity(0.2), ), ), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( width: 100, child: Text( label, style: TextStyle( color: AppTheme.of(context).primaryColor, fontSize: 12, fontWeight: FontWeight.bold, ), ), ), Expanded( child: Text( value, style: TextStyle( color: AppTheme.of(context).primaryText, fontSize: 12, ), ), ), ], ), ); } void _confirmDelete(dynamic componente) { showDialog( context: context, builder: (context) => AlertDialog( backgroundColor: AppTheme.of(context).primaryBackground, title: const Text('Eliminar Componente'), content: Text('¿Deseas eliminar "${componente.nombre}"?'), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text( 'Cancelar', style: TextStyle(color: AppTheme.of(context).secondaryText), ), ), TextButton( onPressed: () { Navigator.pop(context); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Eliminar próximamente')), ); }, child: const Text('Eliminar', style: TextStyle(color: Colors.red)), ), ], ), ); } void _showEditComponenteDialog(dynamic componente) { final provider = Provider.of(context, listen: false); showDialog( context: context, builder: (context) => EditComponenteDialog( provider: provider, componente: componente, ), ); } }