import 'dart:async'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:intl/intl.dart'; import '../../../core/api/api_response.dart'; import '../../../core/router/app_router.dart'; import '../../../core/theme/app_colors.dart'; import '../../../core/time/server_time.dart'; import '../../../core/widgets/empty_state.dart'; import '../../../core/widgets/screen_header.dart'; import '../data/wash_repository.dart'; import '../domain/wash.dart'; import '../domain/wash_progress.dart'; import 'widgets/active_wash_progress_card.dart'; bool _isActiveWash(String status) => status == 'running' || status == 'pending_start' || status == 'active'; /// Écran historique et lavages en cours avec progression. class WashScreen extends ConsumerStatefulWidget { const WashScreen({super.key}); @override ConsumerState createState() => _WashScreenState(); } class _WashScreenState extends ConsumerState { Timer? _ticker; @override void initState() { super.initState(); _ticker = Timer.periodic(const Duration(seconds: 1), (_) { if (mounted) setState(() {}); }); } @override void dispose() { _ticker?.cancel(); super.dispose(); } @override Widget build(BuildContext context) { final washesAsync = ref.watch(washesProvider); return washesAsync.when( loading: () => const Center(child: CircularProgressIndicator()), error: (error, _) => EmptyState( icon: Icons.error_outline, title: 'Erreur de chargement', subtitle: error is DioException ? ApiResponse.errorMessage(error, fallback: 'Impossible de charger les lavages.') : 'Impossible de charger les lavages.', actionLabel: 'Réessayer', onAction: () => ref.invalidate(washesProvider), ), data: (washes) { if (washes.isEmpty) { return EmptyState( icon: Icons.qr_code_scanner_outlined, title: 'Aucun lavage', subtitle: 'Scannez une machine pour voir\nses infos et démarrer un cycle.', actionLabel: 'Scanner', onAction: () => context.push(AppRoutes.washScan), ); } final active = washes.where((w) => _isActiveWash(w.status)).toList(); final history = washes.where((w) => !_isActiveWash(w.status)).toList(); final currencyFormat = NumberFormat.currency(locale: 'fr_FR', symbol: '€'); return RefreshIndicator( onRefresh: () async => ref.invalidate(washesProvider), child: ListView( padding: const EdgeInsets.fromLTRB(16, 8, 16, 88), children: [ if (active.isNotEmpty) ...[ const ScreenHeader( title: 'En cours', subtitle: 'Progression en temps réel', ), ...active.map((wash) { final progress = _liveProgress(wash); return ActiveWashProgressCard( wash: wash, format: currencyFormat, progress: progress, ); }), const SizedBox(height: 16), ], if (history.isNotEmpty) ...[ Text('Historique', style: Theme.of(context).textTheme.titleMedium), const SizedBox(height: 10), ...history.map( (wash) => Padding( padding: const EdgeInsets.only(bottom: 8), child: _HistoryWashTile(wash: wash, format: currencyFormat), ), ), ], ], ), ); }, ); } WashProgress _liveProgress(Wash wash) { if (wash.progress != null && wash.progress!.percent > 0) { return WashProgress.compute( startedAt: wash.startedAt, estimatedEndAt: wash.progress!.estimatedEndAt ?? wash.cycleEndsAt, durationMinutes: wash.durationMinutes, machineType: wash.machineType, status: wash.status, ); } return wash.liveProgress; } } class _HistoryWashTile extends StatelessWidget { const _HistoryWashTile({required this.wash, required this.format}); final Wash wash; final NumberFormat format; @override Widget build(BuildContext context) { return Card( child: ListTile( leading: const CircleAvatar( backgroundColor: Color(0xFFE2E8F0), child: Icon(Icons.check_circle_outline, color: AppColors.success, size: 20), ), title: Text(wash.machineName), subtitle: wash.startedAt != null ? Text(ServerTime.format(wash.startedAt, pattern: 'dd/MM/yyyy · HH:mm')) : null, trailing: Text( format.format(wash.cost), style: const TextStyle(fontWeight: FontWeight.w600), ), ), ); } }