diff --git a/lib/repository/scan_repository.dart b/lib/repository/scan_repository.dart index 53b0c6b..3b9cb52 100644 --- a/lib/repository/scan_repository.dart +++ b/lib/repository/scan_repository.dart @@ -13,9 +13,7 @@ class ScanRepository extends BaseRepository { }) async { // final service = "${Constant.baseUrl}xauth/login"; final service = "http://${host}/one-api/scan-ktp/Scanktp/listRiwayatScan"; - final resp = await post(param: { - "userId":userId - }, service: service); + final resp = await post(param: {"userId": userId}, service: service); final result = List.empty(growable: true); resp['data'].forEach((e) { @@ -62,4 +60,36 @@ class ScanRepository extends BaseRepository { return result; } + + // edit + Future prosesEdit({ + required String host, + required String userId, + required String Person_ID, + required String Person_NIK, + required String Person_Name, + required String Person_Dob, + required String Person_Sex, + }) async { + final service = "http://${host}/one-api/scan-ktp/Scanktp/proses_edit"; + final resp = await post( + param: { + "Person_ID": Person_ID, + "Person_NIK": Person_NIK, + "Person_Name": Person_Name, + "Person_Dob": Person_Dob, + "Person_Sex": Person_Sex, + "userId": userId, + }, + service: service, + ); + + if (resp['status'] == "OK") { + return "Sukses Update Data"; + } else { + resp['message']; + } + + return resp['message']; + } } diff --git a/lib/screen/home/card_riwayat_scan.dart b/lib/screen/home/card_riwayat_scan.dart index da32852..b6d1629 100644 --- a/lib/screen/home/card_riwayat_scan.dart +++ b/lib/screen/home/card_riwayat_scan.dart @@ -1,6 +1,7 @@ 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 '../../app/app_extension.dart'; import '../../app/route.dart'; @@ -49,6 +50,15 @@ class CardRiwayatScan extends HookConsumerWidget { // print('id : ${data.personID}'); ref.read(selectedPersonIdx.notifier).state = data.personID; + + // set SEX + ref.read(eSexSelected.notifier).state = SexModel( + M_SexID: data.personSex, + M_SexCode: "", + m_sexname: data.m_sexname, + M_SexNameLang: "" + ); + ref.read(selectedEdit.notifier).state = EditPersonModel( personID: data.personID, personNIK: data.personNIK, diff --git a/lib/screen/scan/edit_scan_provider.dart b/lib/screen/scan/edit_scan_provider.dart new file mode 100644 index 0000000..86bd4e8 --- /dev/null +++ b/lib/screen/scan/edit_scan_provider.dart @@ -0,0 +1,79 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../repository/scan_repository.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +// 3. state provider +final editScanProvider = StateNotifierProvider( + (ref) => EditScanNotifier(ref: ref)); + +// 2. notifier +class EditScanNotifier extends StateNotifier { + final Ref ref; + EditScanNotifier({required this.ref}) : super(EditScanStateInit()); + void editScan({ + required String host, + required String userId, + required String Person_ID, + required String Person_NIK, + required String Person_Name, + required String Person_Dob, + required String Person_Sex, + }) async { + try { + state = EditScanStateLoading(); + final resp = await ScanRepository( + dio: ref.read(dioProvider), + ).prosesEdit( + host: host, + Person_ID: Person_ID, + Person_NIK: Person_NIK, + Person_Name: Person_Name, + Person_Dob: Person_Dob, + Person_Sex: Person_Sex, + userId: userId, + ); + + // print(resp); + state = EditScanStateDone(pesan: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = EditScanStateError(message: e.message); + } else { + state = EditScanStateError(message: e.toString()); + } + } + } +} + +// 1. state +abstract class EditScanState extends Equatable { + final DateTime date; + const EditScanState(this.date); + @override + List get props => [date]; +} + +class EditScanStateInit extends EditScanState { + EditScanStateInit() : super(DateTime.now()); +} + +class EditScanStateLoading extends EditScanState { + EditScanStateLoading() : super(DateTime.now()); +} + +class EditScanStateError extends EditScanState { + final String message; + EditScanStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class EditScanStateDone extends EditScanState { + final String pesan; + EditScanStateDone({ + required this.pesan, + }) : super(DateTime.now()); +} diff --git a/lib/screen/scan/edit_scan_screen.dart b/lib/screen/scan/edit_scan_screen.dart index d03bfad..180175f 100644 --- a/lib/screen/scan/edit_scan_screen.dart +++ b/lib/screen/scan/edit_scan_screen.dart @@ -1,7 +1,9 @@ +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/screen/scan/edit_scan_provider.dart'; import '../../model/sex_model.dart'; import '../../screen/scan/sex_provider.dart'; import '../../provider/scan_provider.dart'; @@ -64,8 +66,27 @@ class EditScanScreen extends HookConsumerWidget { } else if (next is SexStateDone) { isLoading.value = false; listSex.value = next.model; - ref.read(eSexCtr.notifier).state = - TextEditingController(text: next.model[0].m_sexname); + // 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 + } + } } }); @@ -112,6 +133,12 @@ class EditScanScreen extends HookConsumerWidget { 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(sexProvider.notifier).sex( host: host, @@ -124,11 +151,12 @@ class EditScanScreen extends HookConsumerWidget { // date picker Future _selectDate(BuildContext context, WidgetRef ref) async { DateTime? newSelectedDate = await showDatePicker( + initialEntryMode: DatePickerEntryMode.calendarOnly, context: context, - initialDate: (listDataEdit.personDob == "0000-00-00") + initialDate: (listDataEdit.personDob != "0000-00-00") ? DateTime.parse(listDataEdit.personDob) : DateTime.now(), - firstDate: DateTime(2000), + firstDate: DateTime(1900), lastDate: DateTime(2100), ); @@ -139,6 +167,35 @@ class EditScanScreen extends HookConsumerWidget { } } + // proses edit + ref.listen(editScanProvider, (prev, next) { + if (next is EditScanStateLoading) { + isLoading.value = true; + } else if (next is EditScanStateError) { + isLoading.value = false; + // errorMessage.value = next.message; + snackbarWidget( + context, + next.message, + snackbarType.error, + Duration(seconds: 3), + ); + } else if (next is EditScanStateDone) { + isLoading.value = false; + snackbarWidget( + context, + next.pesan, + snackbarType.success, + Duration(seconds: 3), + ); + Navigator.of(context).pushNamedAndRemoveUntil( + homeRoute, + (route) => false, + ); + return; + } + }); + return GestureDetector( onTap: () { FocusManager.instance.primaryFocus!.unfocus(); @@ -204,6 +261,7 @@ class EditScanScreen extends HookConsumerWidget { ), child: TextField( controller: ref.read(eNikCtr), + keyboardType: TextInputType.number, decoration: InputDecoration( // hintText: "NIK", enabledBorder: const OutlineInputBorder( @@ -360,42 +418,63 @@ class EditScanScreen extends HookConsumerWidget { left: Constant.getActualYPhone(context: context, y: 12), right: Constant.getActualYPhone(context: context, y: 12), ), - child: DropdownMenu( - initialSelection: - (listSex.value.isNotEmpty) ? listSex.value[0] : null, - controller: ref.read(eSexCtr), - width: Constant.getActualXPhone( - context: context, x: double.infinity), - // hintText: "Jenis Kelamin", - requestFocusOnTap: true, - enableFilter: true, - // label: const Text('Jenis Kelamin'), - onSelected: (SexModel? sex) { - if (sex != null) { - ref.read(eSexSelected.notifier).state = sex; - FocusScope.of(context).unfocus(); - } - }, - dropdownMenuEntries: listSex.value - .map>((SexModel sex) { - return DropdownMenuEntry( - value: sex, - label: sex.m_sexname, - ); - }).toList(), - inputDecorationTheme: InputDecorationTheme( - enabledBorder: const OutlineInputBorder( - borderSide: BorderSide( - color: Colors.grey, - width: 1, + 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((SexModel 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: (SexModel? 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, ), - focusedBorder: const OutlineInputBorder( - borderSide: BorderSide( - color: Colors.blue, - width: 2, + 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(); + } + }, ), ), ), @@ -426,18 +505,25 @@ class EditScanScreen extends HookConsumerWidget { child: ElevatedButton( onPressed: () { var param = { - "Person_ID":selectedPersonId, - "Person_Name":ref.read(eNamaCtr).text, - "Person_Dob":ref.read(eDobCtr).text, - "Person_Sex":ref.read(eSexSelected).M_SexID, + "userId": userId, + "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, }; print(param); - // Navigator.of(context).pushNamedAndRemoveUntil( - // homeRoute, - // (route) => false, - // ); + ref.read(editScanProvider.notifier).editScan( + host: host, + userId: userId, + 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, + ); }, style: ElevatedButton.styleFrom( backgroundColor: Constant.bgButton, @@ -465,7 +551,7 @@ class EditScanScreen extends HookConsumerWidget { SizedBox( height: Constant.getActualYPhone( context: context, - y: 20, + y: 70, ), ), ], diff --git a/pubspec.lock b/pubspec.lock index f95fa41..988745e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -129,6 +129,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" + dropdown_button2: + dependency: "direct main" + description: + name: dropdown_button2 + sha256: b0fe8d49a030315e9eef6c7ac84ca964250155a6224d491c1365061bc974a9e1 + url: "https://pub.dev" + source: hosted + version: "2.3.9" equatable: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 229018b..4ac0901 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -48,6 +48,7 @@ dependencies: camera: ^0.10.6 image: ^4.1.3 permission_handler: ^11.3.0 + dropdown_button2: ^2.3.9 dev_dependencies: flutter_test: diff --git a/test/widget_test.dart b/test/widget_test.dart index 5a228aa..f849283 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -8,7 +8,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import '../../main.dart'; +import 'package:scanktpflutter/main.dart'; void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async {