import 'dart:io'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import '../data/app_database.dart'; import '../models/work_order.dart'; import '../services/image_storage_service.dart'; import 'work_order_details_screen.dart'; import 'work_order_screen.dart'; enum _OrderFilter { all, open, completed } class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @override State createState() => _HomeScreenState(); } class _HomeScreenState extends State { final _database = AppDatabase.instance; final _imageStorage = ImageStorageService(); List _orders = const []; _OrderFilter _filter = _OrderFilter.all; bool _loading = true; String? _error; @override void initState() { super.initState(); _loadOrders(); } Future _loadOrders() async { setState(() { _loading = true; _error = null; }); try { final orders = await _database.getWorkOrders(); if (!mounted) return; setState(() { _orders = orders; _loading = false; }); } catch (_) { if (!mounted) return; setState(() { _loading = false; _error = 'No fue posible leer las órdenes guardadas.'; }); } } List get _visibleOrders { return switch (_filter) { _OrderFilter.all => _orders, _OrderFilter.open => _orders.where((order) => !order.isCompleted).toList(), _OrderFilter.completed => _orders.where((order) => order.isCompleted).toList(), }; } Future _openOrder([WorkOrder? order]) async { final details = await Navigator.of(context).push( MaterialPageRoute(builder: (_) => WorkOrderDetailsScreen(order: order)), ); if (!mounted || details == null) return; await Navigator.of(context).push( MaterialPageRoute( builder: (_) => WorkOrderScreen(order: order, details: details), ), ); await _loadOrders(); } Future _deleteOrder(WorkOrder order) async { final confirmed = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Eliminar orden'), content: Text( 'Se eliminará “${order.title}” junto con sus fotos y su informe. ' 'Esta acción no se puede deshacer.', ), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('Cancelar'), ), FilledButton( style: FilledButton.styleFrom( backgroundColor: Theme.of(context).colorScheme.error, ), onPressed: () => Navigator.pop(context, true), child: const Text('Eliminar'), ), ], ), ); if (confirmed != true) return; try { await _database.deleteWorkOrder(order.id); await _imageStorage.deleteWorkOrderImages(order.id); final pdfPath = order.pdfPath; if (pdfPath != null) { final report = File(pdfPath); if (await report.exists()) await report.delete(); } await _loadOrders(); if (!mounted) return; ScaffoldMessenger.of( context, ).showSnackBar(const SnackBar(content: Text('Orden eliminada.'))); } catch (_) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('No fue posible eliminar la orden.')), ); } } @override Widget build(BuildContext context) { final visibleOrders = _visibleOrders; return Scaffold( appBar: AppBar( toolbarHeight: 72, title: const _Brand(), actions: [ IconButton( tooltip: 'Actualizar', onPressed: _loading ? null : _loadOrders, icon: const Icon(Icons.refresh_rounded), ), const SizedBox(width: 8), ], ), body: RefreshIndicator( onRefresh: _loadOrders, child: CustomScrollView( physics: const AlwaysScrollableScrollPhysics(), slivers: [ SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.fromLTRB(20, 18, 20, 14), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Órdenes de trabajo', style: Theme.of(context).textTheme.headlineMedium ?.copyWith( color: const Color(0xFF163334), fontWeight: FontWeight.w800, letterSpacing: -.5, ), ), const SizedBox(height: 6), Text( 'Registra el mantenimiento y conserva su evidencia.', style: Theme.of(context).textTheme.bodyLarge?.copyWith( color: const Color(0xFF627372), ), ), const SizedBox(height: 20), _SummaryStrip(orders: _orders), const SizedBox(height: 18), SingleChildScrollView( scrollDirection: Axis.horizontal, child: SegmentedButton<_OrderFilter>( showSelectedIcon: false, segments: const [ ButtonSegment( value: _OrderFilter.all, label: Text('Todas'), ), ButtonSegment( value: _OrderFilter.open, label: Text('Borradores'), ), ButtonSegment( value: _OrderFilter.completed, label: Text('Finalizadas'), ), ], selected: {_filter}, onSelectionChanged: (selection) { setState(() => _filter = selection.first); }, ), ), ], ), ), ), if (_loading) const SliverFillRemaining( hasScrollBody: false, child: Center(child: CircularProgressIndicator()), ) else if (_error != null) SliverFillRemaining( hasScrollBody: false, child: _MessageState( icon: Icons.cloud_off_rounded, title: 'No pudimos cargar las órdenes', message: _error!, buttonLabel: 'Reintentar', onPressed: _loadOrders, ), ) else if (visibleOrders.isEmpty) SliverFillRemaining( hasScrollBody: false, child: _MessageState( icon: Icons.assignment_outlined, title: _orders.isEmpty ? 'Aún no hay órdenes' : 'No hay órdenes en este estado', message: _orders.isEmpty ? 'Crea tu primera OT y agrega las tareas del mantenimiento.' : 'Prueba seleccionando otro filtro.', ), ) else SliverPadding( padding: const EdgeInsets.fromLTRB(20, 4, 20, 110), sliver: SliverList.separated( itemCount: visibleOrders.length, separatorBuilder: (_, _) => const SizedBox(height: 12), itemBuilder: (context, index) { final order = visibleOrders[index]; return _OrderCard( order: order, onTap: () => _openOrder(order), onDelete: () => _deleteOrder(order), ); }, ), ), ], ), ), floatingActionButton: FloatingActionButton.extended( onPressed: () => _openOrder(), backgroundColor: const Color(0xFF0B5C5E), foregroundColor: Colors.white, icon: const Icon(Icons.add_rounded), label: const Text( 'Nueva OT', style: TextStyle(fontWeight: FontWeight.w700), ), ), ); } } class _Brand extends StatelessWidget { const _Brand(); @override Widget build(BuildContext context) { return Row( mainAxisSize: MainAxisSize.min, children: [ Container( width: 40, height: 40, decoration: BoxDecoration( color: const Color(0xFF0B5C5E), borderRadius: BorderRadius.circular(12), ), child: const Icon( Icons.handyman_rounded, color: Colors.white, size: 22, ), ), const SizedBox(width: 11), const Text('SMoya Gasfiter'), ], ); } } class _SummaryStrip extends StatelessWidget { const _SummaryStrip({required this.orders}); final List orders; @override Widget build(BuildContext context) { final completed = orders.where((order) => order.isCompleted).length; return Row( children: [ Expanded( child: _SummaryItem( value: '${orders.length - completed}', label: 'Borradores', icon: Icons.edit_note_rounded, color: const Color(0xFFF4B740), ), ), const SizedBox(width: 10), Expanded( child: _SummaryItem( value: '$completed', label: 'Finalizadas', icon: Icons.task_alt_rounded, color: const Color(0xFF2E8B70), ), ), ], ); } } class _SummaryItem extends StatelessWidget { const _SummaryItem({ required this.value, required this.label, required this.icon, required this.color, }); final String value; final String label; final IconData icon; final Color color; @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(15), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), border: Border.all(color: const Color(0xFFDCE7E5)), ), child: Row( children: [ Container( width: 38, height: 38, decoration: BoxDecoration( color: color.withValues(alpha: .15), borderRadius: BorderRadius.circular(11), ), child: Icon(icon, color: color, size: 21), ), const SizedBox(width: 11), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( value, style: const TextStyle( color: Color(0xFF163334), fontWeight: FontWeight.w800, fontSize: 20, height: 1, ), ), const SizedBox(height: 4), Text( label, style: const TextStyle(color: Color(0xFF627372), fontSize: 12), ), ], ), ], ), ); } } class _OrderCard extends StatelessWidget { const _OrderCard({ required this.order, required this.onTap, required this.onDelete, }); final WorkOrder order; final VoidCallback onTap; final VoidCallback onDelete; @override Widget build(BuildContext context) { final statusColor = order.isCompleted ? const Color(0xFF2E8B70) : const Color(0xFFB77800); final statusBackground = order.isCompleted ? const Color(0xFFE4F3ED) : const Color(0xFFFFF3D8); return Card( child: InkWell( onTap: onTap, borderRadius: BorderRadius.circular(18), child: Padding( padding: const EdgeInsets.fromLTRB(17, 17, 8, 17), child: Row( children: [ Container( width: 48, height: 48, decoration: BoxDecoration( color: const Color(0xFFE7F1F0), borderRadius: BorderRadius.circular(14), ), child: const Icon( Icons.assignment_outlined, color: Color(0xFF0B5C5E), ), ), const SizedBox(width: 14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Text( order.title, maxLines: 2, overflow: TextOverflow.ellipsis, style: const TextStyle( color: Color(0xFF163334), fontSize: 16, fontWeight: FontWeight.w700, ), ), ), const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric( horizontal: 9, vertical: 5, ), decoration: BoxDecoration( color: statusBackground, borderRadius: BorderRadius.circular(20), ), child: Text( order.isCompleted ? 'Finalizada' : 'Borrador', style: TextStyle( color: statusColor, fontSize: 11, fontWeight: FontWeight.w700, ), ), ), ], ), const SizedBox(height: 8), if (order.details.client.isNotEmpty) ...[ _OrderCardDetail( icon: Icons.person_outline_rounded, text: order.details.client, ), const SizedBox(height: 4), ], if (order.details.address.isNotEmpty) ...[ _OrderCardDetail( icon: Icons.location_on_outlined, text: order.details.address, ), const SizedBox(height: 7), ], Text( '${order.tasks.length} tarea(s) · ' '${order.totalPhotos} foto(s) · ' '${DateFormat('dd/MM/yyyy').format(order.updatedAt.toLocal())}', style: const TextStyle( color: Color(0xFF6A7A79), fontSize: 12, ), ), ], ), ), PopupMenuButton( tooltip: 'Más opciones', onSelected: (value) { if (value == 'delete') onDelete(); }, itemBuilder: (context) => const [ PopupMenuItem( value: 'delete', child: Row( children: [ Icon(Icons.delete_outline_rounded), SizedBox(width: 10), Text('Eliminar'), ], ), ), ], ), ], ), ), ), ); } } class _OrderCardDetail extends StatelessWidget { const _OrderCardDetail({required this.icon, required this.text}); final IconData icon; final String text; @override Widget build(BuildContext context) { return Row( children: [ Icon(icon, size: 15, color: const Color(0xFF0B5C5E)), const SizedBox(width: 6), Expanded( child: Text( text, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle(color: Color(0xFF526665), fontSize: 12), ), ), ], ); } } class _MessageState extends StatelessWidget { const _MessageState({ required this.icon, required this.title, required this.message, this.buttonLabel, this.onPressed, }); final IconData icon; final String title; final String message; final String? buttonLabel; final VoidCallback? onPressed; @override Widget build(BuildContext context) { return Center( child: Padding( padding: const EdgeInsets.fromLTRB(32, 20, 32, 100), child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( width: 74, height: 74, decoration: const BoxDecoration( color: Color(0xFFE7F1F0), shape: BoxShape.circle, ), child: Icon(icon, size: 34, color: const Color(0xFF0B5C5E)), ), const SizedBox(height: 20), Text( title, textAlign: TextAlign.center, style: Theme.of(context).textTheme.titleLarge?.copyWith( color: const Color(0xFF163334), fontWeight: FontWeight.w800, ), ), const SizedBox(height: 8), Text( message, textAlign: TextAlign.center, style: const TextStyle(color: Color(0xFF627372), height: 1.45), ), if (buttonLabel != null) ...[ const SizedBox(height: 20), FilledButton(onPressed: onPressed, child: Text(buttonLabel!)), ], ], ), ), ); } }