refactor(logging): remove sensitive flag from logSafe calls across multiple controllers and services
This commit is contained in:
parent
e059ee71f3
commit
aac65104ab
@ -32,7 +32,7 @@ class ForgotPasswordController extends MyController {
|
|||||||
final email = data['email']?.toString() ?? '';
|
final email = data['email']?.toString() ?? '';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logSafe("Forgot password requested for: $email", sensitive: true);
|
logSafe("Forgot password requested for: $email", );
|
||||||
|
|
||||||
final result = await AuthService.forgotPassword(email);
|
final result = await AuthService.forgotPassword(email);
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ class ForgotPasswordController extends MyController {
|
|||||||
message: errorMessage,
|
message: errorMessage,
|
||||||
type: SnackbarType.error,
|
type: SnackbarType.error,
|
||||||
);
|
);
|
||||||
logSafe("Failed to send reset password email for $email: $errorMessage", level: LogLevel.warning, sensitive: true);
|
logSafe("Failed to send reset password email for $email: $errorMessage", level: LogLevel.warning, );
|
||||||
}
|
}
|
||||||
} catch (e, stacktrace) {
|
} catch (e, stacktrace) {
|
||||||
logSafe("Error during forgot password", level: LogLevel.error, error: e, stackTrace: stacktrace);
|
logSafe("Error during forgot password", level: LogLevel.error, error: e, stackTrace: stacktrace);
|
||||||
|
@ -55,12 +55,12 @@ class LoginController extends MyController {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
final loginData = basicValidator.getData();
|
final loginData = basicValidator.getData();
|
||||||
logSafe("Attempting login for user: ${loginData['username']}", sensitive: true);
|
logSafe("Attempting login for user: ${loginData['username']}", );
|
||||||
|
|
||||||
final errors = await AuthService.loginUser(loginData);
|
final errors = await AuthService.loginUser(loginData);
|
||||||
|
|
||||||
if (errors != null) {
|
if (errors != null) {
|
||||||
logSafe("Login failed for user: ${loginData['username']} with errors: $errors", level: LogLevel.warning, sensitive: true);
|
logSafe("Login failed for user: ${loginData['username']} with errors: $errors", level: LogLevel.warning, );
|
||||||
|
|
||||||
showAppSnackbar(
|
showAppSnackbar(
|
||||||
title: "Login Failed",
|
title: "Login Failed",
|
||||||
@ -73,7 +73,7 @@ class LoginController extends MyController {
|
|||||||
basicValidator.clearErrors();
|
basicValidator.clearErrors();
|
||||||
} else {
|
} else {
|
||||||
await _handleRememberMe();
|
await _handleRememberMe();
|
||||||
logSafe("Login successful for user: ${loginData['username']}", sensitive: true);
|
logSafe("Login successful for user: ${loginData['username']}", );
|
||||||
Get.toNamed('/home');
|
Get.toNamed('/home');
|
||||||
}
|
}
|
||||||
} catch (e, stacktrace) {
|
} catch (e, stacktrace) {
|
||||||
|
@ -29,7 +29,7 @@ class MPINController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onDigitChanged(String value, int index, {bool isRetype = false}) {
|
void onDigitChanged(String value, int index, {bool isRetype = false}) {
|
||||||
logSafe("onDigitChanged -> index: $index, value: $value, isRetype: $isRetype", sensitive: true);
|
logSafe("onDigitChanged -> index: $index, value: $value, isRetype: $isRetype", );
|
||||||
final nodes = isRetype ? retypeFocusNodes : focusNodes;
|
final nodes = isRetype ? retypeFocusNodes : focusNodes;
|
||||||
if (value.isNotEmpty && index < 5) {
|
if (value.isNotEmpty && index < 5) {
|
||||||
nodes[index + 1].requestFocus();
|
nodes[index + 1].requestFocus();
|
||||||
@ -47,7 +47,7 @@ class MPINController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final enteredMPIN = digitControllers.map((c) => c.text).join();
|
final enteredMPIN = digitControllers.map((c) => c.text).join();
|
||||||
logSafe("Entered MPIN: $enteredMPIN", sensitive: true);
|
logSafe("Entered MPIN: $enteredMPIN", );
|
||||||
|
|
||||||
if (enteredMPIN.length < 6) {
|
if (enteredMPIN.length < 6) {
|
||||||
_showError("Please enter all 6 digits.");
|
_showError("Please enter all 6 digits.");
|
||||||
@ -56,7 +56,7 @@ class MPINController extends GetxController {
|
|||||||
|
|
||||||
if (isNewUser.value) {
|
if (isNewUser.value) {
|
||||||
final retypeMPIN = retypeControllers.map((c) => c.text).join();
|
final retypeMPIN = retypeControllers.map((c) => c.text).join();
|
||||||
logSafe("Retyped MPIN: $retypeMPIN", sensitive: true);
|
logSafe("Retyped MPIN: $retypeMPIN", );
|
||||||
|
|
||||||
if (retypeMPIN.length < 6) {
|
if (retypeMPIN.length < 6) {
|
||||||
_showError("Please enter all 6 digits in Retype MPIN.");
|
_showError("Please enter all 6 digits in Retype MPIN.");
|
||||||
@ -177,7 +177,7 @@ class MPINController extends GetxController {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
logSafe("Calling AuthService.generateMpin for employeeId: $employeeId", sensitive: true);
|
logSafe("Calling AuthService.generateMpin for employeeId: $employeeId", );
|
||||||
|
|
||||||
final response = await AuthService.generateMpin(
|
final response = await AuthService.generateMpin(
|
||||||
employeeId: employeeId,
|
employeeId: employeeId,
|
||||||
@ -222,7 +222,7 @@ class MPINController extends GetxController {
|
|||||||
logSafe("verifyMPIN triggered");
|
logSafe("verifyMPIN triggered");
|
||||||
|
|
||||||
final enteredMPIN = digitControllers.map((c) => c.text).join();
|
final enteredMPIN = digitControllers.map((c) => c.text).join();
|
||||||
logSafe("Entered MPIN: $enteredMPIN", sensitive: true);
|
logSafe("Entered MPIN: $enteredMPIN", );
|
||||||
|
|
||||||
if (enteredMPIN.length < 6) {
|
if (enteredMPIN.length < 6) {
|
||||||
_showError("Please enter all 6 digits.");
|
_showError("Please enter all 6 digits.");
|
||||||
|
@ -144,7 +144,7 @@ class OTPController extends GetxController {
|
|||||||
Get.offAllNamed('/home');
|
Get.offAllNamed('/home');
|
||||||
} else {
|
} else {
|
||||||
final error = result['error'] ?? "Failed to verify OTP";
|
final error = result['error'] ?? "Failed to verify OTP";
|
||||||
logSafe("[OTPController] OTP verification failed", level: LogLevel.warning, error: error, sensitive: true);
|
logSafe("[OTPController] OTP verification failed", level: LogLevel.warning, error: error, );
|
||||||
showAppSnackbar(
|
showAppSnackbar(
|
||||||
title: "Error",
|
title: "Error",
|
||||||
message: error,
|
message: error,
|
||||||
|
@ -20,7 +20,7 @@ class DashboardController extends GetxController {
|
|||||||
logSafe(
|
logSafe(
|
||||||
'DashboardController initialized with project ID: ${projectController.selectedProjectId.value}',
|
'DashboardController initialized with project ID: ${projectController.selectedProjectId.value}',
|
||||||
level: LogLevel.info,
|
level: LogLevel.info,
|
||||||
sensitive: true,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (projectController.selectedProjectId.value.isNotEmpty) {
|
if (projectController.selectedProjectId.value.isNotEmpty) {
|
||||||
@ -30,7 +30,7 @@ class DashboardController extends GetxController {
|
|||||||
// React to project change
|
// React to project change
|
||||||
ever<String>(projectController.selectedProjectId, (id) {
|
ever<String>(projectController.selectedProjectId, (id) {
|
||||||
if (id.isNotEmpty) {
|
if (id.isNotEmpty) {
|
||||||
logSafe('Project changed to $id, fetching attendance', level: LogLevel.info, sensitive: true);
|
logSafe('Project changed to $id, fetching attendance', level: LogLevel.info, );
|
||||||
fetchRoleWiseAttendance();
|
fetchRoleWiseAttendance();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -106,15 +106,15 @@ class EmployeesScreenController extends GetxController {
|
|||||||
logSafe(
|
logSafe(
|
||||||
"Employees fetched: ${employees.length} for project $projectId",
|
"Employees fetched: ${employees.length} for project $projectId",
|
||||||
level: LogLevel.info,
|
level: LogLevel.info,
|
||||||
sensitive: true,
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onEmpty: () {
|
onEmpty: () {
|
||||||
employees.clear();
|
employees.clear();
|
||||||
logSafe("No employees found for project $projectId.", level: LogLevel.warning, sensitive: true);
|
logSafe("No employees found for project $projectId.", level: LogLevel.warning, );
|
||||||
},
|
},
|
||||||
onError: (e) {
|
onError: (e) {
|
||||||
logSafe("Error fetching employees for project $projectId", level: LogLevel.error, error: e, sensitive: true);
|
logSafe("Error fetching employees for project $projectId", level: LogLevel.error, error: e, );
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -131,15 +131,15 @@ class EmployeesScreenController extends GetxController {
|
|||||||
() => ApiService.getEmployeeDetails(employeeId),
|
() => ApiService.getEmployeeDetails(employeeId),
|
||||||
onSuccess: (data) {
|
onSuccess: (data) {
|
||||||
selectedEmployeeDetails.value = EmployeeDetailsModel.fromJson(data);
|
selectedEmployeeDetails.value = EmployeeDetailsModel.fromJson(data);
|
||||||
logSafe("Employee details loaded for $employeeId", level: LogLevel.info, sensitive: true);
|
logSafe("Employee details loaded for $employeeId", level: LogLevel.info, );
|
||||||
},
|
},
|
||||||
onEmpty: () {
|
onEmpty: () {
|
||||||
selectedEmployeeDetails.value = null;
|
selectedEmployeeDetails.value = null;
|
||||||
logSafe("No employee details found for $employeeId", level: LogLevel.warning, sensitive: true);
|
logSafe("No employee details found for $employeeId", level: LogLevel.warning, );
|
||||||
},
|
},
|
||||||
onError: (e) {
|
onError: (e) {
|
||||||
selectedEmployeeDetails.value = null;
|
selectedEmployeeDetails.value = null;
|
||||||
logSafe("Error fetching employee details for $employeeId", level: LogLevel.error, error: e, sensitive: true);
|
logSafe("Error fetching employee details for $employeeId", level: LogLevel.error, error: e, );
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ class PermissionController extends GetxController {
|
|||||||
employeeInfo.value = userData['employeeInfo'];
|
employeeInfo.value = userData['employeeInfo'];
|
||||||
projectsInfo.assignAll(userData['projects']);
|
projectsInfo.assignAll(userData['projects']);
|
||||||
|
|
||||||
logSafe("State updated with new user data.", sensitive: true);
|
logSafe("State updated with new user data.", );
|
||||||
} catch (e, stacktrace) {
|
} catch (e, stacktrace) {
|
||||||
logSafe("Error updating state", level: LogLevel.error, error: e, stackTrace: stacktrace);
|
logSafe("Error updating state", level: LogLevel.error, error: e, stackTrace: stacktrace);
|
||||||
}
|
}
|
||||||
@ -86,7 +86,7 @@ class PermissionController extends GetxController {
|
|||||||
try {
|
try {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
final token = prefs.getString('jwt_token');
|
final token = prefs.getString('jwt_token');
|
||||||
logSafe("Auth token retrieved successfully.", sensitive: true);
|
logSafe("Auth token retrieved successfully.", );
|
||||||
return token;
|
return token;
|
||||||
} catch (e, stacktrace) {
|
} catch (e, stacktrace) {
|
||||||
logSafe("Error retrieving auth token", level: LogLevel.error, error: e, stackTrace: stacktrace);
|
logSafe("Error retrieving auth token", level: LogLevel.error, error: e, stackTrace: stacktrace);
|
||||||
|
@ -147,6 +147,6 @@ class AddTaskController extends GetxController {
|
|||||||
void selectCategory(String id) {
|
void selectCategory(String id) {
|
||||||
selectedCategoryId.value = id;
|
selectedCategoryId.value = id;
|
||||||
selectedCategoryName.value = categoryIdNameMap[id];
|
selectedCategoryName.value = categoryIdNameMap[id];
|
||||||
logSafe("Category selected", level: LogLevel.debug, sensitive: true);
|
logSafe("Category selected", level: LogLevel.debug, );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,12 +50,12 @@ class DailyTaskPlaningController extends GetxController {
|
|||||||
.where((e) => uploadingStates[e.id]?.value == true)
|
.where((e) => uploadingStates[e.id]?.value == true)
|
||||||
.toList();
|
.toList();
|
||||||
selectedEmployees.value = selected;
|
selectedEmployees.value = selected;
|
||||||
logSafe("Updated selected employees", level: LogLevel.debug, sensitive: true);
|
logSafe("Updated selected employees", level: LogLevel.debug, );
|
||||||
}
|
}
|
||||||
|
|
||||||
void onRoleSelected(String? roleId) {
|
void onRoleSelected(String? roleId) {
|
||||||
selectedRoleId.value = roleId;
|
selectedRoleId.value = roleId;
|
||||||
logSafe("Role selected", level: LogLevel.info, sensitive: true);
|
logSafe("Role selected", level: LogLevel.info, );
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> fetchRoles() async {
|
Future<void> fetchRoles() async {
|
||||||
@ -137,7 +137,7 @@ class DailyTaskPlaningController extends GetxController {
|
|||||||
final data = response?['data'];
|
final data = response?['data'];
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
dailyTasks = [TaskPlanningDetailsModel.fromJson(data)];
|
dailyTasks = [TaskPlanningDetailsModel.fromJson(data)];
|
||||||
logSafe("Daily task Planning Details fetched", level: LogLevel.info, sensitive: true);
|
logSafe("Daily task Planning Details fetched", level: LogLevel.info, );
|
||||||
} else {
|
} else {
|
||||||
logSafe("Data field is null", level: LogLevel.warning);
|
logSafe("Data field is null", level: LogLevel.warning);
|
||||||
}
|
}
|
||||||
@ -164,14 +164,14 @@ class DailyTaskPlaningController extends GetxController {
|
|||||||
uploadingStates[emp.id] = false.obs;
|
uploadingStates[emp.id] = false.obs;
|
||||||
}
|
}
|
||||||
logSafe("Employees fetched: ${employees.length} for project $projectId",
|
logSafe("Employees fetched: ${employees.length} for project $projectId",
|
||||||
level: LogLevel.info, sensitive: true);
|
level: LogLevel.info, );
|
||||||
} else {
|
} else {
|
||||||
employees = [];
|
employees = [];
|
||||||
logSafe("No employees found for project $projectId", level: LogLevel.warning, sensitive: true);
|
logSafe("No employees found for project $projectId", level: LogLevel.warning, );
|
||||||
}
|
}
|
||||||
} catch (e, stack) {
|
} catch (e, stack) {
|
||||||
logSafe("Error fetching employees for project $projectId",
|
logSafe("Error fetching employees for project $projectId",
|
||||||
level: LogLevel.error, error: e, stackTrace: stack, sensitive: true);
|
level: LogLevel.error, error: e, stackTrace: stack, );
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
update();
|
update();
|
||||||
|
@ -272,18 +272,18 @@ class ReportTaskActionController extends MyController {
|
|||||||
final pickedFile = await _picker.pickImage(source: ImageSource.camera, imageQuality: 75);
|
final pickedFile = await _picker.pickImage(source: ImageSource.camera, imageQuality: 75);
|
||||||
if (pickedFile != null) {
|
if (pickedFile != null) {
|
||||||
selectedImages.add(File(pickedFile.path));
|
selectedImages.add(File(pickedFile.path));
|
||||||
logSafe("Image added from camera: ${pickedFile.path}", sensitive: true);
|
logSafe("Image added from camera: ${pickedFile.path}", );
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
final pickedFiles = await _picker.pickMultiImage(imageQuality: 75);
|
final pickedFiles = await _picker.pickMultiImage(imageQuality: 75);
|
||||||
selectedImages.addAll(pickedFiles.map((xfile) => File(xfile.path)));
|
selectedImages.addAll(pickedFiles.map((xfile) => File(xfile.path)));
|
||||||
logSafe("${pickedFiles.length} images added from gallery.", sensitive: true);
|
logSafe("${pickedFiles.length} images added from gallery.", );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void removeImageAt(int index) {
|
void removeImageAt(int index) {
|
||||||
if (index >= 0 && index < selectedImages.length) {
|
if (index >= 0 && index < selectedImages.length) {
|
||||||
logSafe("Removing image at index $index", sensitive: true);
|
logSafe("Removing image at index $index", );
|
||||||
selectedImages.removeAt(index);
|
selectedImages.removeAt(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ class ReportTaskController extends MyController {
|
|||||||
required DateTime reportedDate,
|
required DateTime reportedDate,
|
||||||
List<File>? images,
|
List<File>? images,
|
||||||
}) async {
|
}) async {
|
||||||
logSafe("Reporting task for projectId", sensitive: true);
|
logSafe("Reporting task for projectId", );
|
||||||
final completedWork = completedWorkController.text.trim();
|
final completedWork = completedWorkController.text.trim();
|
||||||
if (completedWork.isEmpty || int.tryParse(completedWork) == null || int.parse(completedWork) < 0) {
|
if (completedWork.isEmpty || int.tryParse(completedWork) == null || int.parse(completedWork) < 0) {
|
||||||
_showError("Completed work must be a positive number.");
|
_showError("Completed work must be a positive number.");
|
||||||
@ -138,7 +138,7 @@ class ReportTaskController extends MyController {
|
|||||||
required String comment,
|
required String comment,
|
||||||
List<File>? images,
|
List<File>? images,
|
||||||
}) async {
|
}) async {
|
||||||
logSafe("Submitting comment for project", sensitive: true);
|
logSafe("Submitting comment for project", );
|
||||||
|
|
||||||
final commentField = commentController.text.trim();
|
final commentField = commentController.text.trim();
|
||||||
if (commentField.isEmpty) {
|
if (commentField.isEmpty) {
|
||||||
@ -221,7 +221,7 @@ class ReportTaskController extends MyController {
|
|||||||
final pickedFiles = await _picker.pickMultiImage(imageQuality: 75);
|
final pickedFiles = await _picker.pickMultiImage(imageQuality: 75);
|
||||||
selectedImages.addAll(pickedFiles.map((xfile) => File(xfile.path)));
|
selectedImages.addAll(pickedFiles.map((xfile) => File(xfile.path)));
|
||||||
}
|
}
|
||||||
logSafe("Images picked: ${selectedImages.length}", sensitive: true);
|
logSafe("Images picked: ${selectedImages.length}", );
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logSafe("Error picking images", level: LogLevel.warning, error: e);
|
logSafe("Error picking images", level: LogLevel.warning, error: e);
|
||||||
}
|
}
|
||||||
|
@ -163,7 +163,7 @@ class ApiService {
|
|||||||
|
|
||||||
final uri = Uri.parse("${ApiEndpoints.baseUrl}$endpoint");
|
final uri = Uri.parse("${ApiEndpoints.baseUrl}$endpoint");
|
||||||
logSafe("POST $uri\nHeaders: ${_headers(token)}\nBody: $body",
|
logSafe("POST $uri\nHeaders: ${_headers(token)}\nBody: $body",
|
||||||
sensitive: true);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await http
|
final response = await http
|
||||||
@ -203,7 +203,7 @@ class ApiService {
|
|||||||
if (additionalHeaders != null) ...additionalHeaders,
|
if (additionalHeaders != null) ...additionalHeaders,
|
||||||
};
|
};
|
||||||
|
|
||||||
logSafe("PUT $uri\nHeaders: $headers\nBody: $body", sensitive: true);
|
logSafe("PUT $uri\nHeaders: $headers\nBody: $body", );
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await http
|
final response = await http
|
||||||
|
@ -19,10 +19,10 @@ class PermissionService {
|
|||||||
String token, {
|
String token, {
|
||||||
bool hasRetried = false,
|
bool hasRetried = false,
|
||||||
}) async {
|
}) async {
|
||||||
logSafe("Fetching user data...", sensitive: true);
|
logSafe("Fetching user data...", );
|
||||||
|
|
||||||
if (_userDataCache.containsKey(token)) {
|
if (_userDataCache.containsKey(token)) {
|
||||||
logSafe("User data cache hit.", sensitive: true);
|
logSafe("User data cache hit.", );
|
||||||
return _userDataCache[token]!;
|
return _userDataCache[token]!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import 'package:marco/helpers/services/app_logger.dart';
|
|||||||
class LauncherUtils {
|
class LauncherUtils {
|
||||||
/// Launches the phone dialer with the provided phone number
|
/// Launches the phone dialer with the provided phone number
|
||||||
static Future<void> launchPhone(String phoneNumber) async {
|
static Future<void> launchPhone(String phoneNumber) async {
|
||||||
logSafe('Attempting to launch phone: $phoneNumber', sensitive: true);
|
logSafe('Attempting to launch phone: $phoneNumber', );
|
||||||
|
|
||||||
final Uri url = Uri(scheme: 'tel', path: phoneNumber);
|
final Uri url = Uri(scheme: 'tel', path: phoneNumber);
|
||||||
await _tryLaunch(url, 'Could not launch phone');
|
await _tryLaunch(url, 'Could not launch phone');
|
||||||
@ -14,7 +14,7 @@ class LauncherUtils {
|
|||||||
|
|
||||||
/// Launches the email app with the provided email address
|
/// Launches the email app with the provided email address
|
||||||
static Future<void> launchEmail(String email) async {
|
static Future<void> launchEmail(String email) async {
|
||||||
logSafe('Attempting to launch email: $email', sensitive: true);
|
logSafe('Attempting to launch email: $email', );
|
||||||
|
|
||||||
final Uri url = Uri(scheme: 'mailto', path: email);
|
final Uri url = Uri(scheme: 'mailto', path: email);
|
||||||
await _tryLaunch(url, 'Could not launch email');
|
await _tryLaunch(url, 'Could not launch email');
|
||||||
@ -22,17 +22,17 @@ class LauncherUtils {
|
|||||||
|
|
||||||
/// Launches WhatsApp with the provided phone number
|
/// Launches WhatsApp with the provided phone number
|
||||||
static Future<void> launchWhatsApp(String phoneNumber) async {
|
static Future<void> launchWhatsApp(String phoneNumber) async {
|
||||||
logSafe('Attempting to launch WhatsApp with: $phoneNumber', sensitive: true);
|
logSafe('Attempting to launch WhatsApp with: $phoneNumber', );
|
||||||
|
|
||||||
String normalized = phoneNumber.replaceAll(RegExp(r'\D'), '');
|
String normalized = phoneNumber.replaceAll(RegExp(r'\D'), '');
|
||||||
if (!normalized.startsWith('91')) {
|
if (!normalized.startsWith('91')) {
|
||||||
normalized = '91$normalized';
|
normalized = '91$normalized';
|
||||||
}
|
}
|
||||||
|
|
||||||
logSafe('Normalized WhatsApp number: $normalized', sensitive: true);
|
logSafe('Normalized WhatsApp number: $normalized', );
|
||||||
|
|
||||||
if (normalized.length < 12) {
|
if (normalized.length < 12) {
|
||||||
logSafe('Invalid WhatsApp number: $normalized', sensitive: true);
|
logSafe('Invalid WhatsApp number: $normalized', );
|
||||||
showAppSnackbar(
|
showAppSnackbar(
|
||||||
title: 'Error',
|
title: 'Error',
|
||||||
message: 'Invalid phone number for WhatsApp',
|
message: 'Invalid phone number for WhatsApp',
|
||||||
@ -62,7 +62,7 @@ class LauncherUtils {
|
|||||||
'Failed to copy $typeLabel to clipboard: $e',
|
'Failed to copy $typeLabel to clipboard: $e',
|
||||||
stackTrace: st,
|
stackTrace: st,
|
||||||
level: LogLevel.error,
|
level: LogLevel.error,
|
||||||
sensitive: true,
|
|
||||||
);
|
);
|
||||||
showAppSnackbar(
|
showAppSnackbar(
|
||||||
title: 'Error',
|
title: 'Error',
|
||||||
|
@ -14,7 +14,7 @@ Future<Uint8List?> compressImageToUnder100KB(File file) async {
|
|||||||
const int maxWidth = 800;
|
const int maxWidth = 800;
|
||||||
const int maxHeight = 800;
|
const int maxHeight = 800;
|
||||||
|
|
||||||
logSafe("Starting image compression...", sensitive: true);
|
logSafe("Starting image compression...", );
|
||||||
|
|
||||||
while (quality >= 10) {
|
while (quality >= 10) {
|
||||||
try {
|
try {
|
||||||
@ -59,7 +59,7 @@ Future<File> saveCompressedImageToFile(Uint8List bytes) async {
|
|||||||
final file = File(filePath);
|
final file = File(filePath);
|
||||||
final savedFile = await file.writeAsBytes(bytes);
|
final savedFile = await file.writeAsBytes(bytes);
|
||||||
|
|
||||||
logSafe("Compressed image saved to ${savedFile.path}", sensitive: true);
|
logSafe("Compressed image saved to ${savedFile.path}", );
|
||||||
return savedFile;
|
return savedFile;
|
||||||
} catch (e, stacktrace) {
|
} catch (e, stacktrace) {
|
||||||
logSafe("Error saving compressed image", level: LogLevel.error, error: e, stackTrace: stacktrace);
|
logSafe("Error saving compressed image", level: LogLevel.error, error: e, stackTrace: stacktrace);
|
||||||
|
@ -1,225 +1,166 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_lucide/flutter_lucide.dart';
|
import 'package:flutter_lucide/flutter_lucide.dart';
|
||||||
import 'package:marco/helpers/utils/mixins/ui_mixin.dart';
|
import 'package:marco/helpers/widgets/my_text.dart';
|
||||||
import 'package:marco/images.dart';
|
import 'package:marco/images.dart';
|
||||||
import 'package:marco/view/auth/email_login_form.dart';
|
import 'package:marco/view/auth/email_login_form.dart';
|
||||||
import 'package:marco/view/auth/otp_login_form.dart';
|
import 'package:marco/view/auth/otp_login_form.dart';
|
||||||
import 'package:marco/helpers/services/api_endpoints.dart';
|
import 'package:marco/helpers/services/api_endpoints.dart';
|
||||||
import 'package:marco/helpers/widgets/my_text.dart'; // Make sure this import is added
|
|
||||||
|
|
||||||
enum LoginOption { email, otp }
|
enum LoginOption { email, otp }
|
||||||
|
|
||||||
class LoginOptionScreen extends StatefulWidget {
|
class LoginOptionScreen extends StatelessWidget {
|
||||||
const LoginOptionScreen({super.key});
|
const LoginOptionScreen({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<LoginOptionScreen> createState() => _LoginOptionScreenState();
|
Widget build(BuildContext context) => const WelcomeScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _LoginOptionScreenState extends State<LoginOptionScreen> with UIMixin {
|
class WelcomeScreen extends StatelessWidget {
|
||||||
LoginOption _selectedOption = LoginOption.email;
|
const WelcomeScreen({super.key});
|
||||||
|
|
||||||
bool get _isBetaEnvironment => ApiEndpoints.baseUrl.contains("stage");
|
bool get _isBetaEnvironment => ApiEndpoints.baseUrl.contains("stage");
|
||||||
|
|
||||||
|
void _showLoginDialog(BuildContext context, LoginOption option) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (_) => Dialog(
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||||
|
insetPadding: const EdgeInsets.all(24),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(24),
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(maxWidth: 420),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
MyText(
|
||||||
|
option == LoginOption.email ? "Login with Email" : "Login with OTP",
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: 700,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
option == LoginOption.email ? EmailLoginForm() : const OTPLoginScreen(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final screenWidth = MediaQuery.of(context).size.width;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: contentTheme.brandRed,
|
backgroundColor: const Color(0xFFB71C1C),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: LayoutBuilder(
|
child: Center(
|
||||||
builder: (context, constraints) {
|
child: SingleChildScrollView(
|
||||||
return Column(
|
padding: const EdgeInsets.all(24),
|
||||||
children: [
|
child: ConstrainedBox(
|
||||||
const SizedBox(height: 24),
|
constraints: BoxConstraints(maxWidth: screenWidth < 500 ? double.infinity : 420),
|
||||||
_buildHeader(),
|
child: Column(
|
||||||
const SizedBox(height: 16),
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
_buildWelcomeTextsAndChips(),
|
children: [
|
||||||
const SizedBox(height: 16),
|
// App Logo
|
||||||
Expanded(
|
Container(
|
||||||
child: Container(
|
padding: const EdgeInsets.all(16),
|
||||||
width: double.infinity,
|
decoration: BoxDecoration(
|
||||||
decoration: const BoxDecoration(
|
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
borderRadius:
|
borderRadius: BorderRadius.circular(20),
|
||||||
BorderRadius.vertical(top: Radius.circular(32)),
|
boxShadow: const [BoxShadow(color: Colors.black26, blurRadius: 8)],
|
||||||
),
|
),
|
||||||
child: SingleChildScrollView(
|
child: Image.asset(Images.logoDark, height: 60),
|
||||||
padding: const EdgeInsets.symmetric(
|
),
|
||||||
horizontal: 20, vertical: 24),
|
|
||||||
child: ConstrainedBox(
|
const SizedBox(height: 24),
|
||||||
constraints: BoxConstraints(
|
|
||||||
minHeight: constraints.maxHeight - 200,
|
// Welcome Text
|
||||||
),
|
MyText(
|
||||||
child: IntrinsicHeight(
|
"Welcome to Marco",
|
||||||
child: Column(
|
fontSize: 24,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
fontWeight: 800,
|
||||||
children: [
|
color: Colors.white,
|
||||||
_buildLoginForm(),
|
textAlign: TextAlign.center,
|
||||||
const SizedBox(height: 24),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 10),
|
||||||
Center(child: _buildVersionInfo()),
|
MyText(
|
||||||
],
|
"Streamline Project Management\nBoost Productivity with Automation.",
|
||||||
),
|
fontSize: 14,
|
||||||
),
|
color: Colors.white70,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
|
||||||
|
if (_isBetaEnvironment) ...[
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.orangeAccent,
|
||||||
|
borderRadius: BorderRadius.circular(6),
|
||||||
|
),
|
||||||
|
child: MyText(
|
||||||
|
'BETA',
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: 600,
|
||||||
|
fontSize: 12,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
|
||||||
|
const SizedBox(height: 36),
|
||||||
|
|
||||||
|
// Login Buttons
|
||||||
|
_buildLoginButton(
|
||||||
|
context,
|
||||||
|
label: "Login with Username",
|
||||||
|
icon: LucideIcons.mail,
|
||||||
|
option: LoginOption.email,
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: 16),
|
||||||
],
|
_buildLoginButton(
|
||||||
);
|
context,
|
||||||
},
|
label: "Login with OTP",
|
||||||
|
icon: LucideIcons.message_square,
|
||||||
|
option: LoginOption.otp,
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 36),
|
||||||
|
|
||||||
|
// Version Info
|
||||||
|
MyText(
|
||||||
|
'App version 1.0.0',
|
||||||
|
color: Colors.white60,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildHeader() {
|
Widget _buildLoginButton(BuildContext context,
|
||||||
return Container(
|
{required String label, required IconData icon, required LoginOption option}) {
|
||||||
padding: const EdgeInsets.all(12),
|
return SizedBox(
|
||||||
decoration: BoxDecoration(
|
width: double.infinity,
|
||||||
color: Colors.white,
|
child: ElevatedButton.icon(
|
||||||
borderRadius: BorderRadius.circular(16),
|
icon: Icon(icon, size: 20, color: Colors.white),
|
||||||
boxShadow: [
|
label: Padding(
|
||||||
BoxShadow(
|
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||||
color: Colors.black12,
|
child: MyText(label, fontSize: 16, fontWeight: 600, color: Colors.white),
|
||||||
blurRadius: 6,
|
|
||||||
offset: Offset(0, 3),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Image.asset(Images.logoDark, height: 70),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildBetaLabel() {
|
|
||||||
return Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.orangeAccent,
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
),
|
|
||||||
child: MyText(
|
|
||||||
'BETA',
|
|
||||||
color: Colors.white,
|
|
||||||
fontWeight: 600,
|
|
||||||
fontSize: 12,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildLoginOptionChips() {
|
|
||||||
return Wrap(
|
|
||||||
spacing: 12,
|
|
||||||
runSpacing: 8,
|
|
||||||
alignment: WrapAlignment.center,
|
|
||||||
children: [
|
|
||||||
_buildOptionChip(
|
|
||||||
title: "User Name",
|
|
||||||
icon: LucideIcons.mail,
|
|
||||||
value: LoginOption.email,
|
|
||||||
),
|
),
|
||||||
_buildOptionChip(
|
style: ElevatedButton.styleFrom(
|
||||||
title: "OTP",
|
backgroundColor: const Color(0xFFB71C1C),
|
||||||
icon: LucideIcons.message_square,
|
side: const BorderSide(color: Colors.white70),
|
||||||
value: LoginOption.otp,
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
|
elevation: 4,
|
||||||
),
|
),
|
||||||
],
|
onPressed: () => _showLoginDialog(context, option),
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildWelcomeTextsAndChips() {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
MyText(
|
|
||||||
"Welcome to Marco",
|
|
||||||
fontSize: 20,
|
|
||||||
fontWeight: 700,
|
|
||||||
color: Colors.white,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
MyText(
|
|
||||||
"Streamline Project Management and Boost Productivity with Automation.",
|
|
||||||
fontSize: 14,
|
|
||||||
color: Colors.white70,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
if (_isBetaEnvironment) ...[
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
_buildBetaLabel(),
|
|
||||||
],
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
_buildLoginOptionChips(),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Widget _buildOptionChip({
|
|
||||||
required String title,
|
|
||||||
required IconData icon,
|
|
||||||
required LoginOption value,
|
|
||||||
}) {
|
|
||||||
final bool isSelected = _selectedOption == value;
|
|
||||||
|
|
||||||
final Color selectedTextColor = contentTheme.brandRed;
|
|
||||||
final Color unselectedTextColor = Colors.white;
|
|
||||||
final Color selectedBgColor = Colors.grey[100]!;
|
|
||||||
final Color unselectedBgColor = contentTheme.brandRed;
|
|
||||||
|
|
||||||
return ChoiceChip(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
|
||||||
label: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
icon,
|
|
||||||
size: 18,
|
|
||||||
color: isSelected ? selectedTextColor : unselectedTextColor,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 6),
|
|
||||||
MyText(
|
|
||||||
title,
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: 600,
|
|
||||||
color: isSelected ? selectedTextColor : unselectedTextColor,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
selected: isSelected,
|
|
||||||
onSelected: (_) => setState(() => _selectedOption = value),
|
|
||||||
selectedColor: selectedBgColor,
|
|
||||||
backgroundColor: unselectedBgColor,
|
|
||||||
side: BorderSide(
|
|
||||||
color: Colors.white.withOpacity(0.6),
|
|
||||||
width: 1.2,
|
|
||||||
),
|
|
||||||
elevation: 3,
|
|
||||||
shadowColor: Colors.black12,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(15),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildLoginForm() {
|
|
||||||
switch (_selectedOption) {
|
|
||||||
case LoginOption.email:
|
|
||||||
return EmailLoginForm();
|
|
||||||
case LoginOption.otp:
|
|
||||||
return const OTPLoginScreen();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildVersionInfo() {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 8),
|
|
||||||
child: MyText(
|
|
||||||
'App version 1.0.0',
|
|
||||||
color: Colors.grey.shade500,
|
|
||||||
fontSize: 12,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -25,7 +25,7 @@ class MyApp extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final bool hasMpin = LocalStorage.getIsMpin();
|
final bool hasMpin = LocalStorage.getIsMpin();
|
||||||
logSafe("MPIN enabled: $hasMpin", sensitive: true);
|
logSafe("MPIN enabled: $hasMpin", );
|
||||||
|
|
||||||
if (hasMpin) {
|
if (hasMpin) {
|
||||||
await LocalStorage.setBool("mpin_verified", false);
|
await LocalStorage.setBool("mpin_verified", false);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user