baseproject

This commit is contained in:
Gaurav Kumar
2025-09-06 19:21:52 +05:30
commit 01be9df2ed
254 changed files with 28342 additions and 0 deletions

View File

@@ -0,0 +1,384 @@
import 'dart:async';
import 'package:base_project/repository/auth_repo.dart';
import 'package:base_project/routes/route_names.dart';
import 'package:base_project/utils/flushbar_messages/flush_message_util.dart';
import 'package:base_project/utils/managers/user_manager.dart';
import 'package:base_project/utils/toast_messages/toast_message_util.dart';
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';
class AuthViewModel extends ChangeNotifier {
final AuthRepo _authRepo = AuthRepo();
bool _isLoading = false;
bool get isLoading => _isLoading;
setLoading(bool value) {
_isLoading = value;
notifyListeners();
}
Timer? _timer;
int _start = 30;
bool _isResendEnabled = false;
int get start => _start;
bool get isResendEnabled => _isResendEnabled;
void startTimer() {
_isResendEnabled = false;
_start = 30; // Set the timer duration (in seconds)
_timer = Timer.periodic(const Duration(seconds: 1), (Timer timer) {
if (_start == 0) {
_timer?.cancel();
_isResendEnabled = true;
notifyListeners();
} else {
_start--;
print(_start);
notifyListeners();
}
});
}
var accId = "";
var uEmail = "";
Future<void> login(BuildContext context, dynamic data) async {
setLoading(true);
try {
final value = await _authRepo.loginApi(data);
print('Value is -$value');
// Check the response structure
if (value['operationStatus'] == 'SUCCESS') {
final item = value['item'];
await UserManager().setUser(item);
// Try to persist sys account from login response if present
try {
final dynamic accountCandidate =
item['sysAccount'] ?? item['account'] ?? item['sys_account'];
if (accountCandidate is Map<String, dynamic>) {
await UserManager().setAccount(accountCandidate);
}
} catch (_) {}
final prefs = await SharedPreferences.getInstance();
prefs.setBool('isLoggedIn', true);
// Handle successful login
ToastMessageUtil.showToast(
message: 'Logged in as ${value['item']['email']}',
toastType: ToastType.general);
// nav to home
Navigator.pushReplacementNamed(context, RouteNames.homeView);
} else {
print('Error got ');
// Handle login failure with backend message if available
final backendMessage = (value is Map)
? (value['operationMessage'] ?? value['message'] ?? value['msg'])
: null;
final message = (backendMessage?.toString().trim().isNotEmpty == true)
? backendMessage.toString()
: "Failed!! Email or Password is incorrect";
FlushBarMessageUtil.showFlushBar(
context: context,
message: message,
flushBarType: FlushBarType.error,
);
}
} catch (error, stackTrace) {
print("Error -$error");
print("st -$stackTrace");
// Handle any other errors
FlushBarMessageUtil.showFlushBar(
context: context,
message: error.toString(),
flushBarType: FlushBarType.error,
);
} finally {
setLoading(false);
}
}
Future<void> signUp(BuildContext context, dynamic data) async {
setLoading(true);
final createUserData = {...data, 'account_id': accId};
print("CreateUserData--$createUserData");
_authRepo.createUserApi(createUserData).then((value) {
print(value);
ToastMessageUtil.showToast(
message: "Account Created", toastType: ToastType.success);
setLoading(false);
Navigator.pushReplacementNamed(context, RouteNames.loginView);
}).onError(
(error, stackTrace) {
print(error);
print(stackTrace);
FlushBarMessageUtil.showFlushBar(
context: context,
message: error.toString(),
flushBarType: FlushBarType.error);
setLoading(false);
},
);
}
// New method for combined registration flow (account + user)
Future<void> signUpWithAccountCreation(
BuildContext context, dynamic data) async {
setLoading(true);
try {
// Step 1: Create account with just email and password
final accountData = {
"email": data["email"],
};
print("Creating account with data: $accountData");
final accountResponse = await _authRepo.createAcApi(accountData);
print("Account creation response: $accountResponse");
// Extract account ID from response
final accountId = accountResponse['account_id']?.toString();
if (accountId == null) {
throw Exception("Account ID not received from server");
}
// Step 2: Create user with account ID
final createUserData = {...data, 'account_id': accountId};
print("Creating user with data: $createUserData");
final userResponse = await _authRepo.createUserApi(createUserData);
print("User creation response: $userResponse");
ToastMessageUtil.showToast(
message: "Account Created Successfully",
toastType: ToastType.success);
// Navigate to login
Navigator.pushReplacementNamed(context, RouteNames.loginView);
} catch (error, stackTrace) {
print("SignUp error: $error");
print("Stack trace: $stackTrace");
String errorMessage = "Registration failed. Please try again.";
if (error.toString().contains('already exist')) {
errorMessage = "Email already exists. Please use a different email.";
} else if (error.toString().contains('account_id')) {
errorMessage = "Account creation failed. Please try again.";
}
FlushBarMessageUtil.showFlushBar(
context: context,
message: errorMessage,
flushBarType: FlushBarType.error);
} finally {
setLoading(false);
}
}
Future<void> createAcc(BuildContext context, dynamic data) async {
setLoading(true);
_authRepo.createAcApi(data).then(
(value) {
print("Value--$value");
accId = value['account_id'].toString();
setLoading(false);
ToastMessageUtil.showToast(
message: "Success creating account", toastType: ToastType.success);
// Navigator.pushNamed(context, RouteNames.signUp,arguments: uEmail);
},
).onError(
(error, stackTrace) {
print("error--$error");
setLoading(false);
ToastMessageUtil.showToast(
message: "Error registering your account",
toastType: ToastType.error);
},
);
}
Future<void> getOtp(BuildContext context, dynamic data) async {
setLoading(true);
try {
final value = await _authRepo.getOtpApi(data);
print(value);
// Handle non-exception responses like { msg: "... already exist" }
if (value is Map) {
final map = Map<String, dynamic>.from(value);
final message = (map['msg'] ?? map['message'] ?? '').toString();
final operationStatus = (map['operationStatus'] ?? '').toString();
final otpSent = (map['otp_sent'] == true) || (map['otpSent'] == true);
// Block when backend says email already exists
if (message.toLowerCase().contains('already exist')) {
ToastMessageUtil.showToast(
message: 'Email already exists',
toastType: ToastType.error,
);
return; // do not navigate
}
// Consider success scenarios (handle variants like "Otp send successfully")
final lowerMsg = message.toLowerCase();
final messageLooksSuccess = lowerMsg.contains('otp sent') ||
lowerMsg.contains('sent successfully') ||
(lowerMsg.contains('otp') && lowerMsg.contains('success')) ||
lowerMsg.contains('send successfully');
if (operationStatus == 'SUCCESS' || otpSent || messageLooksSuccess) {
ToastMessageUtil.showToast(
message: message.isNotEmpty ? message : 'OTP sent successfully',
toastType: ToastType.success,
);
// Start resend countdown when OTP is sent
startTimer();
Navigator.pushNamed(
context,
RouteNames.verifyOtpView,
arguments: {'email': data['email']},
);
return;
}
// Fallback: show backend message if present
if (message.isNotEmpty) {
ToastMessageUtil.showToast(
message: message,
toastType: ToastType.error,
);
return;
}
}
// Unknown response structure
ToastMessageUtil.showToast(
message: 'Failed to send OTP',
toastType: ToastType.error,
);
} catch (error) {
// Handle any other exceptions
print("getOtp error: $error");
final msg = error.toString();
String uiMessage = 'Failed to send OTP';
if (msg.contains('already exist') || msg.contains('already exists')) {
uiMessage = 'Email already exists';
} else if (msg.contains('invalid') || msg.contains('Invalid')) {
uiMessage = 'Invalid email address';
} else if (msg.contains('timeout')) {
uiMessage = 'Request timed out. Please try again.';
}
ToastMessageUtil.showToast(
message: uiMessage,
toastType: ToastType.error,
);
} finally {
setLoading(false);
}
}
Future<void> resendOtp(BuildContext context, dynamic data) async {
startTimer();
await _authRepo.resendOtpApi(data).then((value) {
print(value);
ToastMessageUtil.showToast(
message: "OTP resend successfully",
toastType: ToastType.success,
);
}).onError(
(error, stackTrace) {
print(error);
ToastMessageUtil.showToast(
message: "Failed to resend OTP",
toastType: ToastType.error,
);
},
);
}
/// Returns null on success, or a human-readable error message on failure
Future<String?> verifyOtp(BuildContext context, dynamic queryParams) async {
print("Verifying otp");
setLoading(true);
try {
final value = await _authRepo.verifyOtpApi(queryParams);
print(value);
// Backend returns 200 with EntityResponse("OTP Verified")
// and 400 with EntityResponse("Wrong OTP") or MessageResponse("... not exist")
if (value is Map) {
final map = Map<String, dynamic>.from(value);
final message = (map['msg'] ?? map['message'] ?? '').toString();
final lower = message.toLowerCase();
if (lower.contains('otp verified')) {
ToastMessageUtil.showToast(
message: 'OTP Verified',
toastType: ToastType.success,
);
uEmail = queryParams['email'];
cancelTimer();
return null;
}
if (lower.contains('wrong otp')) {
ToastMessageUtil.showToast(
message: 'Wrong OTP',
toastType: ToastType.error,
);
return 'Wrong OTP';
}
if (lower.contains('not exist')) {
ToastMessageUtil.showToast(
message: 'User not found',
toastType: ToastType.error,
);
return 'User not found';
}
// Fallback: show message if present
if (message.isNotEmpty) {
ToastMessageUtil.showToast(
message: message,
toastType: ToastType.error,
);
return message;
}
}
ToastMessageUtil.showToast(
message: 'OTP verification failed',
toastType: ToastType.error,
);
return 'OTP verification failed';
} catch (error) {
print(error);
ToastMessageUtil.showToast(
message: "OTP verification failed",
toastType: ToastType.error,
);
return 'OTP verification failed';
} finally {
setLoading(false);
}
}
void cancelTimer() {
_timer?.cancel();
}
@override
void dispose() {
cancelTimer(); // Ensure the timer is cancelled
super.dispose();
}
}

