Files
mobile/lib/features/wash/data/wash_repository.dart

132 lines
3.7 KiB
Dart

import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../core/auth/auth_provider.dart';
import '../../../core/api/api_client.dart';
import '../../../core/api/api_endpoints.dart';
import '../../../core/api/api_response.dart';
import '../domain/wash.dart';
/// Référence machine extraite d'un QR code scanné.
class QrMachineReference {
const QrMachineReference({
this.machineUuid,
this.qrCode,
});
final String? machineUuid;
final String? qrCode;
bool get isValid =>
(machineUuid != null && machineUuid!.isNotEmpty) ||
(qrCode != null && qrCode!.isNotEmpty);
factory QrMachineReference.parse(String raw) {
final trimmed = raw.trim();
if (trimmed.isEmpty) {
return const QrMachineReference();
}
try {
final decoded = jsonDecode(trimmed);
if (decoded is Map<String, dynamic>) {
return QrMachineReference(
machineUuid: decoded['machine_uuid'] as String?,
qrCode: decoded['qr_code'] as String?,
);
}
} catch (_) {
// Contenu texte brut (ex. LAVERIE-DEMO-001).
}
final uuidPattern = RegExp(
r'^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$',
);
if (uuidPattern.hasMatch(trimmed)) {
return QrMachineReference(machineUuid: trimmed);
}
return QrMachineReference(qrCode: trimmed);
}
}
/// Dépôt de données pour les lavages.
class WashRepository {
WashRepository(this._apiClient);
final ApiClient _apiClient;
Future<List<Wash>> fetchWashes() async {
final response = await _apiClient.get(ApiEndpoints.washes);
final list = ApiResponse.list(response.data, 'washes');
return list
.map((json) => Wash.fromJson(json as Map<String, dynamic>))
.toList();
}
Future<Wash> startWashFromMachine(String machineUuid) async {
try {
final response = await _apiClient.post(
ApiEndpoints.washesStart,
data: {
'machine_uuid': machineUuid,
'trigger_method': 'qr_code',
},
);
final washJson = ApiResponse.object(response.data, 'wash');
return Wash.fromJson(washJson);
} on DioException catch (error) {
throw WashStartException(ApiResponse.errorMessage(error, fallback: 'Impossible de démarrer le lavage'));
}
}
Future<Wash> startWashFromQr(String scannedValue) async {
final reference = QrMachineReference.parse(scannedValue);
if (!reference.isValid) {
throw const FormatException('QR code invalide ou vide');
}
final payload = <String, dynamic>{
'trigger_method': 'qr_code',
if (reference.machineUuid != null) 'machine_uuid': reference.machineUuid,
if (reference.qrCode != null) 'qr_code': reference.qrCode,
};
try {
final response = await _apiClient.post(
ApiEndpoints.washesStart,
data: payload,
);
final washJson = ApiResponse.object(response.data, 'wash');
return Wash.fromJson(washJson);
} on DioException catch (error) {
throw WashStartException(ApiResponse.errorMessage(error, fallback: 'Impossible de démarrer le lavage'));
}
}
}
class WashStartException implements Exception {
WashStartException(this.message);
final String message;
@override
String toString() => message;
}
final washRepositoryProvider = Provider<WashRepository>((ref) {
return WashRepository(ref.watch(apiClientProvider));
});
final washesProvider = FutureProvider<List<Wash>>((ref) async {
final token = ref.watch(authProvider.select((state) => state.accessToken));
if (token == null || token.isEmpty) {
throw StateError('Non authentifié');
}
return ref.watch(washRepositoryProvider).fetchWashes();
});