import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'package:bookmywages/model/Banner_model.dart'; import 'package:bookmywages/model/Booking_model.dart'; import 'package:bookmywages/model/Categories_model.dart'; import 'package:bookmywages/model/detail_page_model.dart'; import 'package:bookmywages/model/enquriy_list_model.dart'; import 'package:bookmywages/model/enquriy_model.dart'; import 'package:bookmywages/model/expired_plan_model.dart'; import 'package:bookmywages/model/get_review_model.dart'; import 'package:bookmywages/model/login_model.dart'; import 'package:bookmywages/model/most_popular_model.dart'; import 'package:bookmywages/model/notification_model.dart'; import 'package:bookmywages/model/otp_model.dart'; import 'package:bookmywages/model/package_model.dart'; import 'package:bookmywages/model/payment_details_model.dart'; import 'package:bookmywages/model/profile_get_model.dart'; import 'package:bookmywages/model/service_model.dart'; import 'package:bookmywages/model/subcategory_model.dart'; import 'package:bookmywages/model/user_booking_details.dart' show UserBookingDetails; import 'package:bookmywages/model/vendor_model/terms_and_conditions_model.dart'; import 'package:bookmywages/model/vendor_model/vendor_booking_model.dart'; import 'package:bookmywages/model/vendor_model/vendor_catgories_model.dart' show VendorCategoriesModel; import 'package:bookmywages/model/vendor_model/vendor_profile_model.dart'; import 'package:bookmywages/model/vendor_model/vendor_service_model.dart'; import 'package:bookmywages/model/vendor_model/vendor_serviceupload_model.dart'; import 'package:bookmywages/model/vendor_model/vendorregister_model.dart'; import 'package:bookmywages/viewmodel/consts_api.dart'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:http/http.dart' as http; import 'package:image_picker/image_picker.dart'; import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:http_parser/http_parser.dart'; // Add this at the top class AuthRepository { Future> loginUser( String url, LoginModel loginModel, ) async { try { final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode(loginModel.toJson()), ); if (response.statusCode == 200) { final decoded = jsonDecode(response.body); print('Decoded Response: $decoded'); // Debug print dynamic data = decoded['data']; String userId = ''; String isverify = ''; // Handle different response structures if (data is List && data.isNotEmpty) { // Response contains a list of data userId = data[0]['id']?.toString() ?? ''; isverify = data[0]['is_verified']?.toString() ?? ''; } else if (data is Map) { // Response contains a single data object userId = data['id']?.toString() ?? ''; isverify = data['is_verified']?.toString() ?? ''; // Check if vendor_id exists in the data map } else { throw Exception('Unexpected response structure: $data'); } // Store both values in SharedPreferences final prefs = await SharedPreferences.getInstance(); await prefs.setString('userId', userId); await prefs.setString('is_verified', isverify); // Also store the full data JSON string for other operations await prefs.setString('data', userId); return decoded; } else { throw Exception( 'Login failed with status code ${response.statusCode}: ${response.body}', ); } } catch (e) { print('Login error: $e'); throw Exception('Login failed: $e'); } } Future> registerUser( String url, Map data, ) async { final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode(data), ); if (response.statusCode == 200) { final decoded = jsonDecode(response.body); print('Decoded Response: $decoded'); // Debug print dynamic data = decoded['data']; String userId; String isverify; if (data is List && data.isNotEmpty) { userId = data[0]['id'].toString(); isverify = data[0]['is_verified'].toString(); } else if (data is Map && data.containsKey('id')) { userId = data['id'].toString(); isverify = data['is_verified'].toString(); } else { throw Exception('Unexpected response structure: $data'); } final prefs = await SharedPreferences.getInstance(); await prefs.setString('userId', userId); await prefs.setString('is_verified', isverify); return decoded; } else { throw Exception('Register failed: ${response.body}'); } } // Add to AuthRepository class Future> verifyOtp(String url, OtpModel otpModel) async { final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode(otpModel.toJson()), ); if (response.statusCode == 200) { final decoded = jsonDecode(response.body); print('OTP Verify Response: $decoded'); // Directly store is_verified = '1' final prefs = await SharedPreferences.getInstance(); await prefs.setString('is_verified', '1'); return decoded; } else { throw Exception('OTP verification failed: ${response.body}'); } } } class BannerRepository { Future> fetchBanners(String url) async { try { final response = await http .get(Uri.parse(url)) .timeout(const Duration(seconds: 10)); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); final List bannerList = jsonBody['data'] ?? []; return bannerList.map((json) => BannerModel.fromJson(json)).toList(); } else { return []; // return empty list on server error } } catch (_) { return []; // return empty list on timeout or any error } } } class CategoryRepository { Future> fetchCategories(String url) async { final response = await http.get(Uri.parse(url)); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); final List categoryList = jsonBody['data'] ?? []; return categoryList .map((json) => CategoriesModel.fromJson(json)) .toList(); } else { throw Exception('Failed to load categories'); } } } class packageRepository { Future> fetchpackage(String url) async { final response = await http.get(Uri.parse(url)); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); final List packagelist = jsonBody['data'] ?? []; return packagelist.map((json) => PackageModel.fromJson(json)).toList(); } else { throw Exception('Failed to load categories'); } } } class SubcategoryRepository { Future> fetchSubcategories( String url, String categoryId, ) async { final prefs = await SharedPreferences.getInstance(); final userId = prefs.getString('userId') ?? ''; final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'userId': userId, 'category': categoryId}), ); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); final List list = jsonBody['data'] ?? []; return list.map((e) => SubcategoryModel.fromJson(e)).toList(); } else { throw Exception('Failed to load subcategories'); } } } class ProfilegetRepository { Future> fetchprofileget(String url) async { // Retrieve userId from SharedPreferences final prefs = await SharedPreferences.getInstance(); final userId = prefs.getString('userId') ?? ''; // Debugging: Print the userId to make sure it's correct print('User ID: $userId'); // Check if userId is available before making the request if (userId.isEmpty) { throw Exception('User ID is not available.'); } // Send the userId in the API request final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'user_id': userId}), ); // Check if the response status code is successful if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); final List list = jsonBody['data'] ?? []; // Convert the JSON response to a list of ProfileGetModel objects return list.map((e) => ProfileGetModel.fromJson(e)).toList(); } else { // Handle API errors throw Exception( 'Failed to load profile data, status code: ${response.statusCode}', ); } } } class ServiceRepository { Future> fetchService( String url, String categoryId, [ String? subcategoryId, String? selecttype, // Optional parameter in square brackets ]) async { final prefs = await SharedPreferences.getInstance(); final userId = prefs.getString('userId') ?? ''; // Create the request body Map requestBody = { 'user_id': userId, 'category': categoryId, }; // Add subcategory only if it's provided if (subcategoryId != null && subcategoryId.isNotEmpty) { requestBody['subcategory'] = subcategoryId; } if (selecttype != null && selecttype.isNotEmpty) { requestBody['service_type'] = selecttype; } print('Request Body: $requestBody'); // Debug log final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode(requestBody), ); if (response.statusCode == 200) { print('Response: ${response.body}'); // Debug log final jsonBody = jsonDecode(response.body); final List list = jsonBody['data'] ?? []; return list.map((e) => ServiceModel.fromJson(e)).toList(); } else { throw Exception('Failed to load services: ${response.statusCode}'); } } } class ProfileupdateRepository { Future> fetchprofileupdate(String url) async { final prefs = await SharedPreferences.getInstance(); final userId = prefs.getString('userId') ?? ''; if (userId.isEmpty) throw Exception('User ID is not available.'); final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'user_id': userId}), ); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); final List list = jsonBody['data'] ?? []; return list.map((e) => ProfileGetModel.fromJson(e)).toList(); } else { throw Exception('Failed to load profile, code: ${response.statusCode}'); } } Future updateProfile({ required String url, required String name, required String number, required String email, required String address, File? imageFile, }) async { final prefs = await SharedPreferences.getInstance(); final userId = prefs.getString('userId') ?? ''; final request = http.MultipartRequest('POST', Uri.parse(url)); request.fields['user_id'] = userId; request.fields['name'] = name; request.fields['number'] = number; request.fields['email'] = email; request.fields['address'] = address; if (imageFile != null) { request.files.add( await http.MultipartFile.fromPath( 'profile_pic', // Make sure this matches your backend key imageFile.path, contentType: MediaType('image', 'jpeg'), // Optional but recommended ), ); } final response = await request.send(); if (response.statusCode != 200) { throw Exception('Failed to update profile'); } } } class GetReviewRepository { Future> fetchgetreview( String url, String serviceId, ) async { Map requestBody = {'service_id': serviceId}; print('Request Body: $requestBody'); final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode(requestBody), ); if (response.statusCode == 200) { print('Response: ${response.body}'); final jsonBody = jsonDecode(response.body); final List list = jsonBody['data'] ?? []; return list.map((e) => GetReviewModel.fromJson(e)).toList(); } else { throw Exception('Failed to load reviews: ${response.statusCode}'); } } } class MostPopularRepository { Future> fetchMostPopular( String url, String categoryId, ) async { final prefs = await SharedPreferences.getInstance(); final userId = prefs.getString('userId') ?? ''; Map requestBody = { 'user_id': userId, 'category': categoryId, }; final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode(requestBody), ); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); final List list = jsonBody['data'] ?? []; return list.map((e) => MostPopularModel.fromJson(e)).toList(); } else { throw Exception('Failed to load services: ${response.statusCode}'); } } } class EnquriyRepository { Future> fetchenquriy(String url) async { final prefs = await SharedPreferences.getInstance(); final userId = prefs.getString('userId') ?? ''; if (userId.isEmpty) throw Exception('User ID is not available.'); final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'user_id': userId}), ); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); final List list = jsonBody['data'] ?? []; return list.map((e) => EnquiryModel.fromJson(e)).toList(); } else { throw Exception('Failed to load profile, code: ${response.statusCode}'); } } Future updateEnquriy({ required BuildContext context, required String url, required String name, required String number, required String email, required String message, required String serviceid, }) async { final prefs = await SharedPreferences.getInstance(); final userId = prefs.getString('userId') ?? ''; final request = http.MultipartRequest('POST', Uri.parse(url)); request.fields['user_id'] = userId; request.fields['name'] = name; request.fields['mobile'] = number; request.fields['email'] = email; request.fields['message'] = message; request.fields['service_id'] = serviceid; final response = await request.send(); if (response.statusCode == 200) { Fluttertoast.showToast( msg: "Enquiry Successful", toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, ); } else { Fluttertoast.showToast( msg: "Failed to update profile", toastLength: Toast.LENGTH_LONG, gravity: ToastGravity.BOTTOM, ); throw Exception('Failed to update profile'); } } } class BookingRepository { Future> fetchBooking(String url) async { final prefs = await SharedPreferences.getInstance(); final userId = prefs.getString('userId') ?? ''; if (userId.isEmpty) throw Exception('User ID is not available.'); final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'user_id': userId}), ); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); final List list = jsonBody['data'] ?? []; return list.map((e) => BookingModel.fromJson(e)).toList(); } else { throw Exception( 'Failed to load booking data. Code: ${response.statusCode}', ); } } Future updateBooking({ required BuildContext context, required String url, required String name, required String number, required String email, required String message, required String address, required String date, required String time, required String serviceId, }) async { final prefs = await SharedPreferences.getInstance(); final userId = prefs.getString('userId') ?? ''; if (userId.isEmpty) { Fluttertoast.showToast( msg: "User ID not found", toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, ); return; } final request = http.MultipartRequest('POST', Uri.parse(url)); request.fields['user_id'] = userId; request.fields['name'] = name; request.fields['mobile_number'] = number; request.fields['email'] = email; request.fields['message'] = message; request.fields['service_id'] = serviceId; request.fields['address'] = address; request.fields['service_date'] = date; request.fields['service_time'] = time; final response = await request.send(); final responseBody = await response.stream.bytesToString(); final jsonResponse = jsonDecode(responseBody); if (response.statusCode == 200) { Fluttertoast.showToast( msg: "Booking Successful", toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, ); } else if (response.statusCode == 404 && jsonResponse['data'] == 'You can book maximum 1 services.') { Fluttertoast.showToast( msg: jsonResponse['data'], toastLength: Toast.LENGTH_LONG, gravity: ToastGravity.BOTTOM, ); } else { Fluttertoast.showToast( msg: jsonResponse['data'] ?? 'Failed to submit enquiry. Code: ${response.statusCode}', toastLength: Toast.LENGTH_LONG, gravity: ToastGravity.BOTTOM, ); throw Exception('Failed to update booking'); } } } // ignore: camel_case_types class updatereviewRepository { Future> updatereview(String url) async { final prefs = await SharedPreferences.getInstance(); final userId = prefs.getString('userId') ?? ''; if (userId.isEmpty) throw Exception('User ID is not available.'); final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'user_id': userId}), ); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); final List list = jsonBody['data'] ?? []; return list.map((e) => GetReviewModel.fromJson(e)).toList(); } else { throw Exception( 'Failed to load booking data. Code: ${response.statusCode}', ); } } Future updatereviews({ required BuildContext context, required String url, required String review, required String serviceId, }) async { final prefs = await SharedPreferences.getInstance(); final userId = prefs.getString('userId') ?? ''; if (userId.isEmpty) { Fluttertoast.showToast( msg: "User ID not found", toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, ); return; } final request = http.MultipartRequest('POST', Uri.parse(url)); request.fields['user_id'] = userId; request.fields['review'] = review; request.fields['service_id'] = serviceId; final response = await request.send(); final responseBody = await response.stream.bytesToString(); final jsonResponse = jsonDecode(responseBody); if (response.statusCode == 200) { Fluttertoast.showToast( msg: "Review submitted successfully", toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, backgroundColor: Colors.green, textColor: Colors.white, ); } else { Fluttertoast.showToast( msg: jsonResponse['data'] ?? 'Failed to submit review. Code: ${response.statusCode}', toastLength: Toast.LENGTH_LONG, gravity: ToastGravity.BOTTOM, backgroundColor: Colors.red, textColor: Colors.white, ); throw Exception('Failed to update review'); } } } class enquriylistRepository { Future> fetchenquirylist(String url) async { final prefs = await SharedPreferences.getInstance(); final userId = prefs.getString('userId') ?? ''; if (userId.isEmpty) throw Exception('User ID is not available.'); final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'user_id': userId}), ); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); final List list = jsonBody['data'] ?? []; return list.map((e) => EnquiryListModel.fromJson(e)).toList(); } else { throw Exception( 'Failed to load booking data. Code: ${response.statusCode}', ); } } } class EnquriydeleteRepository { Future enquriydelete(String url, String id) async { final prefs = await SharedPreferences.getInstance(); final userId = prefs.getString('userId') ?? ''; final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'user_id': userId, 'id': id}), ); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); // Assuming your API returns a success flag or message // Adjust this based on your actual API response return jsonBody['success'] == true || jsonBody['status'] == 'success' || jsonBody['message']?.toLowerCase().contains('success') == true; } else { throw Exception('Failed to delete enquiry'); } } } class PaymentdetailsRepository { Future> fetchpayment(String url) async { final prefs = await SharedPreferences.getInstance(); final userId = prefs.getString('userId') ?? ''; final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'user_id': userId}), ); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); final List dataList = jsonBody['data'] ?? []; return dataList .map((json) => PaymentDetailsModel.fromJson(json)) .toList(); } else { throw Exception('Failed to fetch payment details'); } } } class PaymentdeleteRepository { Future paymentdelete(String url, String id) async { final prefs = await SharedPreferences.getInstance(); final userId = prefs.getString('userId') ?? ''; final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'user_id': userId, 'id': id}), ); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); // Assuming your API returns a success flag or message // Adjust this based on your actual API response return jsonBody['success'] == true || jsonBody['status'] == 'success' || jsonBody['message']?.toLowerCase().contains('success') == true; } else { throw Exception('Failed to delete payment Details'); } } } class ChangepasswordRepository { Future changepassword(String url, String password) async { final prefs = await SharedPreferences.getInstance(); final userId = prefs.getString('userId') ?? ''; final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'user_id': userId, 'password': password}), ); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); // Assuming your API returns a success flag or message // Adjust this based on your actual API response return jsonBody['success'] == true || jsonBody['status'] == 'success' || jsonBody['message']?.toLowerCase().contains('success') == true; } else { throw Exception('Failed to changepassword'); } } } class UserhistoryBookingdetailsRepository { Future> fetchhistorybooking(String url) async { final prefs = await SharedPreferences.getInstance(); final userId = prefs.getString('userId') ?? ''; final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'user_id': userId}), ); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); print(response.body); print(jsonBody); final List dataList = jsonBody['data'] ?? []; return dataList.map((json) => UserBookingDetails.fromJson(json)).toList(); } else { throw Exception('Failed to fetch payment details'); } } } class CancelbookingRepository { Future cancelbooking({ required String url, required String id, required String serviceId, required String type, }) async { final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'id': id, 'service_id': serviceId, 'type': type}), ); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); return jsonBody['success'] == true || jsonBody['status'] == 'success' || jsonBody['message']?.toLowerCase().contains('success') == true; } else { throw Exception('Failed to cancel booking'); } } } class BookingmodifyRepository { Future bookingmodify({ required String url, required String id, required String serviceId, required String servicedate, required String servicetime, }) async { final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode({ 'id': id, 'service_id': serviceId, 'service_date': servicedate, 'service_time': servicetime, }), ); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); return jsonBody['success'] == true || jsonBody['status'] == 'success' || jsonBody['message']?.toLowerCase().contains('success') == true; } else { throw Exception('Failed to modify'); } } } class ExpiredPlanRepository { Future fetchExpiredPlan(String url) async { final prefs = await SharedPreferences.getInstance(); final userId = prefs.getString('userId') ?? ''; final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'user_id': userId}), ); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); final Map data = jsonBody['data'] ?? {}; return ExpiredPlanModel.fromJson(data); } else { throw Exception('Failed to fetch expired plan'); } } } class PlanSuccessRepository { Future planSuccess({ required String url, required String userId, required String planId, required int duration, required int type, }) async { final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode({ 'user_id': userId, 'plan_id': planId, 'duration': duration.toString(), 'type': type.toString(), }), ); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); return jsonBody['success'] == true || jsonBody['status'] == 'success' || jsonBody['message']?.toLowerCase().contains('success') == true; } else { throw Exception('Failed to subscribe to plan'); } } } ////vendor flow class GetvendorId { Future fetchGetid(String url) async { final prefs = await SharedPreferences.getInstance(); final userId = prefs.getString('userId') ?? ''; final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'user_id': userId}), ); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); final data = jsonBody['data']; // Extract and store vendor_id and vendor_status if not null if (data != null) { final vendorId = data['vendor_id']?.toString() ?? ''; final vendorStatus = data['vendor_status']?.toString() ?? ''; await prefs.setString('vendor_id', vendorId); await prefs.setString('vendor_status', vendorStatus); print('Stored vendor_id: $vendorId'); print('Stored vendor_status: $vendorStatus'); } return data; } else { throw Exception('Failed to fetch vendor data'); } } } class VendorRegister { Future> vendorRegister( String url, VendorRegisterModel vendorModel, ) async { final prefs = await SharedPreferences.getInstance(); final userId = prefs.getString('userId') ?? ''; vendorModel.userId = userId; // inject userId into the model final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode(vendorModel.toJson()), ); if (response.statusCode == 200) { final decoded = jsonDecode(response.body); print('Decoded Response: $decoded'); return decoded; } else { throw Exception('Register failed: ${response.body}'); } } } class VendorGetProfile { Future> vendorGetProfile(String url) async { try { final prefs = await SharedPreferences.getInstance(); final dataId = prefs.getString('vendor_id') ?? ''; if (dataId.isEmpty) { throw Exception('Data ID not found in SharedPreferences.'); } final body = jsonEncode({'dataId': dataId}); final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: body, ); if (response.statusCode == 200) { final decoded = jsonDecode(response.body); print('Decoded Response: $decoded'); return decoded; // Return the decoded response directly } else { throw Exception( 'Get profile failed with status ${response.statusCode}: ${response.body}', ); } } catch (e) { print('Error in vendorGetProfile: $e'); rethrow; } } } class Vendorserviceupload { Future> vendorserviceupload( String url, VendorServiceUploadModel serviceupload, List images, ) async { try { // Create a multipart request for uploading files and data var request = http.MultipartRequest('POST', Uri.parse(url)); // Add service data as fields request.fields['vendor_id'] = serviceupload.id; request.fields['vendorname'] = serviceupload.vendorName; request.fields['servicename'] = serviceupload.serviceName; request.fields['service_type'] = serviceupload.serviceType; request.fields['category'] = serviceupload.category; request.fields['subcategory'] = serviceupload.subcategory; request.fields['workinghours'] = serviceupload.workingHours; request.fields['workingduration'] = serviceupload.workingDuration; request.fields['amount'] = serviceupload.amount.toString(); request.fields['location'] = serviceupload.location; request.fields['description'] = serviceupload.description ?? ''; request.fields['details'] = serviceupload.details; // Add video links as JSON array if (serviceupload.videos.isNotEmpty) { request.fields['videos'] = jsonEncode(serviceupload.videos); } // Add images as files with array notation for (int i = 0; i < images.length; i++) { final file = images[i]; final fileName = 'image_${i}_${DateTime.now().millisecondsSinceEpoch}.jpg'; // Get file extension from path String extension = file.path.split('.').last.toLowerCase(); String mimeType = 'image/jpeg'; // Default // Set correct mime type based on extension if (extension == 'png') { mimeType = 'image/png'; } else if (extension == 'jpg' || extension == 'jpeg') { mimeType = 'image/jpeg'; } else if (extension == 'gif') { mimeType = 'image/gif'; } else if (extension == 'webp') { mimeType = 'image/webp'; } // Add the file to the request with indexed array notation request.files.add( http.MultipartFile( 'images[$i]', // Indexed array notation for multiple images file.readAsBytes().asStream(), file.lengthSync(), filename: fileName, contentType: MediaType.parse(mimeType), ), ); } // Send the request var streamedResponse = await request.send(); var response = await http.Response.fromStream(streamedResponse); // Parse response regardless of status code try { final decoded = jsonDecode(response.body) as Map; print('Decoded Response: $decoded'); // Check if request was successful if (response.statusCode == 200 || response.statusCode == 201) { return decoded; } else { // For failed requests, extract error message String errorMessage = decoded['data']?.toString() ?? decoded['message']?.toString() ?? 'Upload failed with status: ${response.statusCode}'; // Return the error response for handling upstream return { 'success': false, 'error': errorMessage, 'statusCode': response.statusCode, 'data': decoded, }; } } catch (jsonError) { // If JSON parsing fails, use raw response String errorMessage = 'Request failed with status: ${response.statusCode}. Response: ${response.body}'; Fluttertoast.showToast( msg: 'Server response error', toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, ); throw Exception(errorMessage); } } on SocketException catch (e) { String errorMessage = 'No internet connection: ${e.message}'; Fluttertoast.showToast( msg: 'No internet connection', toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, ); throw Exception(errorMessage); } on http.ClientException catch (e) { String errorMessage = 'Network error: ${e.message}'; Fluttertoast.showToast( msg: 'Network error occurred', toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, ); throw Exception(errorMessage); } on FormatException catch (e) { String errorMessage = 'Invalid response format: ${e.message}'; Fluttertoast.showToast( msg: 'Invalid server response', toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, ); throw Exception(errorMessage); } catch (e) { String errorMessage = 'Unexpected error: $e'; Fluttertoast.showToast( msg: 'Something went wrong', toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, ); throw Exception(errorMessage); } } } class VendorbookingdetailsRepository { Future> fetchvendorbooking(String url) async { final prefs = await SharedPreferences.getInstance(); final userId = prefs.getString('vendor_id') ?? ''; final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'vendor_id': userId}), ); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); print(response.body); print(jsonBody); final List dataList = jsonBody['data'] ?? []; return dataList.map((json) => VendorBookingModel.fromJson(json)).toList(); } else { throw Exception('Failed to fetch booking details'); } } } class ChangebookingstatusRepository { Future changebooking({ required String url, required String id, required String status, }) async { final prefs = await SharedPreferences.getInstance(); final userId = prefs.getString('vendor_id') ?? ''; final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'id': id, 'user_id': userId, 'status': status}), ); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); return jsonBody['success'] == true || jsonBody['status'] == 'success' || jsonBody['message']?.toLowerCase().contains('success') == true; } else { throw Exception('Failed to update booking status'); } } } class VendorcatgoriesRepository { Future> fetchvendorcat(String url) async { final prefs = await SharedPreferences.getInstance(); final userId = prefs.getString('vendor_id') ?? ''; final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'vendor_id': userId}), ); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); final List dataList = jsonBody['data'] ?? []; return dataList .map((json) => VendorCategoriesModel.fromJson(json)) .toList(); } else { throw Exception('Failed to fetch booking details'); } } } class VendorserviceRepository { Future> fetchvendorservice(String url) async { try { final prefs = await SharedPreferences.getInstance(); final userId = prefs.getString('vendor_id') ?? ''; if (userId.isEmpty) { throw Exception('Vendor ID not found in SharedPreferences'); } final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'vendor_id': userId}), ); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); // Add more detailed logging if (jsonBody['data'] == null) { return []; } final List dataList = jsonBody['data'] ?? []; return dataList .map((json) => VendorServiceModel.fromJson(json)) .toList(); } else { throw Exception( 'Failed to fetch service details: ${response.statusCode}', ); } } catch (e) { throw Exception('Failed to fetch service details: $e'); } } } // Updated API Service Class class Vendorserviceupadate { Future> vendorserviceupdate( String url, VendorServiceUploadModel serviceupload, List newImages, ) async { try { var request = http.MultipartRequest('POST', Uri.parse(url)); // Add all basic fields request.fields.addAll({ 'id': serviceupload.id, 'vendorname': serviceupload.vendorName, 'servicename': serviceupload.serviceName, 'service_type': serviceupload.serviceType, 'category': serviceupload.category, 'subcategory': serviceupload.subcategory, 'workinghours': serviceupload.workingHours, 'workingduration': serviceupload.workingDuration, 'amount': serviceupload.amount.toString(), 'location': serviceupload.location, 'description': serviceupload.description ?? '', 'details': serviceupload.details, }); // Handle videos - send as JSON string if (serviceupload.videos.isNotEmpty) { request.fields['videos'] = jsonEncode(serviceupload.videos); print('Videos JSON: ${request.fields['videos']}'); } // Process ALL images (existing + new) as multipart files List allImageFiles = []; // Step 1: Convert existing images (URLs) to files if needed if (serviceupload.images.isNotEmpty) { print('Processing ${serviceupload.images.length} existing images...'); for (int i = 0; i < serviceupload.images.length; i++) { final imageUrl = serviceupload.images[i]; print('Processing existing image $i: $imageUrl'); try { // If it's already a local file path if (imageUrl.startsWith('/') || imageUrl.startsWith('file://')) { final file = File(imageUrl.replaceFirst('file://', '')); if (await file.exists()) { allImageFiles.add(file); } else { print('⚠ Existing file not found: ${file.path}'); } } // If it's a URL, download it else if (imageUrl.startsWith('http')) { final downloadedFile = await _downloadImageAsFile(imageUrl, i); if (downloadedFile != null) { allImageFiles.add(downloadedFile); print( '✓ Downloaded and added existing image: ${downloadedFile.path}', ); } else { print('⚠ Failed to download existing image: $imageUrl'); } } // If it's a relative path or asset else { print('⚠ Unsupported existing image format: $imageUrl'); } } catch (e) { print('⚠ Error processing existing image $imageUrl: $e'); } } } // Step 2: Add new images to the list allImageFiles.addAll(newImages); print('Total images to upload: ${allImageFiles.length}'); // Step 3: Upload all images with same field name 'images[]' if (allImageFiles.isNotEmpty) { print('Processing ${allImageFiles.length} total images for upload...'); for (int i = 0; i < allImageFiles.length; i++) { final file = allImageFiles[i]; // Validate file exists if (!await file.exists()) { print('WARNING: File does not exist: ${file.path}'); continue; } // Get file info final fileSize = await file.length(); final fileName = file.path.split('/').last; final extension = fileName.split('.').last.toLowerCase(); print('Processing file $i: $fileName ($fileSize bytes)'); // Validate file size (5MB limit) if (fileSize > 5 * 1024 * 1024) { continue; } // Determine MIME type String mimeType; switch (extension) { case 'png': mimeType = 'image/png'; break; case 'jpg': case 'jpeg': mimeType = 'image/jpeg'; break; case 'gif': mimeType = 'image/gif'; break; case 'webp': mimeType = 'image/webp'; break; default: mimeType = 'image/jpeg'; } // Create unique filename for upload final uploadFileName = 'upload_${DateTime.now().millisecondsSinceEpoch}_$i.$extension'; try { // Method 1: Try fromPath (preferred) var multipartFile = await http.MultipartFile.fromPath( 'images[]', // All images use same field name file.path, filename: uploadFileName, contentType: MediaType.parse(mimeType), ); request.files.add(multipartFile); } catch (fromPathError) { // Method 2: Fallback to fromBytes try { final bytes = await file.readAsBytes(); var multipartFile = http.MultipartFile.fromBytes( 'images[]', bytes, filename: uploadFileName, contentType: MediaType.parse(mimeType), ); request.files.add(multipartFile); // ignore: empty_catches } catch (fromBytesError) {} } } } else { print('No images to upload'); } // Add headers request.headers.addAll({ 'Accept': 'application/json', 'Content-Type': 'multipart/form-data', }); // Debug request info print('=== REQUEST SUMMARY ==='); print('Fields: ${request.fields.keys.toList()}'); print('Files: ${request.files.length}'); for (var file in request.files) { print( ' - Field: ${file.field}, Filename: ${file.filename}, Length: ${file.length}', ); } // Send request with timeout print('Sending request...'); var streamedResponse = await request.send().timeout( Duration(seconds: 120), // 2 minute timeout for large uploads onTimeout: () { throw TimeoutException('Upload timeout after 2 minutes'); }, ); var response = await http.Response.fromStream(streamedResponse); print('=== RESPONSE INFO ==='); print('Status Code: ${response.statusCode}'); print('Response Headers: ${response.headers}'); print('Response Body Length: ${response.body.length}'); print( 'Response Body Preview: ${response.body.length > 500 ? "${response.body.substring(0, 500)}..." : response.body}', ); // Handle response if (response.statusCode >= 200 && response.statusCode < 300) { try { final decoded = jsonDecode(response.body) as Map; print('✓ Successfully parsed JSON response'); return decoded; } catch (jsonError) { print('⚠ JSON parsing failed: $jsonError'); // Return success with raw response if JSON parsing fails but status is OK return { 'success': true, 'message': 'Update completed successfully', 'raw_response': response.body, }; } } else { // Handle error response String errorMessage; try { final errorResponse = jsonDecode(response.body); errorMessage = errorResponse['message'] ?? errorResponse['error'] ?? 'Server returned status ${response.statusCode}'; } catch (e) { errorMessage = 'Server error (${response.statusCode}): ${response.body}'; } print('✗ Server error: $errorMessage'); throw Exception(errorMessage); } } on TimeoutException catch (e) { print('✗ Timeout error: $e'); throw Exception( 'Upload timeout. Please check your connection and try again.', ); } on SocketException catch (e) { print('✗ Network error: $e'); throw Exception('Network error. Please check your internet connection.'); } on http.ClientException catch (e) { print('✗ HTTP client error: $e'); throw Exception('Connection error: ${e.message}'); } catch (e) { print('✗ Unexpected error: $e'); rethrow; } } // Helper method to download existing images from URLs Future _downloadImageAsFile(String imageUrl, int index) async { try { final response = await http.get(Uri.parse(imageUrl)); if (response.statusCode == 200) { // Get app's temporary directory final directory = await getTemporaryDirectory(); // Determine file extension from URL or content type String extension = 'jpg'; if (imageUrl.contains('.png')) { extension = 'png'; } else if (imageUrl.contains('.gif')) extension = 'gif'; else if (imageUrl.contains('.webp')) extension = 'webp'; else if (response.headers['content-type']?.contains('png') == true) extension = 'png'; // Create temporary file final fileName = 'existing_image_${index}_${DateTime.now().millisecondsSinceEpoch}.$extension'; final file = File('${directory.path}/$fileName'); // Write downloaded bytes to file await file.writeAsBytes(response.bodyBytes); return file; } else { print('Failed to download image: ${response.statusCode}'); return null; } } catch (e) { print('Error downloading image $imageUrl: $e'); return null; } } } // Image Upload Helper Class class ImageUploadHelper { static Future> pickMultipleImages() async { try { final picker = ImagePicker(); final List pickedFiles = await picker.pickMultiImage( maxWidth: 1920, maxHeight: 1920, imageQuality: 85, ); List validFiles = []; for (XFile xFile in pickedFiles) { File file = File(xFile.path); if (await validateImageFile(file)) { validFiles.add(file); } else { print('Invalid image file: ${xFile.name}'); } } return validFiles; } catch (e) { print('Error picking images: $e'); return []; } } static Future validateImageFile(File file) async { try { if (!await file.exists()) { print('File does not exist: ${file.path}'); return false; } // Check file size (max 5MB) int fileSize = await file.length(); if (fileSize > 5 * 1024 * 1024) { print('File too large: ${file.path} ($fileSize bytes)'); return false; } // Check file extension String extension = file.path.split('.').last.toLowerCase(); List validExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp']; if (!validExtensions.contains(extension)) { print('Invalid file extension: $extension'); return false; } return true; } catch (e) { print('Error validating file: $e'); return false; } } } class VendorExpiredPlanRepository { Future fetchvendorExpiredPlan(String url) async { final prefs = await SharedPreferences.getInstance(); final userId = prefs.getString('vendor_id') ?? ''; final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'user_id': userId}), ); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); final Map data = jsonBody['data'] ?? {}; return ExpiredPlanModel.fromJson(data); } else { throw Exception('Failed to fetch expired plan'); } } } class DetailpageRepository { Future> fetchDetailpage( String url, int serviceid, ) async { final response = await http.get( Uri.parse('$url?service_id=$serviceid'), headers: {'Content-Type': 'application/json'}, ); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); final data = jsonBody['data']; // Handle single object or list if (data is List) { return data.map((e) => DetailPageModel.fromJson(e)).toList(); } else if (data is Map) { return [DetailPageModel.fromJson(data)]; } else { return []; } } else { throw Exception('Failed to load detail page'); } } } class CatManagementDeleteRepository { Future deleteCategoryManagement(String url, String categoryId) async { final prefs = await SharedPreferences.getInstance(); final vendorId = prefs.getString('vendor_id') ?? ''; try { final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'vendor_id': vendorId, 'category': categoryId}), ); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); return jsonBody['success'] == true || jsonBody['status'] == 'success' || jsonBody['message']?.toLowerCase().contains('success') == true; } else { throw Exception('Failed to delete category: ${response.statusCode}'); } } catch (e) { throw Exception('Network error: $e'); } } } class SerciceDeleteRepository { Future deleteService(String url, String id) async { final prefs = await SharedPreferences.getInstance(); final vendorId = prefs.getString('vendor_id') ?? ''; try { final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'vendor_id': vendorId, 'id': id}), ); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); return jsonBody['success'] == true || jsonBody['status'] == 'success' || jsonBody['message']?.toLowerCase().contains('success') == true; } else { throw Exception('Failed to delete service: ${response.statusCode}'); } } catch (e) { throw Exception('Network error: $e'); } } } class ProfilegetvendorRepository { Future> fetchprofilegetvendor(String url) async { // Retrieve userId from SharedPreferences final prefs = await SharedPreferences.getInstance(); final vendorid = prefs.getString('vendor_id') ?? ''; // Debugging: Print the userId to make sure it's correct print('vendor_id: $vendorid'); // Check if userId is available before making the request if (vendorid.isEmpty) { throw Exception('User ID is not available.'); } // Send the userId in the API request final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'vendor_id': vendorid}), ); // Check if the response status code is successful if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); final List list = jsonBody['data'] ?? []; // Convert the JSON response to a list of ProfileGetModel objects return list.map((e) => ProfileGetModel.fromJson(e)).toList(); } else { // Handle API errors throw Exception( 'Failed to load profile data, status code: ${response.statusCode}', ); } } } class ProfileupdatevendorRepository { Future> fetchProfileupdatevendor(String url) async { final prefs = await SharedPreferences.getInstance(); final vendorid = prefs.getString('vendor_id') ?? ''; if (vendorid.isEmpty) throw Exception('User ID is not available.'); final response = await http.post( Uri.parse(url), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'vendor_id': vendorid}), ); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); final List list = jsonBody['data'] ?? []; return list.map((e) => VendorProfileModel.fromJson(e)).toList(); } else { throw Exception('Failed to load profile, code: ${response.statusCode}'); } } Future updateProfilevendor({ required String url, required String name, required String number, required String email, required String address, File? imageFile, }) async { final prefs = await SharedPreferences.getInstance(); final userId = prefs.getString('vendor_id') ?? ''; final request = http.MultipartRequest('POST', Uri.parse(url)); request.fields['vendor_id'] = userId; request.fields['name'] = name; request.fields['number'] = number; request.fields['email'] = email; request.fields['address'] = address; if (imageFile != null) { request.files.add( await http.MultipartFile.fromPath( 'profile_pic', // Make sure this matches your backend key imageFile.path, contentType: MediaType('image', 'jpeg'), // Optional but recommended ), ); } final response = await request.send(); if (response.statusCode != 200) { throw Exception('Failed to update profile'); } } } class TermsAndConditionsRepository { Future fetchTermsAndCondition(String url) async { try { final response = await http.get(Uri.parse(url)); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); return TermsAndConditionsModel.fromJson(jsonBody); } else { throw Exception( 'Failed to load terms and conditions, status code: ${response.statusCode}', ); } } catch (e) { throw Exception('Failed to fetch terms and conditions: $e'); } } } class PrivacypolicyRepository { Future fetchPrivacypolicy(String url) async { try { final response = await http.get(Uri.parse(url)); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); return TermsAndConditionsModel.fromJson(jsonBody); } else { throw Exception( 'Failed to load terms and conditions, status code: ${response.statusCode}', ); } } catch (e) { throw Exception('Failed to fetch terms and conditions: $e'); } } } class NotificationRepository { Future> fetchNotification({ required int type, required String userId, }) async { try { final uri = Uri.parse(ConstsApi.notification); final response = await http.post( uri, headers: {'Content-Type': 'application/json'}, body: jsonEncode({'type': type, 'user_id': userId}), ); print('Fetching notification from: $uri'); print('Status Code: ${response.statusCode}'); print('Response Body: ${response.body}'); if (response.statusCode == 200) { final jsonBody = jsonDecode(response.body); // Handle if data is inside "data" key if (jsonBody is Map && jsonBody['data'] is List) { final List dataList = jsonBody['data']; return dataList .map((e) => NotificationModel.fromJson(e)) .toList(); } // Handle if directly a List if (jsonBody is List) { return jsonBody .map((e) => NotificationModel.fromJson(e)) .toList(); } return []; // no recognizable data } else { throw Exception('Status Code: ${response.statusCode}'); } } catch (e) { print('Error fetching notification: $e'); throw Exception('Failed to fetch notification: $e'); } } }