Merge branch 'feature/travel-cost-calculator' into main
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
import 'dart:convert';
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:em2rp/config/api_config.dart';
|
||||
import 'package:em2rp/models/depot_model.dart';
|
||||
import 'package:em2rp/models/route_result_model.dart';
|
||||
import 'package:em2rp/utils/debug_log.dart';
|
||||
|
||||
class TravelService {
|
||||
final FirebaseFirestore _db = FirebaseFirestore.instance;
|
||||
|
||||
// ─── Auth token ───────────────────────────────────────────
|
||||
Future<String?> _getToken() async {
|
||||
final user = FirebaseAuth.instance.currentUser;
|
||||
return await user?.getIdToken();
|
||||
}
|
||||
|
||||
Future<Map<String, String>> _headers() async {
|
||||
final token = await _getToken();
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
if (token != null) 'Authorization': 'Bearer $token',
|
||||
};
|
||||
}
|
||||
|
||||
// ─── Autocomplétion d'adresses ────────────────────────────
|
||||
Future<List<String>> autocompleteAddress(String query) async {
|
||||
if (query.trim().length < 3) return [];
|
||||
try {
|
||||
final headers = await _headers();
|
||||
final url = Uri.parse('${ApiConfig.baseUrl}/googleMapsAutocomplete');
|
||||
final response = await http.post(
|
||||
url,
|
||||
headers: headers,
|
||||
body: jsonEncode({'data': {'query': query}}),
|
||||
);
|
||||
if (response.statusCode != 200) return [];
|
||||
final data = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
final predictions = data['predictions'] as List<dynamic>? ?? [];
|
||||
return predictions
|
||||
.map((p) => (p['description'] ?? '').toString())
|
||||
.where((s) => s.isNotEmpty)
|
||||
.toList();
|
||||
} catch (e) {
|
||||
DebugLog.error('[Travel] autocompleteAddress error', e);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Calcul des itinéraires ───────────────────────────────
|
||||
Future<List<RouteResult>> computeRoutes({
|
||||
required String origin,
|
||||
required String destination,
|
||||
int vehicleTollCategory = 2,
|
||||
}) async {
|
||||
try {
|
||||
final headers = await _headers();
|
||||
final url = Uri.parse('${ApiConfig.baseUrl}/googleMapsComputeRoute');
|
||||
final response = await http.post(
|
||||
url,
|
||||
headers: headers,
|
||||
body: jsonEncode({
|
||||
'data': {
|
||||
'origin': origin,
|
||||
'destination': destination,
|
||||
'vehicleTollCategory': vehicleTollCategory,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
final err = jsonDecode(response.body);
|
||||
throw Exception('googleMapsComputeRoute: ${err['error']}');
|
||||
}
|
||||
|
||||
final data = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
final routes = data['routes'] as List<dynamic>? ?? [];
|
||||
return routes
|
||||
.map((r) => RouteResult.fromMap(r as Map<String, dynamic>))
|
||||
.toList();
|
||||
} catch (e) {
|
||||
DebugLog.error('[Travel] computeRoutes error', e);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Prix des carburants ───────────────────────────────────
|
||||
Future<FuelPrices> getFuelPrices() async {
|
||||
try {
|
||||
final doc = await _db.collection('app_config').doc('fuel_prices').get();
|
||||
if (!doc.exists) return const FuelPrices();
|
||||
return FuelPrices.fromMap(doc.data()!);
|
||||
} catch (e) {
|
||||
return const FuelPrices();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> saveFuelPrices(FuelPrices prices) async {
|
||||
await _db.collection('app_config').doc('fuel_prices').set(prices.toMap());
|
||||
}
|
||||
|
||||
// ─── Dépôts ───────────────────────────────────────────────
|
||||
Future<List<DepotModel>> getDepots() async {
|
||||
final snap = await _db.collection('depots').orderBy('name').get();
|
||||
return snap.docs.map((d) => DepotModel.fromFirestore(d)).toList();
|
||||
}
|
||||
|
||||
Stream<List<DepotModel>> watchDepots() {
|
||||
return _db
|
||||
.collection('depots')
|
||||
.orderBy('name')
|
||||
.snapshots()
|
||||
.map((s) => s.docs.map((d) => DepotModel.fromFirestore(d)).toList());
|
||||
}
|
||||
|
||||
Future<String> addDepot(DepotModel depot) async {
|
||||
final ref = await _db.collection('depots').add(depot.toMap());
|
||||
return ref.id;
|
||||
}
|
||||
|
||||
Future<void> updateDepot(DepotModel depot) async {
|
||||
final map = depot.toMap();
|
||||
map.remove('createdAt');
|
||||
await _db.collection('depots').doc(depot.id).update(map);
|
||||
}
|
||||
|
||||
Future<void> deleteDepot(String depotId) async {
|
||||
await _db.collection('depots').doc(depotId).delete();
|
||||
}
|
||||
}
|
||||
|
||||
/// Instance singleton
|
||||
final travelService = TravelService();
|
||||
@@ -0,0 +1,46 @@
|
||||
import 'package:cloud_firestore/cloud_firestore.dart';
|
||||
import 'package:em2rp/models/vehicle_model.dart';
|
||||
|
||||
class VehicleService {
|
||||
final FirebaseFirestore _db = FirebaseFirestore.instance;
|
||||
static const String _collection = 'vehicles';
|
||||
|
||||
/// Récupère tous les véhicules, triés par nom.
|
||||
Future<List<VehicleModel>> getVehicles() async {
|
||||
final snapshot = await _db
|
||||
.collection(_collection)
|
||||
.orderBy('name')
|
||||
.get();
|
||||
return snapshot.docs
|
||||
.map((doc) => VehicleModel.fromFirestore(doc))
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// Stream en temps réel
|
||||
Stream<List<VehicleModel>> watchVehicles() {
|
||||
return _db
|
||||
.collection(_collection)
|
||||
.orderBy('name')
|
||||
.snapshots()
|
||||
.map((snap) =>
|
||||
snap.docs.map((d) => VehicleModel.fromFirestore(d)).toList());
|
||||
}
|
||||
|
||||
/// Ajoute un véhicule
|
||||
Future<String> addVehicle(VehicleModel vehicle) async {
|
||||
final ref = await _db.collection(_collection).add(vehicle.toMap());
|
||||
return ref.id;
|
||||
}
|
||||
|
||||
/// Modifie un véhicule existant
|
||||
Future<void> updateVehicle(VehicleModel vehicle) async {
|
||||
final map = vehicle.toMap();
|
||||
map.remove('createdAt'); // Ne pas écraser la date de création
|
||||
await _db.collection(_collection).doc(vehicle.id).update(map);
|
||||
}
|
||||
|
||||
/// Supprime un véhicule
|
||||
Future<void> deleteVehicle(String vehicleId) async {
|
||||
await _db.collection(_collection).doc(vehicleId).delete();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user