diff --git a/sigap-mobile/lib/src/features/auth/presentasion/controllers/signup/step/id-card-verification/id_card_verification_controller.dart b/sigap-mobile/lib/src/features/auth/presentasion/controllers/signup/step/id-card-verification/id_card_verification_controller.dart index 2e12862..e09cec2 100644 --- a/sigap-mobile/lib/src/features/auth/presentasion/controllers/signup/step/id-card-verification/id_card_verification_controller.dart +++ b/sigap-mobile/lib/src/features/auth/presentasion/controllers/signup/step/id-card-verification/id_card_verification_controller.dart @@ -3,11 +3,13 @@ import 'dart:io'; import 'package:get/get.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:logger/logger.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:sigap/src/cores/services/azure_ocr_service.dart'; import 'package:sigap/src/features/auth/data/models/face_model.dart'; import 'package:sigap/src/features/auth/data/models/kta_model.dart'; import 'package:sigap/src/features/auth/data/models/ktp_model.dart'; +import 'package:sigap/src/features/auth/data/repositories/authentication_repository.dart'; class IdCardVerificationController extends GetxController { // Singleton instance @@ -16,12 +18,49 @@ class IdCardVerificationController extends GetxController { // Services final AzureOCRService _ocrService = AzureOCRService(); - final bool isOfficer; + // Store isOfficer as a reactive variable instead of a final field + final RxBool _isOfficer = RxBool(false); + bool get isOfficer => _isOfficer.value; // Maximum allowed file size in bytes (4MB) final int maxFileSizeBytes = 4 * 1024 * 1024; // 4MB in bytes - IdCardVerificationController({required this.isOfficer}); + // Constructor that accepts initial isOfficer value but will be updated from user data + IdCardVerificationController({bool isOfficer = false}) { + _isOfficer.value = isOfficer; + Logger().i( + 'IdCardVerificationController initialized with isOfficer: $_isOfficer', + ); + // Fetch current user role when controller is created + fetchCurrentUserRole(); + } + + // Method to fetch current user role and update isOfficer flag + Future fetchCurrentUserRole() async { + try { + final metadata = AuthenticationRepository.instance.authUser?.userMetadata; + + if (metadata == null) { + Logger().w('User metadata is null, cannot determine role'); + return; + } + + // Ambil nilai is_officer + final bool isOfficer = metadata['is_officer'] == true; + + // Simpan ke variabel observasi jika perlu + _isOfficer.value = isOfficer; + + Logger().i('Updated isOfficer flag from user role: $isOfficer'); + + // Tampilkan jenis kartu identitas yang perlu diverifikasi + final String idCardType = isOfficer ? 'KTA' : 'KTP'; + Logger().i('User should verify $idCardType based on role'); + } catch (e) { + Logger().e('Error fetching current user role: $e'); + // Keep the initial value if there's an error + } + } // Local storage keys static const String _kOcrResultsKey = 'ocr_results'; @@ -64,26 +103,34 @@ class IdCardVerificationController extends GetxController { try { final prefs = await SharedPreferences.getInstance(); final String jsonData = jsonEncode(results); + final String idCardType = isOfficer ? 'KTA' : 'KTP'; + + Logger().i( + 'Saving to local storage for ID card type: $idCardType (isOfficer=$isOfficer)', + ); + await prefs.setString(_kOcrResultsKey, jsonData); - await prefs.setString(_kIdCardTypeKey, isOfficer ? 'KTA' : 'KTP'); + await prefs.setString(_kIdCardTypeKey, idCardType); // Also save the model if (isOfficer && ktaModel.value != null) { + Logger().i('Saving KTA model to storage'); await prefs.setString( _kOcrModelKey, jsonEncode(ktaModel.value!.toJson()), ); } else if (!isOfficer && ktpModel.value != null) { + Logger().i('Saving KTP model to storage'); await prefs.setString( _kOcrModelKey, jsonEncode(ktpModel.value!.toJson()), ); } - print('OCR results saved to local storage: ${results.length} items'); - print('ID Card Type saved: ${isOfficer ? 'KTA' : 'KTP'}'); + Logger().i('OCR results saved to local storage: ${results.length} items'); + Logger().i('ID Card Type saved: $idCardType'); } catch (e) { - print('Error saving OCR results to local storage: $e'); + Logger().e('Error saving OCR results to local storage: $e'); } } @@ -97,7 +144,7 @@ class IdCardVerificationController extends GetxController { return decodedData.map((key, value) => MapEntry(key, value.toString())); } } catch (e) { - print('Error loading OCR results from local storage: $e'); + Logger().e('Error loading OCR results from local storage: $e'); } return {}; } @@ -191,41 +238,72 @@ class IdCardVerificationController extends GetxController { } try { + // Make sure we have the latest user role before proceeding + await fetchCurrentUserRole(); + isVerifying.value = true; final idCardType = isOfficer ? 'KTA' : 'KTP'; + Logger().i( + 'Starting ID card validation for ${isOfficer ? "KTA (Officer)" : "KTP (Citizen)"} - isOfficer flag: $isOfficer', + ); + // Basic validation to check if the image is clear enough for OCR bool isImageValid = false; try { - // Process the ID card with OCR (still using Azure OCR) + Logger().i( + 'About to process ID card image with OCR service - isOfficer flag: $isOfficer', + ); + + // Process the ID card with OCR final result = await _ocrService.processIdCard( idCardImage.value!, isOfficer, ); + Logger().i( + 'OCR processing completed for $idCardType - Data received: ${result.length} fields', + ); + // Store the extraction results extractedInfo.assignAll(result); hasExtractedInfo.value = result.isNotEmpty; // Save the OCR results to local storage if (result.isNotEmpty) { - print('Saving OCR results to local storage...'); + Logger().i('Saving $idCardType OCR results to local storage...'); await _saveOcrResultsToLocalStorage(result); } + // Debug the extracted data + Logger().i('Card type being validated: $idCardType'); + Logger().i('isOfficer flag: $isOfficer'); + Logger().i('Extracted data: $result'); + // Check if the extracted information is valid using our validation methods if (isOfficer) { + Logger().i('Validating as KTA (Officer card)'); isImageValid = _ocrService.isKtaValid(result); - } else { - isImageValid = _ocrService.isKtpValid(result); - } + Logger().i('KTA validation result: $isImageValid'); - // Create model from extracted data - if (isOfficer) { + // Create KTA model from extracted data ktaModel.value = _ocrService.createKtaModel(result); + Logger().i('KTA model created: ${ktaModel.value != null}'); + + // Ensure KTP model is null + ktpModel.value = null; } else { + Logger().i('Validating as KTP (Citizen card)'); + isImageValid = _ocrService.isKtpValid(result); + Logger().i('KTP validation result: $isImageValid'); + + // Create KTP model from extracted data ktpModel.value = _ocrService.createKtpModel(result); + Logger().i('KTP model created: ${ktpModel.value != null}'); + + // Ensure KTA model is null + ktaModel.value = null; } if (isImageValid) { @@ -233,10 +311,14 @@ class IdCardVerificationController extends GetxController { isIdCardValid.value = true; idCardValidationMessage.value = '$idCardType image looks valid. Please confirm this is your $idCardType.'; + Logger().i('$idCardType validation successful'); } else { isIdCardValid.value = false; idCardValidationMessage.value = 'Unable to verify your $idCardType clearly. Please ensure all text is visible and try again.'; + Logger().i( + '$idCardType validation failed - unable to verify clearly', + ); } } on SocketException catch (e) { isIdCardValid.value = false; @@ -248,10 +330,16 @@ class IdCardVerificationController extends GetxController { idCardValidationMessage.value = 'Network error occurred. Please try again later.'; } + Logger().e( + 'Network error during $idCardType validation: ${e.toString()}', + ); } catch (processingError) { isIdCardValid.value = false; idCardValidationMessage.value = 'We\'re having trouble processing your $idCardType. Please try again with a clearer image.'; + Logger().e( + 'Processing error during $idCardType validation: ${processingError.toString()}', + ); } } catch (e) { isIdCardValid.value = false; @@ -265,14 +353,22 @@ class IdCardVerificationController extends GetxController { 'The verification is taking too long. Please try again later when the connection is better.'; } else { // Generic but still user-friendly error message + final idCardType = isOfficer ? 'KTA' : 'KTP'; idCardValidationMessage.value = - 'We encountered an issue during verification. Please try again later.'; + 'We encountered an issue during $idCardType verification. Please try again later.'; } // Log the actual error for debugging (wouldn't be shown to the user) - print('ID Card validation error: ${e.toString()}'); + Logger().e('ID Card validation error: ${e.toString()}'); } finally { isVerifying.value = false; + + // Final verification of which model is set + Logger().i( + 'Final verification - Card type: ${isOfficer ? "KTA" : "KTP"}', + ); + Logger().i('KTP model is null: ${ktpModel.value == null}'); + Logger().i('KTA model is null: ${ktaModel.value == null}'); } } @@ -304,7 +400,7 @@ class IdCardVerificationController extends GetxController { await prefs.remove(_kOcrModelKey); await prefs.remove(_kIdCardTypeKey); } catch (e) { - print('Error clearing OCR results from local storage: $e'); + Logger().e('Error clearing OCR results from local storage: $e'); } } @@ -315,14 +411,14 @@ class IdCardVerificationController extends GetxController { // Log storage data for debugging SharedPreferences.getInstance().then((prefs) { - print('Storage check on confirmation:'); - print( + Logger().i('Storage check on confirmation:'); + Logger().i( 'OCR results: ${prefs.getString(_kOcrResultsKey)?.substring(0, 50)}...', ); - print( + Logger().i( 'OCR model: ${prefs.getString(_kOcrModelKey)?.substring(0, 50)}...', ); - print('ID card type: ${prefs.getString(_kIdCardTypeKey)}'); + Logger().i('ID card type: ${prefs.getString(_kIdCardTypeKey)}'); }); } } @@ -331,4 +427,25 @@ class IdCardVerificationController extends GetxController { dynamic get verifiedIdCardModel { return isOfficer ? ktaModel.value : ktpModel.value; } + + // Debug helper method - can be called from UI for troubleshooting + void debugControllerState() { + Logger().i('======= ID CARD VERIFICATION CONTROLLER STATE ======='); + Logger().i('isOfficer: $isOfficer'); + Logger().i('Current ID card type: ${isOfficer ? "KTA" : "KTP"}'); + Logger().i('Has ID card image: ${idCardImage.value != null}'); + Logger().i('Is ID card valid: ${isIdCardValid.value}'); + Logger().i('Has confirmed ID card: ${hasConfirmedIdCard.value}'); + Logger().i('Has extracted info: ${hasExtractedInfo.value}'); + Logger().i('Extracted info fields: ${extractedInfo.length}'); + Logger().i('KTP model null: ${ktpModel.value == null}'); + Logger().i('KTA model null: ${ktaModel.value == null}'); + Logger().i('================================================'); + } + + // Method to force update the isOfficer flag (useful for testing or if user role changes) + void updateIsOfficerFlag(bool value) { + Logger().i('Manually updating isOfficer flag from $isOfficer to $value'); + _isOfficer.value = value; + } } diff --git a/sigap-mobile/lib/src/features/auth/presentasion/pages/signup/step/id-card-verification/id_card_verification_step.dart b/sigap-mobile/lib/src/features/auth/presentasion/pages/signup/step/id-card-verification/id_card_verification_step.dart index ed433bb..78532e6 100644 --- a/sigap-mobile/lib/src/features/auth/presentasion/pages/signup/step/id-card-verification/id_card_verification_step.dart +++ b/sigap-mobile/lib/src/features/auth/presentasion/pages/signup/step/id-card-verification/id_card_verification_step.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:logger/logger.dart'; import 'package:sigap/src/features/auth/data/models/kta_model.dart'; import 'package:sigap/src/features/auth/data/models/ktp_model.dart'; import 'package:sigap/src/features/auth/presentasion/controllers/signup/main/registration_form_controller.dart'; @@ -25,8 +26,25 @@ class IdCardVerificationStep extends StatelessWidget { mainController.formKey = formKey; final isOfficer = mainController.selectedRole.value?.isOfficer ?? false; + + // Ensure controller's isOfficer flag matches mainController's selected role + if (controller.isOfficer != isOfficer) { + controller.updateIsOfficerFlag(isOfficer); + Logger().i( + 'Updated controller isOfficer flag to match selected role: $isOfficer', + ); + } + final String idCardType = isOfficer ? 'KTA' : 'KTP'; + Logger().i( + 'Building IdCardVerificationStep with isOfficer: $isOfficer (Card type: $idCardType)', + ); + Logger().i('Controller isOfficer flag: ${controller.isOfficer}'); + + // Call the debug method to print full controller state + controller.debugControllerState(); + return Form( key: formKey, child: Column(