first app design

This commit is contained in:
root
2026-05-24 02:35:37 -04:00
parent c842eed601
commit d04c8ce437
138 changed files with 9672 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
import '../models/analytics.dart';
import 'api_client.dart';
class AnalyticsService {
final _client = ApiClient.instance;
Future<DashboardMetrics> getDashboard() async {
final res = await _client.get('/analytics/dashboard');
return DashboardMetrics.fromJson(res.data as Map<String, dynamic>);
}
}
+58
View File
@@ -0,0 +1,58 @@
import 'package:dio/dio.dart';
import '../constants/app_constants.dart';
import '../storage/token_storage.dart';
class ApiClient {
static final ApiClient _instance = ApiClient._();
late final Dio _dio;
ApiClient._() {
_dio = Dio(BaseOptions(
baseUrl: AppConstants.baseUrl,
connectTimeout: const Duration(seconds: 10),
receiveTimeout: const Duration(seconds: 30),
headers: {'Content-Type': 'application/json'},
));
_dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) async {
final token = await TokenStorage.getToken();
if (token != null) {
options.headers['Authorization'] = 'Bearer $token';
}
handler.next(options);
},
onResponse: (response, handler) {
// Every successful response from this API is wrapped: { data: <payload> }
// Unwrap it so services always receive the raw payload.
final body = response.data;
if (body is Map<String, dynamic> &&
body.length == 1 &&
body.containsKey('data')) {
response.data = body['data'];
}
handler.next(response);
},
onError: (e, handler) {
handler.next(e);
},
));
}
static ApiClient get instance => _instance;
// baseUrl is dynamic (platform-aware), so recreate when it may have changed.
// In practice this is called once and cached via the singleton.
Dio get dio => _dio;
Future<Response> get(String path, {Map<String, dynamic>? params}) =>
_dio.get(path, queryParameters: params);
Future<Response> post(String path, {dynamic data}) =>
_dio.post(path, data: data);
Future<Response> patch(String path, {dynamic data}) =>
_dio.patch(path, data: data);
Future<Response> delete(String path) => _dio.delete(path);
}
+38
View File
@@ -0,0 +1,38 @@
import '../models/auth_models.dart';
import '../storage/token_storage.dart';
import 'api_client.dart';
class AuthService {
final _client = ApiClient.instance;
// Response after envelope unwrap: { token, employee: { id, firstName, ... } }
Future<({String token, Employee employee})> loginEmployee({
required String email,
required String password,
}) async {
final res = await _client.post('/auth/employee/login', data: {
'email': email,
'password': password,
});
final data = res.data as Map<String, dynamic>;
final token = data['token'] as String;
final employee =
Employee.fromJson(data['employee'] as Map<String, dynamic>);
return (token: token, employee: employee);
}
// Response after envelope unwrap: { employee: { id, firstName, ... } }
Future<Employee> getMe() async {
final res = await _client.get('/auth/employee/me');
final data = res.data as Map<String, dynamic>;
return Employee.fromJson(data['employee'] as Map<String, dynamic>);
}
Future<Renter> getRenterMe() async {
final res = await _client.get('/auth/renter/me');
final data = res.data as Map<String, dynamic>;
return Renter.fromJson(data);
}
Future<void> logout() => TokenStorage.clear();
}
+29
View File
@@ -0,0 +1,29 @@
import '../models/customer.dart';
import 'api_client.dart';
class CustomerService {
final _client = ApiClient.instance;
Future<CustomerListResponse> getCustomers({
int page = 1,
int pageSize = 20,
String? search,
}) async {
final res = await _client.get('/customers', params: {
'page': page,
'pageSize': pageSize,
if (search != null && search.isNotEmpty) 'search': search,
});
return CustomerListResponse.fromJson(res.data as Map<String, dynamic>);
}
Future<Customer> getCustomer(String id) async {
final res = await _client.get('/customers/$id');
return Customer.fromJson(res.data as Map<String, dynamic>);
}
Future<Customer> createCustomer(Map<String, dynamic> data) async {
final res = await _client.post('/customers', data: data);
return Customer.fromJson(res.data as Map<String, dynamic>);
}
}
@@ -0,0 +1,98 @@
import '../models/marketplace.dart';
import 'api_client.dart';
class MarketplaceService {
final _client = ApiClient.instance;
// After envelope unwrap: [ "City1", "City2", ... ]
Future<List<String>> getCities() async {
final res = await _client.get('/marketplace/cities');
final data = res.data;
if (data is List) {
return data.whereType<String>().toList();
}
return [];
}
// After envelope unwrap: [ { id, title, ... }, ... ]
Future<List<MarketplaceOffer>> getOffers() async {
final res = await _client.get('/marketplace/offers');
final data = res.data;
if (data is List) {
return data
.map((e) => MarketplaceOffer.fromJson(e as Map<String, dynamic>))
.toList();
}
return [];
}
// After envelope unwrap: [ { id, make, model, ... }, ... ]
Future<MarketplaceSearchResult> search({
String? city,
DateTime? startDate,
DateTime? endDate,
String? category,
String? transmission,
String? make,
int? maxPriceCents,
int page = 1,
int pageSize = 20,
}) async {
final res = await _client.get('/marketplace/search', params: {
'page': page,
'pageSize': pageSize,
if (city != null && city.isNotEmpty) 'city': city,
if (startDate != null)
'startDate': '${startDate.toIso8601String().substring(0, 10)}T00:00:00.000Z',
if (endDate != null)
'endDate': '${endDate.toIso8601String().substring(0, 10)}T00:00:00.000Z',
if (category != null) 'category': category,
if (transmission != null) 'transmission': transmission,
if (make != null && make.isNotEmpty) 'make': make,
if (maxPriceCents != null) 'maxPrice': maxPriceCents,
});
final data = res.data;
if (data is List) {
final vehicles = data
.map((e) => MarketplaceVehicle.fromJson(e as Map<String, dynamic>))
.toList();
return MarketplaceSearchResult(
data: vehicles,
total: vehicles.length,
page: page,
pageSize: pageSize,
);
}
// Fallback: paginated object shape { data: [...], total: N }
return MarketplaceSearchResult.fromJson(data as Map<String, dynamic>);
}
Future<String> createReservation({
required String vehicleId,
required String companySlug,
required String firstName,
required String lastName,
required String email,
String? phone,
required DateTime startDate,
required DateTime endDate,
String? notes,
String language = 'en',
}) async {
final res = await _client.post('/marketplace/reservations', data: {
'vehicleId': vehicleId,
'companySlug': companySlug,
'firstName': firstName,
'lastName': lastName,
'email': email,
if (phone != null && phone.isNotEmpty) 'phone': phone,
'startDate': '${startDate.toIso8601String().substring(0, 10)}T00:00:00.000Z',
'endDate': '${endDate.toIso8601String().substring(0, 10)}T00:00:00.000Z',
if (notes != null && notes.isNotEmpty) 'notes': notes,
'language': language,
});
final data = res.data as Map<String, dynamic>;
return data['reservationId'] as String;
}
}
@@ -0,0 +1,47 @@
import '../models/reservation.dart';
import 'api_client.dart';
class ReservationService {
final _client = ApiClient.instance;
Future<ReservationListResponse> getReservations({
int page = 1,
int pageSize = 20,
String? status,
String? search,
}) async {
final res = await _client.get('/reservations', params: {
'page': page,
'pageSize': pageSize,
if (status != null) 'status': status,
if (search != null && search.isNotEmpty) 'search': search,
});
return ReservationListResponse.fromJson(res.data as Map<String, dynamic>);
}
Future<Reservation> getReservation(String id) async {
final res = await _client.get('/reservations/$id');
return Reservation.fromJson(res.data as Map<String, dynamic>);
}
Future<Reservation> createReservation(Map<String, dynamic> data) async {
final res = await _client.post('/reservations', data: data);
return Reservation.fromJson(res.data as Map<String, dynamic>);
}
Future<void> confirmReservation(String id) async {
await _client.post('/reservations/$id/confirm');
}
Future<void> cancelReservation(String id, String reason) async {
await _client.post('/reservations/$id/cancel', data: {'reason': reason});
}
Future<void> checkin(String id, int mileage) async {
await _client.post('/reservations/$id/checkin', data: {'mileage': mileage});
}
Future<void> checkout(String id, int mileage) async {
await _client.post('/reservations/$id/checkout', data: {'mileage': mileage});
}
}
+40
View File
@@ -0,0 +1,40 @@
import '../models/vehicle.dart';
import 'api_client.dart';
class VehicleService {
final _client = ApiClient.instance;
Future<VehicleListResponse> getVehicles({
int page = 1,
int pageSize = 20,
String? status,
String? search,
}) async {
final res = await _client.get('/vehicles', params: {
'page': page,
'pageSize': pageSize,
if (status != null) 'status': status,
if (search != null && search.isNotEmpty) 'search': search,
});
return VehicleListResponse.fromJson(res.data as Map<String, dynamic>);
}
Future<Vehicle> getVehicle(String id) async {
final res = await _client.get('/vehicles/$id');
return Vehicle.fromJson(res.data as Map<String, dynamic>);
}
Future<Vehicle> createVehicle(Map<String, dynamic> data) async {
final res = await _client.post('/vehicles', data: data);
return Vehicle.fromJson(res.data as Map<String, dynamic>);
}
Future<Vehicle> updateVehicle(String id, Map<String, dynamic> data) async {
final res = await _client.patch('/vehicles/$id', data: data);
return Vehicle.fromJson(res.data as Map<String, dynamic>);
}
Future<void> togglePublish(String id, bool publish) async {
await _client.patch('/vehicles/$id/publish', data: {'isPublished': publish});
}
}