diff --git a/lib/app/app_extension.dart b/lib/app/app_extension.dart index 07477b1..513c2f0 100644 --- a/lib/app/app_extension.dart +++ b/lib/app/app_extension.dart @@ -32,7 +32,8 @@ Future isTokenExpired() async { try { DateTime expiredDate = DateTime.parse(expireDateStr); - DateTime batasExpired = expiredDate.subtract(Duration(minutes: 3)); + // DateTime batasExpired = expiredDate.subtract(Duration(minutes: 3)); + DateTime batasExpired = expiredDate.subtract(Duration(minutes: 10)); DateTime now = DateTime.now(); final bool expired = now.isAfter(batasExpired); diff --git a/lib/app/constant.dart b/lib/app/constant.dart index 9906f44..9a1480c 100644 --- a/lib/app/constant.dart +++ b/lib/app/constant.dart @@ -4,6 +4,11 @@ class Constant { // static double designHeight = 1024; // static double designWidth = 1440; + // prosesAksi + static String getRiwayat = "getRiwayat"; + static String postUploadFoto = "postUploadFoto"; + static String postEditScan = "postEditScan"; + // base url static String baseURL = "http://devone.aplikasi.web.id/"; diff --git a/lib/app/route.dart b/lib/app/route.dart index 098efe1..3bb9e00 100644 --- a/lib/app/route.dart +++ b/lib/app/route.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:scanktpflutter/screen/no-login-scan/no_login_scan_screen.dart'; import '../screen/no-login-home/no_login_home_screen.dart'; +import '../screen/no-login-scan/no_login_edit_scan_screen.dart'; import '../screen/no-login-splash/no_login_splash_screen.dart'; import '../screen/no-login/no_login_screen.dart'; @@ -119,6 +120,16 @@ class AppRoute { }); } + if (settings.name == noLoginEditScanRoute) { + return MaterialPageRoute(builder: (context) { + return MediaQuery( + data: MediaQuery.of(context).copyWith( + textScaler: TextScaler.linear(1.0), padding: EdgeInsets.all(0)), + child: NoLoginEditScanScreen(), + ); + }); + } + return MaterialPageRoute(builder: (context) { return MediaQuery( data: MediaQuery.of(context).copyWith( diff --git a/lib/model/no-login/no_login_edit_person_ktp_model.dart b/lib/model/no-login/no_login_edit_person_ktp_model.dart new file mode 100644 index 0000000..ca5a46c --- /dev/null +++ b/lib/model/no-login/no_login_edit_person_ktp_model.dart @@ -0,0 +1,49 @@ +class NoLoginEditPersonKtpModel { + final String personID; + final String personNIK; + final String personName; + final String personDob; + final String personSex; + final String personUrl; + final String personQrCode; + final String m_sexname; + + NoLoginEditPersonKtpModel({ + required this.personID, + required this.personNIK, + required this.personName, + required this.personDob, + required this.personSex, + required this.personUrl, + required this.personQrCode, + required this.m_sexname, + }); + + // Convert JSON to Model + factory NoLoginEditPersonKtpModel.fromJson(Map json) { + return NoLoginEditPersonKtpModel( + personID: json['Person_ID'] ?? "", + personNIK: json['Person_NIK'] ?? "", + personName: json['Person_Name'] ?? "", + personDob: json['Person_Dob'] ?? DateTime.now(), + personSex: json['Person_Sex'] ?? "", + personUrl: json['Person_Url'] ?? "", + personQrCode: json['Person_QrCode'] ?? "", + m_sexname: json['m_sexname'] ?? "", + ); + } + + // Convert Model to JSON + Map toJson() { + return { + 'Person_ID':personID, + 'Person_NIK': personNIK, + 'Person_Name': personName, + 'Person_Dob': personDob, + 'Person_Sex': personSex, + 'Person_Url': personUrl, + 'Person_QrCode': personQrCode, + 'm_sexname':m_sexname, + }; + } +} diff --git a/lib/model/no-login/no_login_person_ktp_model.dart b/lib/model/no-login/no_login_person_ktp_model.dart new file mode 100644 index 0000000..6816873 --- /dev/null +++ b/lib/model/no-login/no_login_person_ktp_model.dart @@ -0,0 +1,49 @@ +class NoLoginPersonKtpModel { + final String personID; + final String personNIK; + final String personName; + final String personDob; + final String personSex; + final String personUrl; + final String personQrCode; + final String m_sexname; + + NoLoginPersonKtpModel({ + required this.personID, + required this.personNIK, + required this.personName, + required this.personDob, + required this.personSex, + required this.personUrl, + required this.personQrCode, + required this.m_sexname, + }); + + // Convert JSON to Model + factory NoLoginPersonKtpModel.fromJson(Map json) { + return NoLoginPersonKtpModel( + personID: json['Person_ID'] ?? "", + personNIK: json['Person_NIK'] ?? "", + personName: json['Person_Name'] ?? "", + personDob: json['Person_Dob'] ?? DateTime.now(), + personSex: json['Person_Sex'] ?? "", + personUrl: json['Person_Url'] ?? "", + personQrCode: json['Person_QrCode'] ?? "", + m_sexname: json['m_sexname'] ?? "", + ); + } + + // Convert Model to JSON + Map toJson() { + return { + 'Person_ID':personID, + 'Person_NIK': personNIK, + 'Person_Name': personName, + 'Person_Dob': personDob, + 'Person_Sex': personSex, + 'Person_Url': personUrl, + 'Person_QrCode': personQrCode, + 'm_sexname':m_sexname, + }; + } +} diff --git a/lib/model/no-login/no_login_sex_model.dart b/lib/model/no-login/no_login_sex_model.dart new file mode 100644 index 0000000..71b17ec --- /dev/null +++ b/lib/model/no-login/no_login_sex_model.dart @@ -0,0 +1,31 @@ +class NoLoginSexModel { + final String M_SexID; + final String M_SexCode; + final String m_sexname; + final String M_SexNameLang; + + NoLoginSexModel({ + required this.M_SexID, + required this.M_SexCode, + required this.m_sexname, + required this.M_SexNameLang, + }); + + factory NoLoginSexModel.fromJson(Map json) { + return NoLoginSexModel( + M_SexID: json['M_SexID'], + M_SexCode: json['M_SexCode'], + m_sexname: json['m_sexname'], + M_SexNameLang: json['M_SexNameLang'], + ); + } + + Map toJson() { + return { + 'M_SexID': M_SexID, + 'M_SexCode': M_SexCode, + 'm_sexname': m_sexname, + 'M_SexNameLang': M_SexNameLang, + }; + } +} diff --git a/lib/model/no-login/no_login_sukses_person_model.dart b/lib/model/no-login/no_login_sukses_person_model.dart new file mode 100644 index 0000000..bdd0ab9 --- /dev/null +++ b/lib/model/no-login/no_login_sukses_person_model.dart @@ -0,0 +1,49 @@ +class NoLoginSuksesPersonKtpModel { + final String personID; + final String personNIK; + final String personName; + final String personDob; + final String personSex; + final String personUrl; + final String personQrCode; + final String m_sexname; + + NoLoginSuksesPersonKtpModel({ + required this.personID, + required this.personNIK, + required this.personName, + required this.personDob, + required this.personSex, + required this.personUrl, + required this.personQrCode, + required this.m_sexname, + }); + + // Convert JSON to Model + factory NoLoginSuksesPersonKtpModel.fromJson(Map json) { + return NoLoginSuksesPersonKtpModel( + personID: json['Person_ID'] ?? "", + personNIK: json['Person_NIK'] ?? "", + personName: json['Person_Name'] ?? "", + personDob: json['Person_Dob'] ?? DateTime.now(), + personSex: json['Person_Sex'] ?? "", + personUrl: json['Person_Url'] ?? "", + personQrCode: json['Person_QrCode'] ?? "", + m_sexname: json['m_sexname'] ?? "", + ); + } + + // Convert Model to JSON + Map toJson() { + return { + 'Person_ID':personID, + 'Person_NIK': personNIK, + 'Person_Name': personName, + 'Person_Dob': personDob, + 'Person_Sex': personSex, + 'Person_Url': personUrl, + 'Person_QrCode': personQrCode, + 'm_sexname':m_sexname, + }; + } +} diff --git a/lib/provider/no-login-scan/no_login_scan_provider.dart b/lib/provider/no-login-scan/no_login_scan_provider.dart index cbc6ea2..7bf886a 100644 --- a/lib/provider/no-login-scan/no_login_scan_provider.dart +++ b/lib/provider/no-login-scan/no_login_scan_provider.dart @@ -1,32 +1,39 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:mobile_scanner/mobile_scanner.dart'; -import 'package:scanktpflutter/model/sex_model.dart'; +import 'package:scanktpflutter/model/no-login/no_login_edit_person_ktp_model.dart'; +import 'package:scanktpflutter/model/no-login/no_login_person_ktp_model.dart'; +import 'package:scanktpflutter/model/no-login/no_login_sex_model.dart'; +import '../../app/constant.dart'; import '../../model/edit_person_model.dart'; -import '../../model/person_ktp_model.dart'; // list scan -final listScanRwt = StateProvider>( +final listScanRwt = StateProvider>( (ref) => List.empty( growable: true, ), ); final selectedPersonIdx = StateProvider((ref) => "0"); -final selectedEdit = StateProvider( - (ref) => EditPersonModel( +final selectedEdit = StateProvider( + (ref) => NoLoginEditPersonKtpModel( personID: "", personNIK: "", personName: "", personDob: "", personSex: "", personUrl: "", + personQrCode: "", m_sexname: "", ), ); // inputan edit +final eQrCode = StateProvider( + (ref) => TextEditingController(text: ""), +); + final eNikCtr = StateProvider( (ref) => TextEditingController(text: ""), ); @@ -47,8 +54,8 @@ final eSexCtr = StateProvider( (ref) => TextEditingController(text: ""), ); -final eSexSelected = StateProvider( - (ref) => SexModel( +final eSexSelected = StateProvider( + (ref) => NoLoginSexModel( M_SexID: "", M_SexCode: "", m_sexname: "", @@ -58,4 +65,6 @@ final eSexSelected = StateProvider( final barcodeX = StateProvider( (ref) => Barcode(), -); \ No newline at end of file +); + +final noLoginprosesAksi = StateProvider((ref) => Constant.getRiwayat); \ No newline at end of file diff --git a/lib/repository/no-login-scan/no_login_scan_repository.dart b/lib/repository/no-login-scan/no_login_scan_repository.dart index 70386d1..fc883a6 100644 --- a/lib/repository/no-login-scan/no_login_scan_repository.dart +++ b/lib/repository/no-login-scan/no_login_scan_repository.dart @@ -1,50 +1,56 @@ -import '../../model/person_ktp_model.dart'; -import '../../model/sex_model.dart'; - -import '../../model/sukses_person_model.dart'; +import '../../model/no-login/no_login_person_ktp_model.dart'; +import '../../model/no-login/no_login_sex_model.dart'; +import '../../model/no-login/no_login_sukses_person_model.dart'; import '../base_repository.dart'; class NoLoginScanRepository extends BaseRepository { NoLoginScanRepository({required super.dio}); - Future> listRiwayatScanRepo({ + Future> listRiwayatScanRepo({ required String host, - required String userId, + required String client_id, required String token, }) async { // final service = "${Constant.baseUrl}xauth/login"; final service = "http://${host}/one-api/scan-ktpv2-no-login/Scanktpv2/listRiwayatScan"; final resp = await post( - param: {"userId": userId}, + param: {"client_id": client_id}, service: service, token: token, ); - final result = List.empty(growable: true); + final result = List.empty(growable: true); resp['data'].forEach((e) { - final model = PersonKtp.fromJson(e); + final model = NoLoginPersonKtpModel.fromJson(e); result.add(model); }); return result; } - Future> prosesScan({ + Future> prosesScan({ required String host, required String base64File, - required String userId, + required String client_id, + required String qr_code, + required String token, }) async { final service = "http://${host}/one-api/scan-ktpv2-no-login/Scanktpv2/proses_scan"; - final resp = await post(param: { - "base64File": base64File, - "userId": userId, - }, service: service); + final resp = await post( + param: { + "base64File": base64File, + "client_id": client_id, + "qr_code": qr_code, + }, + service: service, + token: token, + ); - final result = List.empty(growable: true); + final result = List.empty(growable: true); resp['data'].forEach((e) { - final model = SuksesPersonModel.fromJson(e); + final model = NoLoginSuksesPersonKtpModel.fromJson(e); result.add(model); }); @@ -52,16 +58,23 @@ class NoLoginScanRepository extends BaseRepository { } // sex - Future> sexRepo({ + Future> sexRepo({ required String host, + required String client_id, + required String token, }) async { // final service = "${Constant.baseUrl}xauth/login"; - final service = "http://${host}/one-api/scan-ktpv2-no-login/Scanktpv2/getSex"; - final resp = await post(param: {}, service: service); + final service = + "http://${host}/one-api/scan-ktpv2-no-login/Scanktpv2/getSex"; + final resp = await post( + param: {"client_id": client_id}, + service: service, + token: token, + ); - final result = List.empty(growable: true); + final result = List.empty(growable: true); resp['data'].forEach((e) { - final model = SexModel.fromJson(e); + final model = NoLoginSexModel.fromJson(e); result.add(model); }); @@ -70,8 +83,9 @@ class NoLoginScanRepository extends BaseRepository { // edit Future prosesEdit({ + required String token, required String host, - required String userId, + required String client_id, required String Person_ID, required String Person_NIK, required String Person_Name, @@ -87,9 +101,10 @@ class NoLoginScanRepository extends BaseRepository { "Person_Name": Person_Name, "Person_Dob": Person_Dob, "Person_Sex": Person_Sex, - "userId": userId, + "client_id": client_id, }, service: service, + token: token, ); if (resp['status'] == "OK") { diff --git a/lib/screen/home/card_riwayat_scan.dart b/lib/screen/home/card_riwayat_scan.dart index 74e5474..b6d1629 100644 --- a/lib/screen/home/card_riwayat_scan.dart +++ b/lib/screen/home/card_riwayat_scan.dart @@ -8,7 +8,7 @@ import '../../app/route.dart'; import '../../app/constant.dart'; import '../../model/edit_person_model.dart'; import '../../model/person_ktp_model.dart'; -import '../../provider/no-login-scan/no_login_scan_provider.dart'; +import '../../provider/scan_provider.dart'; class CardRiwayatScan extends HookConsumerWidget { final PersonKtp data; diff --git a/lib/screen/home/home_screen.dart b/lib/screen/home/home_screen.dart index 189950b..bd68e09 100644 --- a/lib/screen/home/home_screen.dart +++ b/lib/screen/home/home_screen.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import '../../provider/no-login-scan/no_login_scan_provider.dart'; +import 'package:scanktpflutter/provider/scan_provider.dart'; import '../../screen/home/card_riwayat_scan.dart'; import '../../screen/home/list_riwayat_scan_provider.dart'; diff --git a/lib/screen/home/list_riwayat_scan_provider.dart b/lib/screen/home/list_riwayat_scan_provider.dart index 1a6d32e..ea07824 100644 --- a/lib/screen/home/list_riwayat_scan_provider.dart +++ b/lib/screen/home/list_riwayat_scan_provider.dart @@ -1,6 +1,6 @@ import 'package:equatable/equatable.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import '../../provider/no-login-scan/no_login_scan_provider.dart'; +import '../../provider/scan_provider.dart'; import '../../repository/scan_repository.dart'; import '../../model/person_ktp_model.dart'; diff --git a/lib/screen/no-login-home/no_login_card_riwayat_scan.dart b/lib/screen/no-login-home/no_login_card_riwayat_scan.dart index f563ca4..3095e12 100644 --- a/lib/screen/no-login-home/no_login_card_riwayat_scan.dart +++ b/lib/screen/no-login-home/no_login_card_riwayat_scan.dart @@ -1,17 +1,17 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:scanktpflutter/model/sex_model.dart'; +import '../../model/no-login/no_login_edit_person_ktp_model.dart'; +import '../../model/no-login/no_login_person_ktp_model.dart'; +import '../../model/no-login/no_login_sex_model.dart'; import '../../app/app_extension.dart'; import '../../app/route.dart'; import '../../app/constant.dart'; -import '../../model/edit_person_model.dart'; -import '../../model/person_ktp_model.dart'; import '../../provider/no-login-scan/no_login_scan_provider.dart'; class NoLoginCardRiwayatScan extends HookConsumerWidget { - final PersonKtp data; + final NoLoginPersonKtpModel data; const NoLoginCardRiwayatScan({ super.key, required this.data, @@ -52,14 +52,14 @@ class NoLoginCardRiwayatScan extends HookConsumerWidget { data.personID; // set SEX - ref.read(eSexSelected.notifier).state = SexModel( + ref.read(eSexSelected.notifier).state = NoLoginSexModel( M_SexID: data.personSex, M_SexCode: "", m_sexname: data.m_sexname, M_SexNameLang: "" ); - ref.read(selectedEdit.notifier).state = EditPersonModel( + ref.read(selectedEdit.notifier).state = NoLoginEditPersonKtpModel( personID: data.personID, personNIK: data.personNIK, personName: data.personName, @@ -67,9 +67,10 @@ class NoLoginCardRiwayatScan extends HookConsumerWidget { personDob: data.personDob, personSex: data.personSex, personUrl: data.personUrl, + personQrCode: data.personQrCode, ); Navigator.of(context).pushNamed( - editScanRoute, + noLoginEditScanRoute, ); }, child: Icon( diff --git a/lib/screen/no-login-home/no_login_home_screen.dart b/lib/screen/no-login-home/no_login_home_screen.dart index 884602b..1f0533e 100644 --- a/lib/screen/no-login-home/no_login_home_screen.dart +++ b/lib/screen/no-login-home/no_login_home_screen.dart @@ -28,25 +28,29 @@ class NoLoginHomeScreen extends HookConsumerWidget { final host = currentUser?.host ?? ""; final isLoading = useState(false); final listScanArr = ref.watch(listScanRwt); - final userId = currentUser?.client_id ?? ""; + final client_id = currentUser?.client_id ?? ""; + final token = currentUser?.token ?? ""; + final expire_date = currentUser?.expire_date ?? ""; + final readProsesAksi = ref.watch(noLoginprosesAksi); useEffect(() { WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { - final userID = currentUser?.client_id ?? "0"; - // final prefs = await SharedPreferences.getInstance(); - final token = currentUser?.token ?? ""; - - if (userID == "0") { - // not logged in + if (client_id == "0" || token == "") { + // not logged in and doesn't have token Navigator.of(context) .pushNamedAndRemoveUntil(noLoginRoute, (route) => false); return; } - if (token == "") { - Navigator.of(context) - .pushNamedAndRemoveUntil(noLoginRoute, (route) => false); - return; + if (token != "") { + if (await isTokenExpired() == true) { + print("Token expired, refreshing..."); + ref.read(noLoginRefreshTokenProvider.notifier).refreshToken( + client_id: client_id, + host: host, + expire_date: expire_date, + ); + } } }); return () {}; @@ -56,14 +60,42 @@ class NoLoginHomeScreen extends HookConsumerWidget { WidgetsBinding.instance.addPostFrameCallback((timestap) async { ref.read(noLoginRiwayatScanProvider.notifier).noLoginRiwayatScan( host: host, - userId: userId, - token: currentUser?.token ?? "", - expire_date: currentUser?.expire_date ?? "", + client_id: client_id, + token: token, + expire_date: expire_date, ); }); return () {}; }, []); + // refreshToken Provider + ref.listen(noLoginRefreshTokenProvider, (prev, next) async { + if (next is NoLoginRefreshTokenStateLoading) { + isLoading.value = true; + } else if (next is NoLoginRefreshTokenStateError) { + isLoading.value = false; + // errorMessage.value = next.message; + snackbarWidget( + context, + next.message, + snackbarType.error, + Duration(seconds: 3), + ); + } else if (next is NoLoginRefreshTokenStateDone) { + isLoading.value = false; + + // check proses aksi yg berlangsung + if (readProsesAksi == Constant.getRiwayat) { + ref.read(noLoginRiwayatScanProvider.notifier).noLoginRiwayatScan( + host: host, + client_id: client_id, + token: token, + expire_date: expire_date, + ); + } + } + }); + // listRiwayProvider ref.listen(noLoginRiwayatScanProvider, (prev, next) async { if (next is NoLoginRiwayatScanStateLoading) { @@ -83,33 +115,20 @@ class NoLoginHomeScreen extends HookConsumerWidget { }); void getRiwayat() async { - final currentUser = ref.read(noLoginCurrentUserProvider); - final host = currentUser?.host ?? ""; - final userId = currentUser?.client_id ?? ""; - final expireDate = currentUser?.expire_date ?? ""; - if (await isTokenExpired() == true) { + ref.read(noLoginprosesAksi.notifier).state = Constant.getRiwayat; print("Token expired, refreshing..."); ref.read(noLoginRefreshTokenProvider.notifier).refreshToken( - client_id: userId, + client_id: client_id, host: host, - expire_date: expireDate, - ); - - ref.read(noLoginRiwayatScanProvider.notifier).noLoginRiwayatScan( - host: host, - userId: userId, - token: ref.read(noLoginCurrentUserProvider)?.token ?? "", - expire_date: - ref.read(noLoginCurrentUserProvider)?.expire_date ?? "", + expire_date: expire_date, ); } else { ref.read(noLoginRiwayatScanProvider.notifier).noLoginRiwayatScan( host: host, - userId: userId, - token: ref.read(noLoginCurrentUserProvider)?.token ?? "", - expire_date: - ref.read(noLoginCurrentUserProvider)?.expire_date ?? "", + client_id: client_id, + token: token, + expire_date: expire_date, ); } } diff --git a/lib/screen/no-login-home/no_login_riwayat_scan_provider.dart b/lib/screen/no-login-home/no_login_riwayat_scan_provider.dart index f0e5791..9e644f2 100644 --- a/lib/screen/no-login-home/no_login_riwayat_scan_provider.dart +++ b/lib/screen/no-login-home/no_login_riwayat_scan_provider.dart @@ -1,11 +1,9 @@ import 'package:equatable/equatable.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:scanktpflutter/screen/no-login/no_login_refresh_token_provider.dart'; -import '../../app/app_extension.dart'; +import 'package:scanktpflutter/model/no-login/no_login_person_ktp_model.dart'; import '../../repository/no-login-scan/no_login_scan_repository.dart'; import '../../provider/no-login-scan/no_login_scan_provider.dart'; -import '../../model/person_ktp_model.dart'; import '../../provider/dio_provider.dart'; import '../../repository/base_repository.dart'; @@ -22,7 +20,7 @@ class NoLoginRiwayatScanNotifier : super(NoLoginRiwayatScanStateInit()); void noLoginRiwayatScan({ required String host, - required String userId, + required String client_id, required String token, required String expire_date, }) async { @@ -32,7 +30,7 @@ class NoLoginRiwayatScanNotifier dio: ref.read(dioProvider), ).listRiwayatScanRepo( host: host, - userId: userId, + client_id: client_id, token: token, ); @@ -83,7 +81,7 @@ class NoLoginRiwayatScanStateError extends NoLoginRiwayatScanState { } class NoLoginRiwayatScanStateDone extends NoLoginRiwayatScanState { - final List model; + final List model; NoLoginRiwayatScanStateDone({ required this.model, }) : super(DateTime.now()); diff --git a/lib/screen/no-login-scan/no_login_edit_scan_provider.dart b/lib/screen/no-login-scan/no_login_edit_scan_provider.dart new file mode 100644 index 0000000..4926bd8 --- /dev/null +++ b/lib/screen/no-login-scan/no_login_edit_scan_provider.dart @@ -0,0 +1,81 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:scanktpflutter/repository/no-login-scan/no_login_scan_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +// 3. state provider +final noLoginEditScanProvider = StateNotifierProvider( + (ref) => NoLoginEditScanNotifier(ref: ref)); + +// 2. notifier +class NoLoginEditScanNotifier extends StateNotifier { + final Ref ref; + NoLoginEditScanNotifier({required this.ref}) : super(NoLoginEditScanStateInit()); + void editScan({ + required String token, + required String host, + required String client_id, + required String Person_ID, + required String Person_NIK, + required String Person_Name, + required String Person_Dob, + required String Person_Sex, + }) async { + try { + state = NoLoginEditScanStateLoading(); + final resp = await NoLoginScanRepository( + dio: ref.read(dioProvider), + ).prosesEdit( + token: token, + host: host, + Person_ID: Person_ID, + Person_NIK: Person_NIK, + Person_Name: Person_Name, + Person_Dob: Person_Dob, + Person_Sex: Person_Sex, + client_id: client_id, + ); + + // print(resp); + state = NoLoginEditScanStateDone(pesan: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = NoLoginEditScanStateError(message: e.message); + } else { + state = NoLoginEditScanStateError(message: e.toString()); + } + } + } +} + +// 1. state +abstract class NoLoginEditScanState extends Equatable { + final DateTime date; + const NoLoginEditScanState(this.date); + @override + List get props => [date]; +} + +class NoLoginEditScanStateInit extends NoLoginEditScanState { + NoLoginEditScanStateInit() : super(DateTime.now()); +} + +class NoLoginEditScanStateLoading extends NoLoginEditScanState { + NoLoginEditScanStateLoading() : super(DateTime.now()); +} + +class NoLoginEditScanStateError extends NoLoginEditScanState { + final String message; + NoLoginEditScanStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class NoLoginEditScanStateDone extends NoLoginEditScanState { + final String pesan; + NoLoginEditScanStateDone({ + required this.pesan, + }) : super(DateTime.now()); +} diff --git a/lib/screen/no-login-scan/no_login_edit_scan_screen.dart b/lib/screen/no-login-scan/no_login_edit_scan_screen.dart new file mode 100644 index 0000000..c01a080 --- /dev/null +++ b/lib/screen/no-login-scan/no_login_edit_scan_screen.dart @@ -0,0 +1,715 @@ +import 'package:dropdown_button2/dropdown_button2.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:scanktpflutter/model/no-login/no_login_sex_model.dart'; +import 'package:scanktpflutter/provider/no-login-scan/no_login_scan_provider.dart'; +import 'package:scanktpflutter/screen/no-login-scan/no_login_edit_scan_provider.dart'; +import 'package:scanktpflutter/screen/scan/edit_scan_provider.dart'; +import '../../model/sex_model.dart'; +import '../../provider/no-login/no_login_current_user_provider.dart'; +import '../../widget/customsnackbarwidget.dart'; + +import '../../app/app_extension.dart'; +import '../../app/constant.dart'; +import '../../app/route.dart'; +import '../../provider/current_user_provider.dart'; +import '../home/list_riwayat_scan_provider.dart'; +import '../no-login-home/no_login_riwayat_scan_provider.dart'; +import '../no-login/no_login_refresh_token_provider.dart'; +import 'no_login_sex_provider.dart'; + +class NoLoginEditScanScreen extends HookConsumerWidget { + const NoLoginEditScanScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [ + SystemUiOverlay.bottom, + ]); + + final currentUser = ref.watch(noLoginCurrentUserProvider); + // final username = currentUser?.model.username ?? "-"; + final host = currentUser?.host ?? ""; + final isLoading = useState(false); + final client_id = currentUser?.client_id ?? ""; + final token = currentUser?.token ?? ""; + final expire_date = currentUser?.expire_date ?? ""; + final readProsesAksi = ref.watch(noLoginprosesAksi); + final baseUrl = "https://$host/"; + + final selectedPersonId = ref.watch(selectedPersonIdx); + + final listDataEdit = ref.watch(selectedEdit); + + final listSex = useState>( + List.empty(growable: true), + ); + + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + if (client_id == "0" || token == "") { + // not logged in and doesn't have token + Navigator.of(context) + .pushNamedAndRemoveUntil(noLoginRoute, (route) => false); + return; + } + + if (token != "") { + if (await isTokenExpired() == true) { + print("Token expired, refreshing..."); + ref.read(noLoginRefreshTokenProvider.notifier).refreshToken( + client_id: client_id, + host: host, + expire_date: expire_date, + ); + } + } + }); + return () {}; + }, [currentUser]); + + // sex + ref.listen(noLoginSexProvider, (prev, next) { + if (next is NoLoginSexStateLoading) { + isLoading.value = true; + } else if (next is NoLoginSexStateError) { + isLoading.value = false; + // errorMessage.value = next.message; + snackbarWidget( + context, + next.message, + snackbarType.error, + Duration(seconds: 3), + ); + } else if (next is NoLoginSexStateDone) { + isLoading.value = false; + listSex.value = next.model; + // ref.read(eSexCtr.notifier).state = + // TextEditingController(text: next.model[0].m_sexname); + + final sexSel = ref.read(selectedEdit.notifier).state; + if (next.model.isNotEmpty) { + final matchedItems = next.model + .where((item) => item.M_SexID == sexSel.personSex) + .toList(); + + if (matchedItems.length == 1) { + ref.read(eSexCtr.notifier).state = TextEditingController( + text: matchedItems[0].m_sexname, + ); + } else { + // Jika lebih dari satu atau tidak ada yang cocok + print('Error: Multiple or no matching items found.'); + // Bisa juga mengatur default value jika tidak ditemukan + ref.read(eSexCtr.notifier).state = + TextEditingController(text: ''); // atau nilai default lain + } + } + } + }); + + // isLoading + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timestamp) async { + if (isLoading.value == true) { + snackbarWidget( + context, + "Sedang Memuat Data", + snackbarType.warning, + Duration(days: 1), + ); + } + }); + return () {}; + }, [isLoading]); + + // check person id + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timestamp) async { + final personIDx = selectedPersonId; + if (personIDx == "0") { + snackbarWidget( + context, + 'Gagal mendapatkan data', + snackbarType.error, + Duration(seconds: 3), + ); + Navigator.of(context) + .pushNamedAndRemoveUntil(homeRoute, (route) => false); + return; + } else { + // listDataEdit + // set ke inputan + ref.read(eQrCode.notifier).state = + TextEditingController(text: listDataEdit.personQrCode); + + ref.read(eNikCtr.notifier).state = + TextEditingController(text: listDataEdit.personNIK); + + ref.read(eNamaCtr.notifier).state = + TextEditingController(text: listDataEdit.personName); + + ref.read(eDobCtr.notifier).state = TextEditingController( + text: formatDateJiffy(listDataEdit.personDob)); + + ref.read(eDobDt.notifier).state = + DateTime.parse(listDataEdit.personDob); + + final parsedDate = DateTime.parse(listDataEdit.personDob); + // print("Parsed Date: $parsedDate"); + + ref.read(eDobDt.notifier).state = parsedDate; + + // sex + ref.read(noLoginSexProvider.notifier).sex( + host: host, + client_id: client_id, + token: token, + ); + } + }); + return () {}; + }, [selectedPersonId]); + + // date picker + Future _selectDate(BuildContext context, WidgetRef ref) async { + DateTime? newSelectedDate = await showDatePicker( + initialEntryMode: DatePickerEntryMode.calendarOnly, + context: context, + initialDate: (listDataEdit.personDob != "0000-00-00") + ? DateTime.parse(listDataEdit.personDob) + : DateTime.now(), + firstDate: DateTime(1900), + lastDate: DateTime(2100), + ); + + if (newSelectedDate != null) { + ref.read(eDobDt.notifier).state = newSelectedDate; + ref.read(eDobCtr.notifier).state.text = + formatDateJiffy(newSelectedDate.toLocal().toString()); + } + } + + // refreshToken Provider + ref.listen(noLoginRefreshTokenProvider, (prev, next) async { + if (next is NoLoginRefreshTokenStateLoading) { + isLoading.value = true; + } else if (next is NoLoginRefreshTokenStateError) { + isLoading.value = false; + // errorMessage.value = next.message; + snackbarWidget( + context, + next.message, + snackbarType.error, + Duration(seconds: 3), + ); + } else if (next is NoLoginRefreshTokenStateDone) { + isLoading.value = false; + + // check proses aksi yg berlangsung + if (readProsesAksi == Constant.getRiwayat) { + ref.read(noLoginRiwayatScanProvider.notifier).noLoginRiwayatScan( + host: host, + client_id: client_id, + token: token, + expire_date: expire_date, + ); + } else if (readProsesAksi == Constant.postEditScan) { + ref.read(noLoginEditScanProvider.notifier).editScan( + token: token, + host: host, + client_id: client_id, + Person_ID: selectedPersonId, + Person_NIK: ref.read(eNikCtr).text, + Person_Name: ref.read(eNamaCtr).text, + Person_Dob: ref.read(eDobCtr).text, + Person_Sex: ref.read(eSexSelected).M_SexID, + ); + } + } + }); + + // void getRiwayat + void getRiwayat() async { + if (await isTokenExpired() == true) { + ref.read(noLoginprosesAksi.notifier).state = Constant.getRiwayat; + print("Token expired, refreshing..."); + ref.read(noLoginRefreshTokenProvider.notifier).refreshToken( + client_id: client_id, + host: host, + expire_date: expire_date, + ); + } else { + ref.read(noLoginRiwayatScanProvider.notifier).noLoginRiwayatScan( + host: host, + client_id: client_id, + token: token, + expire_date: expire_date, + ); + } + } + + // listRiwayProvider + ref.listen(noLoginRiwayatScanProvider, (prev, next) async { + if (next is NoLoginRiwayatScanStateLoading) { + isLoading.value = true; + } else if (next is NoLoginRiwayatScanStateError) { + isLoading.value = false; + // errorMessage.value = next.message; + snackbarWidget( + context, + next.message, + snackbarType.error, + Duration(seconds: 3), + ); + } else if (next is NoLoginRiwayatScanStateDone) { + // isLoading.value = false; + Navigator.of(context).pop(); + Navigator.of(context).pushNamedAndRemoveUntil( + noLoginHomeRoute, + (route) => false, + ); + } + }); + + // proses edit + ref.listen(noLoginEditScanProvider, (prev, next) { + if (next is NoLoginEditScanStateLoading) { + isLoading.value = true; + } else if (next is NoLoginEditScanStateError) { + isLoading.value = false; + // errorMessage.value = next.message; + snackbarWidget( + context, + next.message, + snackbarType.error, + Duration(seconds: 3), + ); + } else if (next is NoLoginEditScanStateDone) { + isLoading.value = false; + getRiwayat(); + } + }); + + void postEdit() async { + if (await isTokenExpired() == true) { + ref.read(noLoginprosesAksi.notifier).state = Constant.postEditScan; + print("Token expired, refreshing..."); + ref.read(noLoginRefreshTokenProvider.notifier).refreshToken( + client_id: client_id, + host: host, + expire_date: expire_date, + ); + } else { + ref.read(noLoginEditScanProvider.notifier).editScan( + token: token, + host: host, + client_id: client_id, + Person_ID: selectedPersonId, + Person_NIK: ref.read(eNikCtr).text, + Person_Name: ref.read(eNamaCtr).text, + Person_Dob: ref.read(eDobCtr).text, + Person_Sex: ref.read(eSexSelected).M_SexID, + ); + } + } + + return GestureDetector( + onTap: () { + FocusManager.instance.primaryFocus!.unfocus(); + }, + child: Scaffold( + resizeToAvoidBottomInset: true, + backgroundColor: Constant.bgGrey, + body: ListView( + // crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // atas + Image.asset( + 'images/vektoratas.png', + width: double.infinity, + fit: BoxFit.cover, + ), + SizedBox( + height: Constant.getActualYPhone( + context: context, + y: 34, + ), + ), + // image ktp + Padding( + padding: EdgeInsets.only( + left: Constant.getActualYPhone(context: context, y: 12), + right: Constant.getActualYPhone(context: context, y: 12), + ), + child: Image.network( + baseUrl + listDataEdit.personUrl, + fit: BoxFit.fitWidth, + ), + ), + SizedBox( + height: Constant.getActualYPhone( + context: context, + y: 32, + ), + ), + // inputan qrcode + Padding( + padding: EdgeInsets.only( + left: Constant.getActualYPhone(context: context, y: 12), + right: Constant.getActualYPhone(context: context, y: 12), + ), + child: Text( + 'QR Code', + style: Constant.title_400(context: context).copyWith( + color: Constant.inputanGrey, + ), + ), + ), + SizedBox( + height: Constant.getActualYPhone( + context: context, + y: 16, + ), + ), + Padding( + padding: EdgeInsets.only( + left: Constant.getActualYPhone(context: context, y: 12), + right: Constant.getActualYPhone(context: context, y: 12), + ), + child: TextField( + readOnly: true, + maxLines: 2, + controller: ref.read(eQrCode), + decoration: InputDecoration( + // hintText: "NIK", + enabledBorder: const OutlineInputBorder( + borderSide: BorderSide( + color: Colors.grey, + width: 1, + ), + ), + focusedBorder: const OutlineInputBorder( + borderSide: BorderSide( + color: Colors.blue, + width: 2, + ), + ), + ), + ), + ), + SizedBox( + height: Constant.getActualYPhone( + context: context, + y: 16, + ), + ), + // inputan nik + Padding( + padding: EdgeInsets.only( + left: Constant.getActualYPhone(context: context, y: 12), + right: Constant.getActualYPhone(context: context, y: 12), + ), + child: Text( + 'NIK', + style: Constant.title_400(context: context).copyWith( + color: Constant.inputanGrey, + ), + ), + ), + SizedBox( + height: Constant.getActualYPhone( + context: context, + y: 16, + ), + ), + Padding( + padding: EdgeInsets.only( + left: Constant.getActualYPhone(context: context, y: 12), + right: Constant.getActualYPhone(context: context, y: 12), + ), + child: TextField( + controller: ref.read(eNikCtr), + keyboardType: TextInputType.number, + decoration: InputDecoration( + // hintText: "NIK", + enabledBorder: const OutlineInputBorder( + borderSide: BorderSide( + color: Colors.grey, + width: 1, + ), + ), + focusedBorder: const OutlineInputBorder( + borderSide: BorderSide( + color: Colors.blue, + width: 2, + ), + ), + ), + ), + ), + SizedBox( + height: Constant.getActualYPhone( + context: context, + y: 16, + ), + ), + + // inputan nama + Padding( + padding: EdgeInsets.only( + left: Constant.getActualYPhone(context: context, y: 12), + right: Constant.getActualYPhone(context: context, y: 12), + ), + child: Text( + 'Nama', + style: Constant.title_400(context: context).copyWith( + color: Constant.inputanGrey, + ), + ), + ), + SizedBox( + height: Constant.getActualYPhone( + context: context, + y: 16, + ), + ), + Padding( + padding: EdgeInsets.only( + left: Constant.getActualYPhone(context: context, y: 12), + right: Constant.getActualYPhone(context: context, y: 12), + ), + child: TextField( + controller: ref.read(eNamaCtr), + decoration: InputDecoration( + // hintText: "NIK", + enabledBorder: const OutlineInputBorder( + borderSide: BorderSide( + color: Colors.grey, + width: 1, + ), + ), + focusedBorder: const OutlineInputBorder( + borderSide: BorderSide( + color: Colors.blue, + width: 2, + ), + ), + ), + ), + ), + SizedBox( + height: Constant.getActualYPhone( + context: context, + y: 16, + ), + ), + // inputan dob + Padding( + padding: EdgeInsets.only( + left: Constant.getActualYPhone(context: context, y: 12), + right: Constant.getActualYPhone(context: context, y: 12), + ), + child: Text( + 'DOB', + style: Constant.title_400(context: context).copyWith( + color: Constant.inputanGrey, + ), + ), + ), + SizedBox( + height: Constant.getActualYPhone( + context: context, + y: 16, + ), + ), + Padding( + padding: EdgeInsets.only( + left: Constant.getActualYPhone(context: context, y: 12), + right: Constant.getActualYPhone(context: context, y: 12), + ), + child: TextField( + readOnly: true, + controller: ref.read(eDobCtr), + onTap: () { + _selectDate(context, ref); + }, + decoration: InputDecoration( + // hintText: "NIK", + suffixIcon: IconButton( + icon: Icon(Icons.calendar_today, + color: Constant.textCardGrey), + onPressed: () { + _selectDate(context, ref); + }, + ), + enabledBorder: const OutlineInputBorder( + borderSide: BorderSide( + color: Colors.grey, + width: 1, + ), + ), + focusedBorder: const OutlineInputBorder( + borderSide: BorderSide( + color: Colors.blue, + width: 2, + ), + ), + ), + ), + ), + SizedBox( + height: Constant.getActualYPhone( + context: context, + y: 16, + ), + ), + // inputan jenis kelamin + Padding( + padding: EdgeInsets.only( + left: Constant.getActualYPhone(context: context, y: 12), + right: Constant.getActualYPhone(context: context, y: 12), + ), + child: Text( + 'Jenis Kelamin', + style: Constant.title_400(context: context).copyWith( + color: Constant.inputanGrey, + ), + ), + ), + SizedBox( + height: Constant.getActualYPhone( + context: context, + y: 16, + ), + ), + Padding( + padding: EdgeInsets.only( + left: Constant.getActualYPhone(context: context, y: 12), + right: Constant.getActualYPhone(context: context, y: 12), + ), + child: DropdownButtonHideUnderline( + child: DropdownButton2( + isExpanded: true, + value: (listSex.value.isNotEmpty) + ? listSex.value.firstWhere( + (sex) => + sex.M_SexID == + ref.read(eSexSelected.notifier).state.M_SexID, + orElse: () => listSex.value[0], + ) + : null, + items: listSex.value.map((NoLoginSexModel g) { + return DropdownMenuItem( + value: g, + child: Text( + g.m_sexname, + style: Constant.title_400(context: context).copyWith( + color: Colors.black, + fontWeight: FontWeight.normal, + ), + ), + ); + }).toList(), + onChanged: (NoLoginSexModel? value) { + if (value != null) { + ref.read(eSexSelected.notifier).state = value; + ref.read(eSexCtr.notifier).state = TextEditingController( + text: value.m_sexname, + ); + FocusScope.of(context).unfocus(); + } + }, + buttonStyleData: ButtonStyleData( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + border: Border.all(color: Colors.grey), + ), + padding: EdgeInsets.symmetric(horizontal: 12), + width: double.infinity, + ), + dropdownStyleData: DropdownStyleData( + maxHeight: 100, + width: MediaQuery.of(context).size.width - 20, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + color: Colors.white, + ), + offset: Offset(0, 2), + ), + menuItemStyleData: MenuItemStyleData( + padding: EdgeInsets.symmetric(horizontal: 12, vertical: 8), + ), + onMenuStateChange: (isOpen) { + if (!isOpen) { + ref.read(eSexCtr).clear(); + } + }, + ), + ), + ), + SizedBox( + height: Constant.getActualYPhone( + context: context, + y: 44, + ), + ), + // button save + Padding( + padding: EdgeInsets.only( + left: Constant.getActualXPhone( + context: context, + x: 16, + ), + right: Constant.getActualXPhone( + context: context, + x: 16, + ), + ), + child: SizedBox( + width: double.infinity, + height: Constant.getActualYPhone( + context: context, + y: 48, + ), + child: ElevatedButton( + onPressed: () { + postEdit(); + }, + style: ElevatedButton.styleFrom( + backgroundColor: Constant.bgButton, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + elevation: 8, + shadowColor: Constant.bgButton.withOpacity(0.24), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'SAVE', + style: + Constant.titleButton500(context: context).copyWith( + color: Constant.textWhite, + ), + ), + ], + ), + ), + ), + ), + SizedBox( + height: Constant.getActualYPhone( + context: context, + y: 70, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/screen/no-login-scan/no_login_scan_screen.dart b/lib/screen/no-login-scan/no_login_scan_screen.dart index b6f6be3..4f088ba 100644 --- a/lib/screen/no-login-scan/no_login_scan_screen.dart +++ b/lib/screen/no-login-scan/no_login_scan_screen.dart @@ -7,15 +7,17 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:image/image.dart' as img; import 'package:mobile_scanner/mobile_scanner.dart'; +import 'package:scanktpflutter/model/no-login/no_login_edit_person_ktp_model.dart'; +import 'package:scanktpflutter/model/no-login/no_login_sex_model.dart'; +import '../../app/app_extension.dart'; import '../../app/route.dart'; import '../../app/constant.dart'; -import '../../model/edit_person_model.dart'; -import '../../model/sex_model.dart'; -import '../../provider/current_user_provider.dart'; import '../../provider/no-login-scan/no_login_scan_provider.dart'; +import '../../provider/no-login/no_login_current_user_provider.dart'; import '../../widget/customsnackbarwidget.dart'; -import '../home/list_riwayat_scan_provider.dart'; +import '../no-login-home/no_login_riwayat_scan_provider.dart'; +import '../no-login/no_login_refresh_token_provider.dart'; import 'no_login_upload_scan_provider.dart'; class NoLoginScanScreen extends HookConsumerWidget { @@ -43,11 +45,40 @@ class NoLoginScanScreen extends HookConsumerWidget { final croppedImage = useState(null); final isLoading = useState(false); final isLoadingUpload = useState(false); - final currentUser = ref.watch(currentUserProvider); + + // auth + final currentUser = ref.watch(noLoginCurrentUserProvider); final host = currentUser?.host ?? ""; - final userId = currentUser?.model.userId ?? ""; + final client_id = currentUser?.client_id ?? ""; + final expire_date = currentUser?.expire_date ?? ""; + final token = currentUser?.token ?? ""; final selectedResolution = useState(ResolutionPreset.medium); + final readProsesAksi = ref.watch(noLoginprosesAksi); + final base64Post = useState(""); + + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + if (client_id == "0" || token == "") { + // not logged in and doesn't have token + Navigator.of(context) + .pushNamedAndRemoveUntil(noLoginRoute, (route) => false); + return; + } + + if (token != "") { + if (await isTokenExpired() == true) { + print("Token expired, refreshing..."); + ref.read(noLoginRefreshTokenProvider.notifier).refreshToken( + client_id: client_id, + host: host, + expire_date: expire_date, + ); + } + } + }); + return () {}; + }, [currentUser]); Future initializeCamera() async { try { @@ -85,13 +116,13 @@ class NoLoginScanScreen extends HookConsumerWidget { if (loadingScreen.value) { return Scaffold( appBar: AppBar( - title: (cameraOn.value == false) + title: (judulTombol.value == "SCAN") ? Text( - judulAppBar.value, + judulPosisiHp.value, style: Constant.titlePosisiHP(context: context), ) : Text( - judulPosisiHp.value, + judulAppBar.value, style: Constant.titlePosisiHP(context: context), ), automaticallyImplyLeading: false, @@ -204,6 +235,54 @@ class NoLoginScanScreen extends HookConsumerWidget { return () {}; }, []); + // void getRiwayat + void getRiwayat() async { + if (await isTokenExpired() == true) { + ref.read(noLoginprosesAksi.notifier).state = Constant.getRiwayat; + print("Token expired, refreshing..."); + ref.read(noLoginRefreshTokenProvider.notifier).refreshToken( + client_id: client_id, + host: host, + expire_date: expire_date, + ); + } else { + ref.read(noLoginRiwayatScanProvider.notifier).noLoginRiwayatScan( + host: host, + client_id: client_id, + token: token, + expire_date: expire_date, + ); + } + } + + // void prosesUpload + void prosesUpload() async { + print("Token expired, refreshing..."); + print("client_id : $client_id"); + print("host : $host"); + print("expire_date : $expire_date"); + print("token : $token"); + print("qrCodeStr.value : ${qrCodeStr.value}"); + + if (await isTokenExpired() == true) { + ref.read(noLoginprosesAksi.notifier).state = Constant.postUploadFoto; + print("Token expired, refreshing..."); + ref.read(noLoginRefreshTokenProvider.notifier).refreshToken( + client_id: client_id, + host: host, + expire_date: expire_date, + ); + } else { + ref.read(noLoginUploadScanProvider.notifier).uploadScan( + host: host, + base64File: base64Post.value, + client_id: client_id, + qr_code: qrCodeStr.value, + token: token, + ); + } + } + Future initializeCameraAfterChangeResolution() async { final cameras = await availableCameras(); cameraController.value = CameraController( @@ -290,11 +369,8 @@ class NoLoginScanScreen extends HookConsumerWidget { // post ke BE Uint8List finalBytes = await croppedImage.value!.readAsBytes(); String base64String = base64Encode(finalBytes); - ref.read(noLoginUploadScanProvider.notifier).uploadScan( - host: host, - base64File: base64String, - userId: userId, - ); + base64Post.value = base64String; + prosesUpload(); } catch (e) { print("Error capturing image: $e"); } finally { @@ -302,12 +378,12 @@ class NoLoginScanScreen extends HookConsumerWidget { } } - // listRiwayProvider - ref.listen(listRiwayatScanProvider, (prev, next) { - if (next is ListRiwayatScanStateLoading) { - // isLoading.value = true; - } else if (next is ListRiwayatScanStateError) { - // isLoading.value = false; + // refreshToken Provider + ref.listen(noLoginRefreshTokenProvider, (prev, next) async { + if (next is NoLoginRefreshTokenStateLoading) { + isLoading.value = true; + } else if (next is NoLoginRefreshTokenStateError) { + isLoading.value = false; // errorMessage.value = next.message; snackbarWidget( context, @@ -315,12 +391,47 @@ class NoLoginScanScreen extends HookConsumerWidget { snackbarType.error, Duration(seconds: 3), ); - } else if (next is ListRiwayatScanStateDone) { - // isLoading.value = false; + } else if (next is NoLoginRefreshTokenStateDone) { + isLoading.value = false; + // check proses aksi yg berlangsung + if (readProsesAksi == Constant.getRiwayat) { + ref.read(noLoginRiwayatScanProvider.notifier).noLoginRiwayatScan( + host: host, + client_id: client_id, + token: token, + expire_date: expire_date, + ); + } else if (readProsesAksi == Constant.postUploadFoto) { + ref.read(noLoginUploadScanProvider.notifier).uploadScan( + host: host, + base64File: base64Post.value, + client_id: client_id, + qr_code: qrCodeStr.value, + token: token, + ); + } + } + }); + + // listRiwayProvider + ref.listen(noLoginRiwayatScanProvider, (prev, next) async { + if (next is NoLoginRiwayatScanStateLoading) { + isLoading.value = true; + } else if (next is NoLoginRiwayatScanStateError) { + isLoading.value = false; + // errorMessage.value = next.message; + snackbarWidget( + context, + next.message, + snackbarType.error, + Duration(seconds: 3), + ); + } else if (next is NoLoginRiwayatScanStateDone) { + // isLoading.value = false; Navigator.of(context).pop(); Navigator.of(context).pushNamed( - editScanRoute, + noLoginEditScanRoute, ); } }); @@ -344,13 +455,13 @@ class NoLoginScanScreen extends HookConsumerWidget { ref.read(selectedPersonIdx.notifier).state = next.model[0].personID; // set SEX - ref.read(eSexSelected.notifier).state = SexModel( + ref.read(eSexSelected.notifier).state = NoLoginSexModel( M_SexID: next.model[0].personSex, M_SexCode: "", m_sexname: next.model[0].m_sexname, M_SexNameLang: ""); - ref.read(selectedEdit.notifier).state = EditPersonModel( + ref.read(selectedEdit.notifier).state = NoLoginEditPersonKtpModel( personID: next.model[0].personID, personNIK: next.model[0].personNIK, personName: next.model[0].personName, @@ -358,12 +469,9 @@ class NoLoginScanScreen extends HookConsumerWidget { personDob: next.model[0].personDob, personSex: next.model[0].personSex, personUrl: next.model[0].personUrl, + personQrCode: next.model[0].personQrCode, ); - - ref.read(listRiwayatScanProvider.notifier).listRiwayatScan( - host: host, - userId: userId, - ); + getRiwayat(); } }); @@ -384,7 +492,7 @@ class NoLoginScanScreen extends HookConsumerWidget { return Scaffold( appBar: AppBar( - title: (cameraOn.value == false) + title: (cameraOrScanner.value == "SCAN") ? Text( judulAppBar.value, style: Constant.titlePosisiHP(context: context), diff --git a/lib/screen/no-login-scan/no_login_scan_screen.txt b/lib/screen/no-login-scan/no_login_scan_screen.txt deleted file mode 100644 index c6ef998..0000000 --- a/lib/screen/no-login-scan/no_login_scan_screen.txt +++ /dev/null @@ -1,487 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; -import 'package:camera/camera.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:image/image.dart' as img; -import 'package:mobile_scanner/mobile_scanner.dart'; -import '../../app/route.dart'; - -import '../../app/constant.dart'; -import '../../model/edit_person_model.dart'; -import '../../model/sex_model.dart'; -import '../../provider/current_user_provider.dart'; -import '../../provider/no-login-scan/no_login_scan_provider.dart'; -import '../../widget/customsnackbarwidget.dart'; -import '../home/list_riwayat_scan_provider.dart'; -import 'no_login_upload_scan_provider.dart'; - -class NoLoginScanScreen extends HookConsumerWidget { - const NoLoginScanScreen({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [ - SystemUiOverlay.bottom, - ]); - - final judulAppBar = useState("Scan QRCode & Scan KTP"); - final qrCodeExists = useState(false); - final cameraOn = useState(false); - final qrCodeStr = useState("Scan QRCode untuk dapat merekam"); - final judulTombol = useState("SCAN QR CODE"); - final awalan = useState("Info :"); - final judulPosisiHp = useState("Posisi HP Landscape"); - - final cameraController = useState(null); - final initializeControllerFuture = useState?>(null); - final capturedImage = useState(null); - final croppedImage = useState(null); - final isLoading = useState(false); - final isLoadingUpload = useState(false); - final currentUser = ref.watch(currentUserProvider); - final host = currentUser?.host ?? ""; - final userId = currentUser?.model.userId ?? ""; - final selectedResolution = - useState(ResolutionPreset.medium); - - void handleBarcode(BarcodeCapture barcodes) { - if (barcodes.barcodes.isNotEmpty) { - final scannedBarcode = - barcodes.barcodes.firstOrNull?.displayValue ?? ""; - if (scannedBarcode.isNotEmpty) { - ref.read(barcodeX.notifier).state = barcodes.barcodes.first; - qrCodeStr.value = scannedBarcode; - awalan.value = "QrCode : "; - qrCodeExists.value = true; - cameraOn.value = true; - judulTombol.value = "FOTO"; - } - } - } - - useEffect(() { - Future initializeCamera() async { - final cameras = await availableCameras(); - cameraController.value = CameraController( - cameras[0], - // ResolutionPreset.max, - // ResolutionPreset.medium, - selectedResolution.value); - initializeControllerFuture.value = cameraController.value!.initialize(); - await initializeControllerFuture.value; - await cameraController.value!.setFlashMode(FlashMode.off); - } - - initializeCamera(); - return null; - }, []); - - Future initializeCameraAfterChangeResolution() async { - final cameras = await availableCameras(); - cameraController.value = CameraController( - cameras[0], - // ResolutionPreset.max, - // ResolutionPreset.medium, - selectedResolution.value); - initializeControllerFuture.value = cameraController.value!.initialize(); - await initializeControllerFuture.value; - await cameraController.value!.setFlashMode(FlashMode.off); - } - - Future rotateAndCropImage(File imageFile) async { - Uint8List imageBytes = await imageFile.readAsBytes(); - img.Image? original = img.decodeImage(imageBytes); - if (original == null) return imageFile; - - img.Image rotated = img.bakeOrientation(original); - - int width = rotated.width; - int height = rotated.height; - bool isPortrait = height > width; - - int cropWidth, cropHeight; - - if (isPortrait) { - cropHeight = (height * 0.7).toInt(); - cropWidth = (cropHeight ~/ 1.59).toInt(); - } else { - cropWidth = (width * 0.7).toInt(); - cropHeight = (cropWidth ~/ 1.59).toInt(); - } - - int left = ((width - cropWidth) ~/ 2).toInt(); - int top = ((height - cropHeight) ~/ 2).toInt(); - - img.Image cropped = img.copyCrop(rotated, - x: left, y: top, width: cropWidth, height: cropHeight); - - File croppedFile = File('${imageFile.path}_cropped.jpg'); - await croppedFile.writeAsBytes(img.encodeJpg(cropped)); - - return croppedFile; - } - - Future rotateImage(File imageFile) async { - Uint8List bytes = await imageFile.readAsBytes(); - img.Image? image = img.decodeImage(bytes); - - if (image == null) return imageFile; - - img.Image rotated = img.copyRotate(image, angle: -90); - - File rotatedFile = File('${imageFile.path}_rotated.jpg'); - await rotatedFile.writeAsBytes(img.encodeJpg(rotated)); - - return rotatedFile; - } - - Future captureAndCropImage() async { - try { - isLoading.value = true; - await initializeControllerFuture.value; - - // Ambil gambar dari kamera - final image = await cameraController.value!.takePicture(); - File cropped = await rotateAndCropImage(File(image.path)); - - // Rotate gambar setelah cropping - File rotatedImage = await rotateImage(cropped); - - // Convert to grayscale - Uint8List bytes = await rotatedImage.readAsBytes(); - img.Image? coloredImage = img.decodeImage(bytes); - if (coloredImage != null) { - img.Image grayscaleImage = img.grayscale(coloredImage); - rotatedImage.writeAsBytesSync(img.encodeJpg(grayscaleImage)); - } - - // Simpan hasil yang sudah di-crop dan di-rotate - capturedImage.value = image; - croppedImage.value = rotatedImage; - - // post ke BE - Uint8List finalBytes = await croppedImage.value!.readAsBytes(); - String base64String = base64Encode(finalBytes); - ref.read(noLoginUploadScanProvider.notifier).uploadScan( - host: host, - base64File: base64String, - userId: userId, - ); - } catch (e) { - print("Error capturing image: $e"); - } finally { - isLoading.value = false; - } - } - - // listRiwayProvider - ref.listen(listRiwayatScanProvider, (prev, next) { - if (next is ListRiwayatScanStateLoading) { - // isLoading.value = true; - } else if (next is ListRiwayatScanStateError) { - // isLoading.value = false; - // errorMessage.value = next.message; - snackbarWidget( - context, - next.message, - snackbarType.error, - Duration(seconds: 3), - ); - } else if (next is ListRiwayatScanStateDone) { - // isLoading.value = false; - - Navigator.of(context).pop(); - Navigator.of(context).pushNamed( - editScanRoute, - ); - } - }); - - // proses upload - ref.listen(noLoginUploadScanProvider, (prev, next) { - if (next is NoLoginUploadScanStateLoading) { - isLoadingUpload.value = true; - } else if (next is NoLoginUploadScanStateError) { - isLoadingUpload.value = false; - // errorMessage.value = next.message; - print("Err : ${next.message}"); - snackbarWidget( - context, - next.message, - snackbarType.error, - Duration(seconds: 3), - ); - } else if (next is NoLoginUploadScanStateDone) { - isLoadingUpload.value = false; - ref.read(selectedPersonIdx.notifier).state = next.model[0].personID; - - // set SEX - ref.read(eSexSelected.notifier).state = SexModel( - M_SexID: next.model[0].personSex, - M_SexCode: "", - m_sexname: next.model[0].m_sexname, - M_SexNameLang: ""); - - ref.read(selectedEdit.notifier).state = EditPersonModel( - personID: next.model[0].personID, - personNIK: next.model[0].personNIK, - personName: next.model[0].personName, - m_sexname: next.model[0].m_sexname, - personDob: next.model[0].personDob, - personSex: next.model[0].personSex, - personUrl: next.model[0].personUrl, - ); - - ref.read(listRiwayatScanProvider.notifier).listRiwayatScan( - host: host, - userId: userId, - ); - } - }); - - // loading proses upload useEffect - useEffect(() { - WidgetsBinding.instance.addPostFrameCallback((timestamp) { - if (isLoadingUpload.value == true) { - snackbarWidget( - context, - 'Sedang Upload Foto...', - snackbarType.warning, - Duration(seconds: 3), - ); - } - }); - return () {}; - }, [isLoadingUpload.value]); - - return Scaffold( - appBar: AppBar( - title: Text( - judulAppBar.value, - style: Constant.titlePosisiHP(context: context), - ), - automaticallyImplyLeading: false, - actions: [ - if (cameraOn.value == true) - DropdownButton( - value: selectedResolution.value, - onChanged: (ResolutionPreset? newValue) { - if (newValue != null) { - selectedResolution.value = newValue; - initializeCameraAfterChangeResolution(); - } - }, - items: ResolutionPreset.values.map((ResolutionPreset value) { - return DropdownMenuItem( - value: value, - child: Text(value.toString().split('.').last), - ); - }).toList(), - underline: SizedBox.shrink(), - ), - ], - ), - backgroundColor: Colors.black.withOpacity(0.5), - body: Stack( - children: [ - if (awalan.value == "Info :") - SizedBox( - width: double.infinity, - height: Constant.getActualYPhone( - context: context, - y: 450, - ), - child: MobileScanner( - onDetect: handleBarcode, - ), - ), - // Show Qr code Str - // Container( - // alignment: Alignment.center, - // padding: EdgeInsets.symmetric(vertical: 10), - // color: Constant.inputanGrey, - // child: Text( - // "${awalan.value} ${qrCodeStr.value}", - // style: TextStyle(color: Colors.white), - // ), - // ), - // jika camera scan ktp true show - if (cameraOn.value == true) ...[ - SizedBox( - height: Constant.getActualYPhone( - context: context, - y: 20, - ), - ), - // show notif posisi HP - Container( - alignment: Alignment.center, - padding: EdgeInsets.symmetric(vertical: 10), - color: Constant.textRed, - child: Text( - judulPosisiHp.value, - style: TextStyle(color: Colors.white), - ), - ), - SizedBox( - height: Constant.getActualYPhone( - context: context, - y: 20, - ), - ), - if (cameraController.value != null && croppedImage.value == null) - FutureBuilder( - future: initializeControllerFuture.value, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - return CameraPreview(cameraController.value!); - } else { - return const Center(child: CircularProgressIndicator()); - } - }, - ), - ], - - // jika sudah ada foto - if (croppedImage.value != null) ...[ - Container( - width: double.infinity, - height: 300, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - ), - child: ClipRRect( - borderRadius: BorderRadius.circular(12), - child: Image.file( - croppedImage.value!, - fit: BoxFit.contain, - ), - ), - ), - ] else ...[ - // bingkai foto - Positioned.fill( - child: CustomPaint( - painter: OverlayPainter(), - ), - ), - ], - // loading - if (isLoading.value) - Positioned.fill( - child: Container( - color: Colors.black.withOpacity(0.5), - child: const Center( - child: CircularProgressIndicator( - color: Colors.white, - ), - ), - ), - ), - ], - ), - bottomNavigationBar: BottomAppBar( - height: Constant.getActualYPhone( - context: context, - y: 100, - ), - shape: const CircularNotchedRectangle(), - child: Row( - children: [ - ElevatedButton.icon( - onPressed: () { - capturedImage.value = null; - croppedImage.value = null; - - ref.read(barcodeX.notifier).state = Barcode(); - qrCodeExists.value = false; - judulTombol.value = "SCAN QR CODE"; - qrCodeStr.value = "Scan QRCode untuk dapat merekam"; - cameraOn.value = false; - awalan.value = "Info :"; - Navigator.of(context).pop(); - }, - icon: Icon( - Icons.arrow_back, - size: 17, - color: Constant.textWhite, - ), - label: Text( - 'Kembali', - style: Constant.cardText(context: context).copyWith( - color: Constant.textWhite, - ), - ), - style: ElevatedButton.styleFrom( - backgroundColor: Constant.textCardGrey, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - elevation: 8, - shadowColor: Constant.bgButton.withOpacity(0.24), - ), - ), - const Spacer(), - // scan - ElevatedButton.icon( - onPressed: captureAndCropImage, - icon: Icon( - Icons.camera, - size: 17, - color: Constant.textWhite, - ), - label: Text( - judulTombol.value, - style: Constant.cardText(context: context).copyWith( - color: Constant.textWhite, - ), - ), - style: ElevatedButton.styleFrom( - backgroundColor: Constant.bgButton, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - elevation: 8, - shadowColor: Constant.bgButton.withOpacity(0.24), - ), - ), - ], - ), - ), - ); - } -} - -class OverlayPainter extends CustomPainter { - @override - void paint(Canvas canvas, Size size) { - final paint = Paint() - ..color = Colors.black.withOpacity(0.6) - ..style = PaintingStyle.fill; - - final borderPaint = Paint() - ..color = Colors.yellow - ..strokeWidth = 4 - ..style = PaintingStyle.stroke; - - double boxHeight = size.height * 0.7; - double boxWidth = boxHeight / 1.59; - - double left = (size.width - boxWidth) / 2; - double top = (size.height - boxHeight) / 2; - - Path path = Path() - ..addRect(Rect.fromLTWH(0, 0, size.width, size.height)) - ..addRect(Rect.fromLTWH(left, top, boxWidth, boxHeight)) - ..fillType = PathFillType.evenOdd; - - canvas.drawPath(path, paint); - canvas.drawRect(Rect.fromLTWH(left, top, boxWidth, boxHeight), borderPaint); - } - - @override - bool shouldRepaint(CustomPainter oldDelegate) => false; -} diff --git a/lib/screen/no-login-scan/no_login_sex_provider.dart b/lib/screen/no-login-scan/no_login_sex_provider.dart new file mode 100644 index 0000000..4b6a5d8 --- /dev/null +++ b/lib/screen/no-login-scan/no_login_sex_provider.dart @@ -0,0 +1,73 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:scanktpflutter/model/no-login/no_login_sex_model.dart'; +import 'package:scanktpflutter/repository/no-login-scan/no_login_scan_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +// 3. state provider +final noLoginSexProvider = + StateNotifierProvider( + (ref) => NoLoginSexNotifier(ref: ref)); + +// 2. notifier +class NoLoginSexNotifier extends StateNotifier { + final Ref ref; + NoLoginSexNotifier({required this.ref}) : super(NoLoginSexStateInit()); + void sex({ + required String host, + required String client_id, + required String token, + }) async { + try { + state = NoLoginSexStateLoading(); + final resp = await NoLoginScanRepository( + dio: ref.read(dioProvider), + ).sexRepo( + host: host, + client_id: client_id, + token: token, + ); + + // print(resp); + state = NoLoginSexStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = NoLoginSexStateError(message: e.message); + } else { + state = NoLoginSexStateError(message: e.toString()); + } + } + } +} + +// 1. state +abstract class NoLoginSexState extends Equatable { + final DateTime date; + const NoLoginSexState(this.date); + @override + List get props => [date]; +} + +class NoLoginSexStateInit extends NoLoginSexState { + NoLoginSexStateInit() : super(DateTime.now()); +} + +class NoLoginSexStateLoading extends NoLoginSexState { + NoLoginSexStateLoading() : super(DateTime.now()); +} + +class NoLoginSexStateError extends NoLoginSexState { + final String message; + NoLoginSexStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class NoLoginSexStateDone extends NoLoginSexState { + final List model; + NoLoginSexStateDone({ + required this.model, + }) : super(DateTime.now()); +} diff --git a/lib/screen/no-login-scan/no_login_upload_scan_provider.dart b/lib/screen/no-login-scan/no_login_upload_scan_provider.dart index 52cee02..f3fd6b8 100644 --- a/lib/screen/no-login-scan/no_login_upload_scan_provider.dart +++ b/lib/screen/no-login-scan/no_login_upload_scan_provider.dart @@ -1,23 +1,27 @@ import 'package:equatable/equatable.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../model/no-login/no_login_sukses_person_model.dart'; import '../../repository/no-login-scan/no_login_scan_repository.dart'; -import '../../model/sukses_person_model.dart'; import '../../provider/dio_provider.dart'; import '../../repository/base_repository.dart'; // 3. state provider -final noLoginUploadScanProvider = StateNotifierProvider( - (ref) => NoLoginUploadScanNotifier(ref: ref)); +final noLoginUploadScanProvider = + StateNotifierProvider( + (ref) => NoLoginUploadScanNotifier(ref: ref)); // 2. notifier class NoLoginUploadScanNotifier extends StateNotifier { final Ref ref; - NoLoginUploadScanNotifier({required this.ref}) : super(NoLoginUploadScanStateInit()); + NoLoginUploadScanNotifier({required this.ref}) + : super(NoLoginUploadScanStateInit()); void uploadScan({ required String host, - required String userId, + required String client_id, required String base64File, + required String qr_code, + required String token, }) async { try { state = NoLoginUploadScanStateLoading(); @@ -26,7 +30,9 @@ class NoLoginUploadScanNotifier extends StateNotifier { ).prosesScan( host: host, base64File: base64File, - userId: userId, + client_id: client_id, + qr_code: qr_code, + token: token, ); // print(resp); @@ -65,7 +71,7 @@ class NoLoginUploadScanStateError extends NoLoginUploadScanState { } class NoLoginUploadScanStateDone extends NoLoginUploadScanState { - final List model; + final List model; NoLoginUploadScanStateDone({ required this.model, }) : super(DateTime.now()); diff --git a/lib/screen/scan/edit_scan_screen.dart b/lib/screen/scan/edit_scan_screen.dart index 5450ea2..60e0edd 100644 --- a/lib/screen/scan/edit_scan_screen.dart +++ b/lib/screen/scan/edit_scan_screen.dart @@ -5,8 +5,8 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:scanktpflutter/screen/scan/edit_scan_provider.dart'; import '../../model/sex_model.dart'; +import '../../provider/scan_provider.dart'; import '../../screen/scan/sex_provider.dart'; -import '../../provider/no-login-scan/no_login_scan_provider.dart'; import '../../widget/customsnackbarwidget.dart'; import '../../app/app_extension.dart'; diff --git a/lib/screen/scan/scan_screen.dart b/lib/screen/scan/scan_screen.dart index aa3da87..77c5884 100644 --- a/lib/screen/scan/scan_screen.dart +++ b/lib/screen/scan/scan_screen.dart @@ -12,7 +12,7 @@ import '../../app/constant.dart'; import '../../model/edit_person_model.dart'; import '../../model/sex_model.dart'; import '../../provider/current_user_provider.dart'; -import '../../provider/no-login-scan/no_login_scan_provider.dart'; +import '../../provider/scan_provider.dart'; import '../../widget/customsnackbarwidget.dart'; import '../home/list_riwayat_scan_provider.dart'; import 'upload_scan_provider.dart';