540 lines
16 KiB
Dart
540 lines
16 KiB
Dart
import 'package:dio/dio.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
class ApiService {
|
|
// static const String baseUrl = 'http://localhost:3000/api';
|
|
static const String baseUrl = 'http://localhost:3000/api';
|
|
static const String tokenKey = 'auth_token';
|
|
|
|
late Dio _dio;
|
|
|
|
ApiService() {
|
|
_dio = Dio(BaseOptions(
|
|
baseUrl: baseUrl,
|
|
connectTimeout: const Duration(seconds: 30),
|
|
receiveTimeout: const Duration(seconds: 30),
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Accept': 'application/json',
|
|
},
|
|
));
|
|
|
|
// Add interceptor to include auth token
|
|
_dio.interceptors.add(InterceptorsWrapper(
|
|
onRequest: (options, handler) async {
|
|
final token = await _getToken();
|
|
if (token != null) {
|
|
options.headers['Authorization'] = 'Bearer $token';
|
|
}
|
|
handler.next(options);
|
|
},
|
|
onError: (error, handler) {
|
|
if (error.response?.statusCode == 401) {
|
|
// Token expired or invalid
|
|
_clearToken();
|
|
}
|
|
handler.next(error);
|
|
},
|
|
));
|
|
}
|
|
|
|
// Token management
|
|
Future<String?> _getToken() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
return prefs.getString(tokenKey);
|
|
}
|
|
|
|
Future<void> _saveToken(String token) async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
await prefs.setString(tokenKey, token);
|
|
}
|
|
|
|
Future<void> _clearToken() async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
await prefs.remove(tokenKey);
|
|
}
|
|
|
|
// Authentication APIs
|
|
Future<Map<String, dynamic>> signup(String mobileNumber, String fullName, String password, {String? email, String? address, String? emergencyContact}) async {
|
|
try {
|
|
final data = {
|
|
'mobile_number': mobileNumber,
|
|
'full_name': fullName,
|
|
'password': password,
|
|
};
|
|
|
|
if (email != null && email.isNotEmpty) data['email'] = email;
|
|
if (address != null && address.isNotEmpty) data['address'] = address;
|
|
if (emergencyContact != null && emergencyContact.isNotEmpty) data['emergency_contact'] = emergencyContact;
|
|
|
|
final response = await _dio.post('/auth/signup', data: data);
|
|
|
|
if (response.data['success']) {
|
|
await _saveToken(response.data['data']['token']);
|
|
}
|
|
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> login(String mobileNumber, String password) async {
|
|
try {
|
|
final response = await _dio.post('/auth/login', data: {
|
|
'mobile_number': mobileNumber,
|
|
'password': password,
|
|
});
|
|
|
|
if (response.data['success']) {
|
|
await _saveToken(response.data['data']['token']);
|
|
}
|
|
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> getProfile() async {
|
|
try {
|
|
final response = await _dio.get('/auth/profile');
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> updateProfile(Map<String, dynamic> data) async {
|
|
try {
|
|
final response = await _dio.put('/auth/profile', data: data);
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> changePassword(String currentPassword, String newPassword) async {
|
|
try {
|
|
final response = await _dio.put('/auth/change-password', data: {
|
|
'current_password': currentPassword,
|
|
'new_password': newPassword,
|
|
});
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> createMember(String mobileNumber, String fullName, {String? email, String? address, String? emergencyContact}) async {
|
|
try {
|
|
final data = {
|
|
'mobile_number': mobileNumber,
|
|
'full_name': fullName,
|
|
};
|
|
|
|
if (email != null && email.isNotEmpty) data['email'] = email;
|
|
if (address != null && address.isNotEmpty) data['address'] = address;
|
|
if (emergencyContact != null && emergencyContact.isNotEmpty) data['emergency_contact'] = emergencyContact;
|
|
|
|
final response = await _dio.post('/auth/create-member', data: data);
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
// Chit Group APIs
|
|
Future<Map<String, dynamic>> createChitGroup(Map<String, dynamic> data) async {
|
|
try {
|
|
final response = await _dio.post('/chit-groups', data: data);
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> getManagerChitGroups({String? status, int page = 1, int limit = 10}) async {
|
|
try {
|
|
final response = await _dio.get('/chit-groups/manager', queryParameters: {
|
|
if (status != null) 'status': status,
|
|
'page': page,
|
|
'limit': limit,
|
|
});
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> getMemberChitGroups({String? status, int page = 1, int limit = 10}) async {
|
|
try {
|
|
final response = await _dio.get('/chit-groups/member', queryParameters: {
|
|
if (status != null) 'status': status,
|
|
'page': page,
|
|
'limit': limit,
|
|
});
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> getChitGroupDetails(String groupId) async {
|
|
try {
|
|
final response = await _dio.get('/chit-groups/$groupId');
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> updateChitGroup(String groupId, Map<String, dynamic> data) async {
|
|
try {
|
|
final response = await _dio.put('/chit-groups/$groupId', data: data);
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> deleteChitGroup(String groupId) async {
|
|
try {
|
|
final response = await _dio.delete('/chit-groups/$groupId');
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> startChitGroup(String groupId) async {
|
|
try {
|
|
final response = await _dio.post('/chit-groups/$groupId/start');
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> getChitGroupStats(String groupId) async {
|
|
try {
|
|
final response = await _dio.get('/chit-groups/$groupId/stats');
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
// Member Management APIs
|
|
Future<Map<String, dynamic>> addMemberToGroup(String groupId, Map<String, dynamic> memberData) async {
|
|
try {
|
|
final response = await _dio.post('/members/$groupId/members', data: memberData);
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> removeMemberFromGroup(String groupId, String memberId) async {
|
|
try {
|
|
final response = await _dio.delete('/members/$groupId/members/$memberId');
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> getAllUsers({String? search, int page = 1, int limit = 50}) async {
|
|
try {
|
|
final queryParams = <String, dynamic>{
|
|
'page': page,
|
|
'limit': limit,
|
|
};
|
|
|
|
if (search != null && search.isNotEmpty) {
|
|
queryParams['search'] = search;
|
|
}
|
|
|
|
final response = await _dio.get('/members/users', queryParameters: queryParams);
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> getAvailableUsersForGroup(String groupId) async {
|
|
try {
|
|
final response = await _dio.get('/members/users/available/$groupId');
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> getGroupMembers(String groupId, {String? status, int page = 1, int limit = 20}) async {
|
|
try {
|
|
final response = await _dio.get('/members/$groupId/members', queryParameters: {
|
|
if (status != null) 'status': status,
|
|
'page': page,
|
|
'limit': limit,
|
|
});
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> getMemberDetails(String groupId, String memberId) async {
|
|
try {
|
|
final response = await _dio.get('/members/$groupId/members/$memberId');
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> updateMemberStatus(String groupId, String memberId, String status) async {
|
|
try {
|
|
final response = await _dio.put('/members/$groupId/members/$memberId/status', data: {
|
|
'status': status,
|
|
});
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> getMemberPaymentSummary(String groupId, String memberId) async {
|
|
try {
|
|
final response = await _dio.get('/members/$groupId/members/$memberId/payment-summary');
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
// Payment Management APIs
|
|
Future<Map<String, dynamic>> recordPayment(Map<String, dynamic> data) async {
|
|
try {
|
|
final response = await _dio.post('/payments', data: data);
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> getGroupPayments(String groupId, {String? status, int? month, int? year, int page = 1, int limit = 20}) async {
|
|
try {
|
|
final response = await _dio.get('/payments/group/$groupId', queryParameters: {
|
|
if (status != null) 'status': status,
|
|
if (month != null) 'month': month,
|
|
if (year != null) 'year': year,
|
|
'page': page,
|
|
'limit': limit,
|
|
});
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> getMemberPayments(String groupId, String memberId, {String? status, int page = 1, int limit = 20}) async {
|
|
try {
|
|
final response = await _dio.get('/payments/group/$groupId/member/$memberId', queryParameters: {
|
|
if (status != null) 'status': status,
|
|
'page': page,
|
|
'limit': limit,
|
|
});
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> updatePaymentStatus(String paymentId, String status, {String? notes}) async {
|
|
try {
|
|
final response = await _dio.put('/payments/$paymentId/status', data: {
|
|
'status': status,
|
|
if (notes != null) 'notes': notes,
|
|
});
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> getGroupPaymentSummary(String groupId) async {
|
|
try {
|
|
final response = await _dio.get('/payments/group/$groupId/summary');
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> getPendingPayments(String groupId) async {
|
|
try {
|
|
final response = await _dio.get('/payments/group/$groupId/pending');
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
// Monthly Draw APIs
|
|
Future<Map<String, dynamic>> createMonthlyDraw(Map<String, dynamic> data) async {
|
|
try {
|
|
final response = await _dio.post('/monthly-draws', data: data);
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> getGroupMonthlyDraws(String groupId, {String? status, int page = 1, int limit = 20}) async {
|
|
try {
|
|
final response = await _dio.get('/monthly-draws/group/$groupId', queryParameters: {
|
|
if (status != null) 'status': status,
|
|
'page': page,
|
|
'limit': limit,
|
|
});
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> getMonthlyDrawDetails(String drawId) async {
|
|
try {
|
|
final response = await _dio.get('/monthly-draws/$drawId');
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> verifyDrawResult(String drawId, {String? clientSeed}) async {
|
|
try {
|
|
final response = await _dio.post('/monthly-draws/$drawId/verify', data: {
|
|
if (clientSeed != null) 'client_seed': clientSeed,
|
|
});
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> getDrawStatistics(String groupId) async {
|
|
try {
|
|
final response = await _dio.get('/monthly-draws/group/$groupId/statistics');
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> updateMonthlyDraw(String drawId, Map<String, dynamic> data) async {
|
|
try {
|
|
final response = await _dio.put('/monthly-draws/$drawId', data: data);
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> deleteMonthlyDraw(String drawId) async {
|
|
try {
|
|
final response = await _dio.delete('/monthly-draws/$drawId');
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> getGroupFinancialData(String groupId) async {
|
|
try {
|
|
final response = await _dio.get('/chit-groups/$groupId/financial-data');
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> updateMemberDetails(String memberId, Map<String, dynamic> data) async {
|
|
try {
|
|
final response = await _dio.put('/auth/member/$memberId', data: data);
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
// Generic HTTP Methods
|
|
Future<Map<String, dynamic>> get(String path, {Map<String, dynamic>? queryParameters}) async {
|
|
try {
|
|
final response = await _dio.get(path, queryParameters: queryParameters);
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> post(String path, Map<String, dynamic> data) async {
|
|
try {
|
|
final response = await _dio.post(path, data: data);
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> put(String path, Map<String, dynamic> data) async {
|
|
try {
|
|
final response = await _dio.put(path, data: data);
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
Future<Map<String, dynamic>> delete(String path) async {
|
|
try {
|
|
final response = await _dio.delete(path);
|
|
return response.data;
|
|
} on DioException catch (e) {
|
|
return _handleError(e);
|
|
}
|
|
}
|
|
|
|
// Logout
|
|
Future<void> logout() async {
|
|
await _clearToken();
|
|
}
|
|
|
|
// Error handling
|
|
Map<String, dynamic> _handleError(DioException e) {
|
|
if (e.response != null) {
|
|
final responseData = e.response?.data;
|
|
String message = 'Request failed';
|
|
|
|
if (responseData is Map<String, dynamic>) {
|
|
message = responseData['message']?.toString() ?? 'Request failed';
|
|
} else if (responseData is String) {
|
|
message = responseData;
|
|
}
|
|
|
|
return {
|
|
'success': false,
|
|
'message': message,
|
|
'statusCode': e.response?.statusCode,
|
|
};
|
|
} else {
|
|
return {
|
|
'success': false,
|
|
'message': 'Network error: ${e.message}',
|
|
'statusCode': 0,
|
|
};
|
|
}
|
|
}
|
|
}
|