View File

@@ -0,0 +1,209 @@
import 'dart:convert';
import 'dart:developer';
import 'package:base_project/data/response/api_response.dart';
import 'package:base_project/model/user/user_profile.dart';
import 'package:base_project/repository/profile/profile_repo.dart';
import 'package:base_project/utils/managers/user_manager.dart';
import 'package:base_project/utils/toast_messages/toast_message_util.dart';
import 'package:dio/dio.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:image_picker/image_picker.dart';
class ProfileViewModel extends ChangeNotifier {
final _repo = ProfileRepo();
Map<String, dynamic>? _sysAccount;
Map<String, dynamic>? get sysAccount => _sysAccount;
ApiResponse<UserProfile> userProfile = ApiResponse.loading();
Uint8List? _profileImageBytes; // Variable for storing fetched image bytes
bool _isUpdating = false;
bool get isUpdating => _isUpdating;
XFile? _profileImage;
XFile? get profileImg => _profileImage;
// Setters
setUserProfile(ApiResponse<UserProfile> val) {
userProfile = val;
notifyListeners();
}
setUpdating(bool val) {
_isUpdating = val;
notifyListeners();
}
setImage(XFile? val) {
_profileImage = val;
notifyListeners();
}
setProfileImageBytes(Uint8List? val) {
_profileImageBytes = val;
notifyListeners();
}
// Picking an image from the gallery
void pickImg(BuildContext context) async {
final ImagePicker picker = ImagePicker();
final XFile? image = await picker.pickImage(source: ImageSource.gallery);
if (image != null) {
setImage(image);
updateImage(context);
} else {
ToastMessageUtil.showToast(
message: "No image picked !!", toastType: ToastType.warning);
}
}
// Fetching the user profile details
Future<void> getProfile() async {
setUserProfile(ApiResponse.loading());
_repo.getProfileApi().then((value) {
log("Profile Data--$value");
setUserProfile(ApiResponse.success(UserProfile.fromJson(value)));
try {
_sysAccount =
UserManager().account ?? value['sysAccount'] ?? value['account'];
} catch (_) {}
}).onError((error, stackTrace) {
log("ERROR--$error");
setUserProfile(ApiResponse.error(error.toString()));
ToastMessageUtil.showToast(
message: "Error fetching profile data", toastType: ToastType.error);
});
}
Future<void> updateAccount(
BuildContext context, Map<String, dynamic> form) async {
setUpdating(true);
try {
final accId = UserManager().accountId;
if (accId == null) {
throw Exception('Account not found');
}
final body = {
'companyName': form['companyName'],
'workspace': form['workspace'],
'gstNumber': form['gstNumber'],
'mobile': form['mobile'],
'email': form['email'],
'pancard': form['pancard'],
'working': form['working'],
'active': form['active'],
};
final res = await _repo.updateAccountApi(accId, body);
if (res is Map<String, dynamic>) {
_sysAccount = res;
await UserManager().setAccount(res);
}
ToastMessageUtil.showToast(
message: 'Account Updated', toastType: ToastType.success);
Navigator.pop(context);
} catch (e) {
ToastMessageUtil.showToast(
message: 'Failed to update account', toastType: ToastType.error);
} finally {
setUpdating(false);
}
}
// Fetching the profile image
Future<void> getProfileImg() async {
_repo.getProfileImgApi().then((value) {
String base64Image = value['image'];
// Remove data URL prefix and decode base64
Uint8List imageBytes = base64Decode(base64Image.split(',').last);
setProfileImageBytes(imageBytes); // Store image bytes
}).onError((error, stackTrace) {
log("Error Fetching Image--$error");
ToastMessageUtil.showToast(
message: "Error fetching profile image", toastType: ToastType.error);
});
}
// Updating the profile details
Future<void> updateProfile(BuildContext context, dynamic data) async {
setUpdating(true);
final uId = UserManager().userId;
data['userId'] = uId.toString();
print("Data--$data");
_repo.updateProfileApi(data, uId).then((value) {
log("Profile Update--$value");
setUpdating(false);
getProfile();
ToastMessageUtil.showToast(
message: "Profile Updated", toastType: ToastType.success);
Navigator.pop(context); // Close dialog/screen after update
}).onError((error, stackTrace) {
log("Error Updating Profile--$error");
setUpdating(false);
ToastMessageUtil.showToast(
message: "Error updating profile!!", toastType: ToastType.error);
});
}
// Updating the profile image
Future<void> updateImage(BuildContext context) async {
if (_profileImage == null) return; // Ensure an image is selected
setUpdating(true);
Uint8List fileBytes = await _profileImage!.readAsBytes();
FormData formData = FormData.fromMap({
'imageFile': MultipartFile.fromBytes(
fileBytes,
filename: _profileImage!.name,
),
});
_repo.updateProfileImg(formData).then((value) {
log("Image Update--$value");
setUpdating(false);
getProfileImg(); // Refresh the displayed image
ToastMessageUtil.showToast(
message: "Profile Image Updated", toastType: ToastType.success);
}).onError((error, stackTrace) {
log("Error Updating Image--$error");
setUpdating(false);
ToastMessageUtil.showToast(
message: "Error updating image!!", toastType: ToastType.error);
});
}
// Changing the password
Future<void> changePassword(dynamic data) async {
setUpdating(true);
print("BODY--$data");
_repo.changPassApi(data).then((value) {
print("Password Change--$value");
setUpdating(false);
ToastMessageUtil.showToast(
message: "Password updated", toastType: ToastType.success);
}).onError((error, stackTrace) {
print("Error Changing Password--$error");
setUpdating(false);
ToastMessageUtil.showToast(
message: "Error updating password!!", toastType: ToastType.error);
});
}
// Helper method to generate headers
// Network headers are now centralized in NetworkApiService via UserManager
// Method to return image bytes (can be used in the UI to display)
Uint8List? get profileImageBytes => _profileImageBytes;
void reset() {
_profileImage = null;
_profileImageBytes = null;
userProfile = ApiResponse.loading(); // Or any default state
notifyListeners();
}
}

