Files
senaro_dealer_app/lib/services/api_client.dart
T
2026-10-01 16:03:32 +05:30

229 lines
6.8 KiB
Dart

import 'package:dio/dio.dart';
import '../config/api_config.dart';
import '../models/models.dart';
class ApiException implements Exception {
ApiException(this.message, {this.statusCode});
final String message;
final int? statusCode;
@override
String toString() => message;
}
class ApiClient {
ApiClient() {
dio = Dio(
BaseOptions(
baseUrl: resolveApiBaseUrl(),
connectTimeout: const Duration(seconds: 15),
receiveTimeout: const Duration(seconds: 15),
headers: const {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
),
);
dio.interceptors.add(
InterceptorsWrapper(
onRequest: (options, handler) {
if (token != null && token!.isNotEmpty) {
options.headers['Authorization'] = 'Bearer $token';
}
handler.next(options);
},
),
);
}
late final Dio dio;
String? token;
Future<({String token, AppUser user})> staffLogin(String phone, String password) {
return _send(
dio.post('/auth/staff-login', data: {'phone': phone, 'password': password}),
(data) {
final map = data as Map<String, dynamic>;
return (
token: map['token'] as String,
user: AppUser.fromJson(map['user'] as Map<String, dynamic>),
);
},
);
}
Future<({String requestId, String secret})> dealerLogin(String phone) {
return _send(dio.post('/auth/dealer-login', data: {'phone': phone}), (data) {
final map = data as Map<String, dynamic>;
return (requestId: map['requestId'] as String, secret: map['secret'] as String);
});
}
Future<DealerLoginStatus> dealerLoginStatus(String requestId, String secret) {
return _send(
dio.get('/auth/dealer-login/$requestId', queryParameters: {'secret': secret}),
(data) => DealerLoginStatus.fromJson(data as Map<String, dynamic>),
);
}
Future<List<LoginRequestItem>> loginRequests() {
return _send(dio.get('/auth/login-requests'), (data) => _list(data, LoginRequestItem.fromJson));
}
Future<void> approveLogin(String id) {
return _send(dio.post('/auth/login-requests/$id/approve'), (_) {});
}
Future<void> rejectLogin(String id) {
return _send(dio.post('/auth/login-requests/$id/reject'), (_) {});
}
Future<AppUser> me() {
return _send(
dio.get('/auth/me'),
(data) => AppUser.fromJson(data as Map<String, dynamic>),
);
}
Future<List<Dealer>> dealers() {
return _send(dio.get('/dealers'), (data) => _list(data, Dealer.fromJson));
}
Future<List<StockItem>> stocks({String? dealerId}) {
return _send(
dio.get(
'/stocks',
queryParameters: {
if (dealerId != null && dealerId.isNotEmpty) 'dealerId': dealerId,
},
),
(data) => _list(data, StockItem.fromJson),
);
}
Future<StockItem> createStock(Map<String, dynamic> body) {
return _send(
dio.post('/stocks', data: body),
(data) => StockItem.fromJson((data as Map<String, dynamic>)['data'] as Map<String, dynamic>),
);
}
Future<StockItem> updateStage(String chassisNumber, Map<String, dynamic> body) {
return _send(
dio.patch('/stocks/$chassisNumber/stage', data: body),
(data) => StockItem.fromJson((data as Map<String, dynamic>)['data'] as Map<String, dynamic>),
);
}
Future<StockItem> transferStock(String chassisNumber, String dealerId) {
return _send(
dio.post('/stocks/$chassisNumber/transfer', data: {'dealerId': dealerId}),
(data) => StockItem.fromJson((data as Map<String, dynamic>)['data'] as Map<String, dynamic>),
);
}
Future<List<EventPlan>> events({String? dealerId, String? planType}) {
return _send(
dio.get(
'/events',
queryParameters: {
if (dealerId != null && dealerId.isNotEmpty) 'dealerId': dealerId,
if (planType != null && planType.isNotEmpty) 'planType': planType,
},
),
(data) => _list(data, EventPlan.fromJson),
);
}
Future<EventPlan> createEvent(Map<String, dynamic> body) {
return _send(
dio.post('/events', data: body),
(data) => EventPlan.fromJson((data as Map<String, dynamic>)['data'] as Map<String, dynamic>),
);
}
Future<List<ServicePoint>> servicePoints({String? district, String? search}) {
return _send(
dio.get(
'/service-points',
queryParameters: {
if (district != null && district.isNotEmpty) 'district': district,
if (search != null && search.isNotEmpty) 'search': search,
},
),
(data) => _list(data, ServicePoint.fromJson),
);
}
Future<ServicePoint> createServicePoint(Map<String, dynamic> body) {
return _send(
dio.post('/service-points', data: body),
(data) => ServicePoint.fromJson((data as Map<String, dynamic>)['data'] as Map<String, dynamic>),
);
}
Future<Dealer> createDealer(Map<String, dynamic> body) {
return _send(
dio.post('/admin/dealers', data: body),
(data) => Dealer.fromJson((data as Map<String, dynamic>)['data'] as Map<String, dynamic>),
);
}
Future<void> deleteDealer(String id) {
return _send(dio.delete('/admin/dealers/$id'), (_) {});
}
Future<List<Dealer>> adminDealers() {
return _send(dio.get('/admin/dealers'), (data) => _list(data, Dealer.fromJson));
}
Future<List<AppUser>> salesReps() {
return _send(dio.get('/admin/sales-reps'), (data) => _list(data, AppUser.fromJson));
}
Future<AppUser> createSalesRep(Map<String, dynamic> body) {
return _send(
dio.post('/admin/sales-reps', data: body),
(data) => AppUser.fromJson((data as Map<String, dynamic>)['data'] as Map<String, dynamic>),
);
}
Future<AppUser> assignDealer({
required String salesRepId,
required String dealerId,
required bool assigned,
}) {
return _send(
dio.patch(
'/admin/assign-dealer',
data: {
'salesRepId': salesRepId,
'dealerId': dealerId,
'assigned': assigned,
},
),
(data) => AppUser.fromJson((data as Map<String, dynamic>)['data'] as Map<String, dynamic>),
);
}
Future<T> _send<T>(Future<Response<dynamic>> request, T Function(dynamic data) parse) async {
try {
final response = await request;
return parse(response.data);
} on DioException catch (error) {
final data = error.response?.data;
final message = data is Map && data['message'] is String
? data['message'] as String
: 'Network request failed';
throw ApiException(message, statusCode: error.response?.statusCode);
}
}
List<T> _list<T>(dynamic data, T Function(Map<String, dynamic>) parse) {
final raw = (data as Map<String, dynamic>)['data'] as List<dynamic>;
return raw.map((item) => parse(item as Map<String, dynamic>)).toList();
}
}