117 lines
3.4 KiB
Dart
117 lines
3.4 KiB
Dart
import '../../../core/time/server_time.dart';
|
|
|
|
/// Progression d'un lavage en cours.
|
|
class WashProgress {
|
|
const WashProgress({
|
|
required this.percent,
|
|
required this.phaseLabel,
|
|
this.phase,
|
|
this.estimatedEndAt,
|
|
this.remainingSeconds,
|
|
});
|
|
|
|
final int percent;
|
|
final String phaseLabel;
|
|
final String? phase;
|
|
final DateTime? estimatedEndAt;
|
|
final int? remainingSeconds;
|
|
|
|
String get remainingLabel {
|
|
if (remainingSeconds == null) return '';
|
|
final s = remainingSeconds!;
|
|
if (s <= 0) return 'Bientôt terminé';
|
|
if (s < 60) return '$s s restantes';
|
|
final min = (s / 60).ceil();
|
|
return '$min min restantes';
|
|
}
|
|
|
|
factory WashProgress.fromJson(Map<String, dynamic>? json) {
|
|
if (json == null) {
|
|
return const WashProgress(percent: 0, phaseLabel: 'En cours');
|
|
}
|
|
return WashProgress(
|
|
percent: json['percent'] as int? ?? 0,
|
|
phase: json['phase'] as String?,
|
|
phaseLabel: json['phase_label'] as String? ?? 'En cours',
|
|
estimatedEndAt: json['estimated_end_at'] != null
|
|
? DateTime.tryParse(json['estimated_end_at'] as String)
|
|
: null,
|
|
remainingSeconds: json['remaining_seconds'] as int?,
|
|
);
|
|
}
|
|
|
|
/// Calcul local si pas de données API (rafraîchissement chaque seconde).
|
|
factory WashProgress.compute({
|
|
required DateTime? startedAt,
|
|
required DateTime? estimatedEndAt,
|
|
required int? durationMinutes,
|
|
required String? machineType,
|
|
required String status,
|
|
}) {
|
|
if (status == 'pending_start') {
|
|
return const WashProgress(percent: 0, phaseLabel: 'En attente de démarrage', phase: 'pending');
|
|
}
|
|
|
|
final now = ServerTime.now();
|
|
final end = estimatedEndAt ??
|
|
(startedAt != null && durationMinutes != null
|
|
? startedAt.add(Duration(minutes: durationMinutes))
|
|
: null);
|
|
|
|
if (startedAt == null || end == null) {
|
|
return const WashProgress(percent: 5, phaseLabel: 'Démarrage…', phase: 'lock');
|
|
}
|
|
|
|
final total = end.difference(startedAt).inSeconds.clamp(1, 999999);
|
|
final elapsed = now.difference(startedAt).inSeconds.clamp(0, total);
|
|
final percent = ((elapsed / total) * 100).round().clamp(0, 99);
|
|
final remaining = end.difference(now).inSeconds.clamp(0, total);
|
|
|
|
return WashProgress(
|
|
percent: percent,
|
|
phaseLabel: _phaseLabel(percent, machineType),
|
|
phase: _phase(percent, machineType),
|
|
estimatedEndAt: end,
|
|
remainingSeconds: remaining,
|
|
);
|
|
}
|
|
|
|
static String _phase(int percent, String? type) {
|
|
final isDryer = type?.startsWith('dryer') ?? false;
|
|
if (percent < 5) return 'lock';
|
|
if (isDryer) {
|
|
if (percent < 15) return 'heat';
|
|
if (percent < 85) return 'dry';
|
|
return 'finish';
|
|
}
|
|
if (percent < 15) return 'fill';
|
|
if (percent < 55) return 'wash';
|
|
if (percent < 75) return 'rinse';
|
|
if (percent < 90) return 'spin';
|
|
return 'finish';
|
|
}
|
|
|
|
static String _phaseLabel(int percent, String? type) {
|
|
switch (_phase(percent, type)) {
|
|
case 'lock':
|
|
return 'Verrouillage';
|
|
case 'heat':
|
|
return 'Préchauffage';
|
|
case 'dry':
|
|
return 'Séchage';
|
|
case 'fill':
|
|
return 'Remplissage';
|
|
case 'wash':
|
|
return 'Lavage';
|
|
case 'rinse':
|
|
return 'Rinçage';
|
|
case 'spin':
|
|
return 'Essorage';
|
|
case 'finish':
|
|
return 'Finition';
|
|
default:
|
|
return 'En cours';
|
|
}
|
|
}
|
|
}
|