View File

@@ -0,0 +1,44 @@
import 'package:base_project/routes/route_names.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class SplashViewModel {
Future<void> checkNavigation(BuildContext context) async {
print("Checking navigation...");
try {
final prefs = await SharedPreferences.getInstance();
final isLoggedIn = prefs.getBool('isLoggedIn') ?? false;
// Wait for splash animations to complete (3 seconds total)
await Future.delayed(const Duration(seconds: 3));
if (!context.mounted) return; // Check if widget is still mounted
if (!isLoggedIn) {
print("Navigating to login...");
// Navigate to login with fade transition
Navigator.pushReplacementNamed(
context,
RouteNames.loginView,
);
} else {
print("Navigating to home...");
// Navigate to home with fade transition
Navigator.pushReplacementNamed(
context,
RouteNames.homeView,
);
}
} catch (e) {
print("Error in splash navigation: $e");
// Fallback to login on error
if (context.mounted) {
Navigator.pushReplacementNamed(
context,
RouteNames.loginView,
);
}
}
}
}

View File

@@ -0,0 +1,497 @@
import 'dart:developer';
import 'dart:typed_data';
import 'dart:convert';
import 'package:base_project/model/system_params_model.dart';
import 'package:base_project/repository/system_params_repo.dart';
import 'package:base_project/utils/managers/user_manager.dart';
import 'package:base_project/utils/toast_messages/toast_message_util.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../core/providers/dynamic_theme_provider.dart';
class SystemParamsViewModel extends ChangeNotifier {
final _repo = SystemParamsRepo();
Uint8List? _profileImageBytes;
XFile? _profileImage;
bool _loading = false;
// Store system parameters as model
SystemParamsModel? _systemParams;
Map<String, dynamic> _systemParamsList = {};
// Getter for system parameters model
SystemParamsModel? get systemParams => _systemParams;
// Getter for system parameters list (for backward compatibility)
Map<String, dynamic> get systemParamsList => _systemParamsList;
// Getter for profile image bytes
Uint8List? get profileImageBytes => _profileImageBytes;
// Getter for profile image
XFile? get profileImg => _profileImage;
// Getter for loading status
bool get loading => _loading;
// Setter for loading status with notification to listeners
set loading(bool value) {
_loading = value;
notifyListeners();
}
// Setter for profile image
setImage(XFile? val) {
_profileImage = val;
// Avoid notify during build; schedule for next frame
WidgetsBinding.instance.addPostFrameCallback((_) {
notifyListeners();
});
}
// Setter for profile image bytes
setProfileImageBytes(Uint8List? val) {
_profileImageBytes = val;
// Avoid notify during build; schedule for next frame
WidgetsBinding.instance.addPostFrameCallback((_) {
notifyListeners();
});
// Persist to SharedPreferences for future sessions
_persistLogoToLocal(val);
}
// Method to pick an image from the gallery
void pickImg(BuildContext context) async {
final ImagePicker picker = ImagePicker();
final XFile? image = await picker.pickImage(source: ImageSource.gallery);
if (image != null) {
setImage(image);
// Convert image to bytes and store
final bytes = await image.readAsBytes();
setProfileImageBytes(bytes);
// Generate dynamic theme IMMEDIATELY when image is selected
print("Image selected - generating dynamic theme immediately...");
await _generateDynamicThemeFromLogo(context);
print("Dynamic theme generated immediately after image selection!");
// Show success message
ToastMessageUtil.showToast(
message:
"Logo selected! Dynamic theme applied. Click 'Upload Logo' to save permanently.",
toastType: ToastType.success);
} else {
ToastMessageUtil.showToast(
message: "No image picked !!", toastType: ToastType.warning);
}
}
// Method to upload logo image separately (like edit profile)
Future<void> uploadLogo(BuildContext context) async {
if (_profileImage == null) {
ToastMessageUtil.showToast(
message: "Please pick an image first!", toastType: ToastType.warning);
return;
}
loading = true;
try {
Uint8List fileBytes = await _profileImage!.readAsBytes();
print("Logo file size: ${fileBytes.length} bytes");
// Generate dynamic theme IMMEDIATELY after image selection (before API call)
print("Generating dynamic theme immediately...");
await _generateDynamicThemeFromLogo(context);
print(
"Dynamic theme generated immediately - user sees instant feedback!");
// Now proceed with API upload
FormData formData = FormData.fromMap({
'imageFile': MultipartFile.fromBytes(
fileBytes,
filename: _profileImage!.name,
),
});
dynamic uploadResult;
try {
uploadResult = await _repo.uploadSystemParamLogo(formData);
print("----LOGO-UPLOADED----");
print(uploadResult);
} catch (e) {
// If backend returns intermittent error but file actually uploaded,
// avoid showing a scary error immediately. Log and proceed to metadata update.
print("Upload API error (may be intermittent): $e");
}
// Try to update system parameters regardless (backend may have the file already)
bool metaUpdated = false;
try {
await _updateLogoInSystemParams();
metaUpdated = true;
} catch (e) {
print("Logo metadata update failed: $e");
}
loading = false;
if (metaUpdated || uploadResult != null) {
ToastMessageUtil.showToast(
message: "Company logo uploaded! Dynamic theme applied.",
toastType: ToastType.success);
} else {
ToastMessageUtil.showToast(
message: "Logo upload may have failed. Please retry.",
toastType: ToastType.warning);
}
} catch (error) {
loading = false;
print("Error uploading logo: $error");
ToastMessageUtil.showToast(
message: "Error uploading logo: ${error.toString()}",
toastType: ToastType.error);
// Even if API fails, theme is already applied - user keeps the visual benefit!
print(
"API failed but dynamic theme is already applied - user keeps the visual benefit!");
}
}
// Generate dynamic theme from logo
Future<void> _generateDynamicThemeFromLogo(BuildContext context) async {
try {
print("_generateDynamicThemeFromLogo called");
print("_profileImageBytes is null: ${_profileImageBytes == null}");
print("_profileImageBytes length: ${_profileImageBytes?.length ?? 0}");
if (_profileImageBytes != null) {
final dynamicThemeProvider =
Provider.of<DynamicThemeProvider>(context, listen: false);
print("DynamicThemeProvider found: ${dynamicThemeProvider != null}");
print(
"Before theme generation - isUsingDynamicTheme: ${dynamicThemeProvider.isUsingDynamicTheme}");
await dynamicThemeProvider.generateThemeFromLogo(_profileImageBytes!);
print(
"After theme generation - isUsingDynamicTheme: ${dynamicThemeProvider.isUsingDynamicTheme}");
print("Dynamic theme generated successfully from logo!");
} else {
print("_profileImageBytes is null, cannot generate theme");
}
} catch (error) {
print("Error generating dynamic theme: $error");
}
}
// Update system parameters to include logo information
Future<void> _updateLogoInSystemParams() async {
try {
final logoData = {
'upload_Logo': _profileImageBytes,
'upload_Logo_name': _profileImage?.name ?? 'logo.png',
'upload_Logo_path': _profileImage?.path ?? '',
};
await _repo.updateSystemParameters(logoData);
print("Logo info updated in system parameters");
} catch (error) {
print("Error updating logo info in system params: $error");
}
}
// Method to fetch system parameters and store them in the model
Future<void> getSystemParams() async {
loading = true;
try {
final value = await _repo.getSystemParameters();
print("----SYSTEM-PARAMS----");
// Normalize response to Map<String, dynamic>
Map<String, dynamic> normalized = {};
if (value is Map<String, dynamic>) {
normalized = value;
} else if (value != null) {
try {
normalized = Map<String, dynamic>.from(value as Map);
} catch (_) {
normalized = {};
}
}
// Store in both formats for backward compatibility
_systemParamsList = normalized;
_systemParams = SystemParamsModel.fromJson(normalized);
// Load existing logo if available
_loadExistingLogo(value);
loading = false;
print(normalized);
} catch (error) {
loading = false;
print("Error-$error");
}
}
// Load existing logo from system parameters
void _loadExistingLogo(Map<String, dynamic> systemParams) {
try {
// Check if logo data exists in system parameters
if (systemParams['upload_Logo'] != null) {
// If logo is stored as base64 string
if (systemParams['upload_Logo'] is String) {
final String base64Image = systemParams['upload_Logo'];
if (base64Image.isNotEmpty) {
// Remove data URL prefix if present and decode base64
String cleanBase64 = base64Image;
if (base64Image.contains(',')) {
cleanBase64 = base64Image.split(',').last;
}
try {
final Uint8List imageBytes = base64Decode(cleanBase64);
setProfileImageBytes(imageBytes);
print("Existing logo loaded from system parameters");
} catch (e) {
print("Error decoding logo: $e");
}
}
}
// If logo is stored as bytes
else if (systemParams['upload_Logo'] is List) {
final List<int> logoBytes =
List<int>.from(systemParams['upload_Logo']);
setProfileImageBytes(Uint8List.fromList(logoBytes));
print("Existing logo loaded from system parameters");
}
}
} catch (error) {
print("Error loading existing logo: $error");
}
// Also attempt to load from local storage if not present in systemParams
if (_profileImageBytes == null) {
_loadLogoFromLocal();
}
}
// Method to update system parameters
Future<void> updateSystemParams(Map<String, dynamic> body) async {
loading = true;
try {
// Convert the form data to proper backend format
final backendData = _convertFormDataToBackendFormat(body);
final value = await _repo.updateSystemParameters(backendData);
print("----SYSTEM-PARAMS-UPDATED----");
loading = false;
print(value);
ToastMessageUtil.showToast(
message: "System parameters updated!!", toastType: ToastType.success);
} catch (error) {
loading = false;
print("Error-$error");
ToastMessageUtil.showToast(
message: error.toString(), toastType: ToastType.error);
}
}
// Convert form data to backend format
Map<String, dynamic> _convertFormDataToBackendFormat(
Map<String, dynamic> formData) {
final Map<String, dynamic> backendData = {};
// Map form field names to backend field names
for (String key in formData.keys) {
switch (key) {
case 'Scheduler Timer':
backendData['schedulerTime'] = int.tryParse(formData[key]) ?? 0;
break;
case 'Lease Tax Code':
backendData['leaseTaxCode'] = formData[key];
break;
case 'Vessel Confirmation':
backendData['vesselConfProcessLimit'] =
int.tryParse(formData[key]) ?? 0;
break;
case 'Row to Display':
backendData['rowToDisplay'] = int.tryParse(formData[key]) ?? 0;
break;
case 'Link to Display':
backendData['linkToDisplay'] = int.tryParse(formData[key]) ?? 0;
break;
case 'Row to Add':
backendData['rowToAdd'] = int.tryParse(formData[key]) ?? 0;
break;
case 'LOV Row to Display':
backendData['lovRowToDisplay'] = int.tryParse(formData[key]) ?? 0;
break;
case 'LOV Link to Display':
backendData['lovLinkToDisplay'] = int.tryParse(formData[key]) ?? 0;
break;
case 'OID Server Name':
backendData['oidserverName'] = formData[key];
break;
case 'OID Base':
backendData['oidBase'] = formData[key];
break;
case 'OID Admin User':
backendData['oidAdminUser'] = formData[key];
break;
case 'OID Server Port':
backendData['oidServerPort'] = int.tryParse(formData[key]) ?? 0;
break;
case 'User Default Group':
backendData['userDefaultGroup'] = int.tryParse(formData[key]) ?? 0;
break;
case 'Default Department':
backendData['defaultDepartment'] = formData[key];
break;
case 'Default Position':
backendData['defaultPosition'] = formData[key];
break;
case 'Single Charge':
backendData['singleCharge'] = formData[key];
break;
case 'First Day of the Week':
backendData['firstDayOftheWeek'] = formData[key];
break;
case 'Hours Per Shift':
backendData['hourPerShift'] = int.tryParse(formData[key]) ?? 0;
break;
case 'CN Billing Frequency':
backendData['cnBillingFrequency'] = int.tryParse(formData[key]) ?? 0;
break;
case 'Billing Department Code':
backendData['billingDepartmentCode'] = formData[key];
break;
case 'Base Price List':
backendData['basePriceList'] = formData[key];
break;
case 'Non-Container Service Order':
backendData['nonContainerServiceOrder'] = formData[key];
break;
case 'EDI MAE Scheduler':
backendData['ediMaeSchedulerONOFF'] =
int.tryParse(formData[key]) ?? 0;
break;
case 'EDI Scheduler':
backendData['ediSchedulerONOFF'] = formData[key];
break;
case 'Company Name':
backendData['Company_Display_Name'] = formData[key];
break;
case 'Registration Allowed':
backendData['isRegitrationAllowed'] = formData[key] == 'true';
break;
default:
// For any unmapped fields, pass through as is
backendData[key] = formData[key];
break;
}
}
// Add logo upload fields if available
if (_profileImageBytes != null) {
backendData['upload_Logo'] = _profileImageBytes;
backendData['upload_Logo_name'] = _profileImage?.name ?? 'logo.png';
backendData['upload_Logo_path'] = _profileImage?.path ?? '';
}
return backendData;
}
// Updating the profile image
Future<void> updateImage(BuildContext context) async {
if (_profileImage == null) return; // Ensure an image is selected
loading = true;
Uint8List fileBytes = await _profileImage!.readAsBytes();
FormData formData = FormData.fromMap({
'imageFile': MultipartFile.fromBytes(
fileBytes,
filename: _profileImage!.name,
),
});
_repo.updateProfileImg(formData).then((value) {
log("Image Update--$value");
loading = false;
ToastMessageUtil.showToast(
message: "Profile Image Updated", toastType: ToastType.success);
}).onError((error, stackTrace) {
log("Error Updating Image--$error");
loading = false;
// setUpdating(false);
ToastMessageUtil.showToast(
message: "Error updating image!!", toastType: ToastType.error);
});
}
// Method to clear logo and reset theme
Future<void> clearLogo(BuildContext context) async {
try {
// Clear the image data
setImage(null);
setProfileImageBytes(null);
// Reset to default theme
final dynamicThemeProvider =
Provider.of<DynamicThemeProvider>(context, listen: false);
dynamicThemeProvider.resetToDefaultTheme();
print("Logo cleared and theme reset to default");
// Update system parameters to remove logo info
await _updateLogoInSystemParams();
// Remove locally persisted logo
final prefs = await SharedPreferences.getInstance();
await prefs.remove('cached_logo_bytes');
ToastMessageUtil.showToast(
message: "Logo cleared! Theme reset to default.",
toastType: ToastType.info);
} catch (error) {
print("Error clearing logo: $error");
ToastMessageUtil.showToast(
message: "Error clearing logo: ${error.toString()}",
toastType: ToastType.error);
}
}
// Persist logo bytes locally
Future<void> _persistLogoToLocal(Uint8List? bytes) async {
try {
final prefs = await SharedPreferences.getInstance();
if (bytes == null || bytes.isEmpty) {
await prefs.remove('cached_logo_bytes');
return;
}
final base64Str = base64Encode(bytes);
await prefs.setString('cached_logo_bytes', base64Str);
} catch (e) {
print('Error persisting logo locally: $e');
}
}
// Load logo bytes from local storage
Future<void> _loadLogoFromLocal() async {
try {
final prefs = await SharedPreferences.getInstance();
final base64Str = prefs.getString('cached_logo_bytes');
if (base64Str != null && base64Str.isNotEmpty) {
final bytes = base64Decode(base64Str);
setProfileImageBytes(bytes);
print('Loaded logo from local storage');
}
} catch (e) {
print('Error loading logo from local storage: $e');
}
}
}