diff --git a/lib/_core/utils/snackbar/snackbar_helper.dart b/lib/_core/utils/snackbar/snackbar_helper.dart index cbc8fc3..371e41c 100644 --- a/lib/_core/utils/snackbar/snackbar_helper.dart +++ b/lib/_core/utils/snackbar/snackbar_helper.dart @@ -10,6 +10,14 @@ class SnackbarHelper { message, backgroundColor: GreenColors.green500, colorText: Colors.white, + snackPosition: SnackPosition.TOP, + duration: Duration(seconds: 3), + margin: EdgeInsets.all(10), + borderRadius: 8, + isDismissible: true, + forwardAnimationCurve: Curves.easeOutBack, + reverseAnimationCurve: Curves.easeInBack, + animationDuration: Duration(milliseconds: 800), ); } @@ -19,6 +27,14 @@ class SnackbarHelper { message, backgroundColor: Colors.red, colorText: Colors.white, + snackPosition: SnackPosition.TOP, + duration: Duration(seconds: 3), + margin: EdgeInsets.all(10), + borderRadius: 8, + isDismissible: true, + forwardAnimationCurve: Curves.easeOutBack, + reverseAnimationCurve: Curves.easeInBack, + animationDuration: Duration(milliseconds: 800), ); } @@ -26,6 +42,14 @@ class SnackbarHelper { Get.snackbar( title, message, + snackPosition: SnackPosition.TOP, + duration: Duration(seconds: 3), + margin: EdgeInsets.all(10), + borderRadius: 8, + isDismissible: true, + forwardAnimationCurve: Curves.easeOutBack, + reverseAnimationCurve: Curves.easeInBack, + animationDuration: Duration(milliseconds: 800), ); } } diff --git a/lib/data/repositories/profil_repository_impl.dart b/lib/data/repositories/profil_repository_impl.dart index 31828bb..6113202 100644 --- a/lib/data/repositories/profil_repository_impl.dart +++ b/lib/data/repositories/profil_repository_impl.dart @@ -27,10 +27,7 @@ class ProfilRepositoryImpl implements ProfilRepository { try { QuerySnapshot querySnapshot = await _firestore.collection('users').doc(userId).collection('passenger').get(); return querySnapshot.docs - .map((doc) => PassengerModel.fromMap({ - 'id': doc.id, - ...doc.data() as Map - })) + .map((doc) => PassengerModel.fromMap({'id': doc.id, ...doc.data() as Map})) .toList(); } catch (e) { rethrow; @@ -50,6 +47,7 @@ class ProfilRepositoryImpl implements ProfilRepository { rethrow; } } + @override Future getUserById(String userId) async { try { @@ -62,7 +60,7 @@ class ProfilRepositoryImpl implements ProfilRepository { rethrow; } } - + @override Future updatePassenger({ required String userId, @@ -119,6 +117,52 @@ class ProfilRepositoryImpl implements ProfilRepository { await _firestore.collection('users').doc(user.uid).update({'phone': newPhone}); } + @override + Future changeNoId({ + required String oldPassword, + required String typeId, + required String noId, + }) async { + final user = FirebaseAuth.instance.currentUser; + if (user == null) { + throw Exception("User belum login"); + } + + final existingUserQuery = + await _firestore.collection('users').where('typeId', isEqualTo: typeId).where('noId', isEqualTo: noId).get(); + + if (existingUserQuery.docs.isNotEmpty) { + final isCurrentUser = existingUserQuery.docs.any((doc) => doc.id == user.uid); + if (!isCurrentUser) { + throw Exception("Nomor ID sudah digunakan oleh pengguna lain"); + } + } + + final allUsersQuery = await _firestore.collection('users').get(); + for (final userDoc in allUsersQuery.docs) { + final passengerQuery = await _firestore + .collection('users') + .doc(userDoc.id) + .collection('passenger') + .where('typeId', isEqualTo: typeId) + .where('noId', isEqualTo: noId) + .get(); + if (passengerQuery.docs.isNotEmpty) { + throw Exception("Nomor ID sudah digunakan oleh pengguna lain"); + } + } + + final cred = EmailAuthProvider.credential( + email: user.email!, + password: oldPassword, + ); + await user.reauthenticateWithCredential(cred); + await _firestore.collection('users').doc(user.uid).update({ + 'typeId': typeId, + 'noId': noId, + }); + } + // @override // Future changeEmail({ // required String oldPassword, diff --git a/lib/domain/bindings/profil_binding.dart b/lib/domain/bindings/profil_binding.dart index 5d896a2..e5647ba 100644 --- a/lib/domain/bindings/profil_binding.dart +++ b/lib/domain/bindings/profil_binding.dart @@ -15,10 +15,11 @@ class ProfilBinding extends Bindings { Get.lazyPut(() => GetUserByIdUseCase(Get.find())); Get.lazyPut(() => ChangePasswordUseCase(Get.find())); Get.lazyPut(() => ChangePhoneUseCase(Get.find())); + Get.lazyPut(() => ChangeNoIdUseCase(Get.find())); Get.lazyPut(() => DeletePassengerUseCase(Get.find())); Get.lazyPut(() => UpdatePassengerUseCase(Get.find())); // Get.lazyPut(() => ChangeEmailUseCase(Get.find())); - + Get.lazyPut( () => ProfilController( createPassengerUseCase: Get.find(), @@ -26,6 +27,7 @@ class ProfilBinding extends Bindings { getUserByIdUseCase: Get.find(), changePasswordUseCase: Get.find(), changePhoneUseCase: Get.find(), + changeNoIdUseCase: Get.find(), deletePassengerUseCase: Get.find(), updatePassengerUseCase: Get.find(), ), diff --git a/lib/domain/repositories/profil_repository.dart b/lib/domain/repositories/profil_repository.dart index 70ae166..4cb0683 100644 --- a/lib/domain/repositories/profil_repository.dart +++ b/lib/domain/repositories/profil_repository.dart @@ -25,12 +25,18 @@ abstract class ProfilRepository { required String oldPassword, required String newPassword, }); - + Future changePhone({ required String oldPassword, required String newPhone, }); + Future changeNoId({ + required String oldPassword, + required String typeId, + required String noId, + }); + // Future changeEmail({ // required String oldPassword, // required String newEmail, diff --git a/lib/domain/usecases/profil_usecase.dart b/lib/domain/usecases/profil_usecase.dart index 43df7ef..7b12609 100644 --- a/lib/domain/usecases/profil_usecase.dart +++ b/lib/domain/usecases/profil_usecase.dart @@ -64,6 +64,23 @@ class ChangePhoneUseCase { } } +class ChangeNoIdUseCase { + final ProfilRepository profilRepository; + ChangeNoIdUseCase(this.profilRepository); + + Future call({ + required String oldPassword, + required String typeId, + required String noId, + }) { + return profilRepository.changeNoId( + oldPassword: oldPassword, + typeId: typeId, + noId: noId, + ); + } +} + class DeletePassengerUseCase { final ProfilRepository profilRepository; DeletePassengerUseCase(this.profilRepository); diff --git a/lib/presentation/controllers/profil_controller.dart b/lib/presentation/controllers/profil_controller.dart index d24cc73..364678c 100644 --- a/lib/presentation/controllers/profil_controller.dart +++ b/lib/presentation/controllers/profil_controller.dart @@ -3,7 +3,6 @@ import 'package:e_porter/_core/service/preferences_service.dart'; import 'package:e_porter/_core/utils/snackbar/snackbar_helper.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:get/get.dart'; - import '../../domain/models/user_entity.dart'; import '../../domain/usecases/profil_usecase.dart'; @@ -13,6 +12,7 @@ class ProfilController extends GetxController { final GetUserByIdUseCase getUserByIdUseCase; final ChangePasswordUseCase changePasswordUseCase; final ChangePhoneUseCase changePhoneUseCase; + final ChangeNoIdUseCase changeNoIdUseCase; final DeletePassengerUseCase deletePassengerUseCase; final UpdatePassengerUseCase updatePassengerUseCase; // final ChangeEmailUseCase changeEmailUseCase; @@ -22,6 +22,7 @@ class ProfilController extends GetxController { var isLoading = false.obs; var isChangingPassword = false.obs; var isChangingPhone = false.obs; + var isChangingNoId = false.obs; var isDeletingPassenger = false.obs; var isUpdatingPassenger = false.obs; // var isChangingEmail = false.obs; @@ -32,6 +33,7 @@ class ProfilController extends GetxController { required this.getUserByIdUseCase, required this.changePasswordUseCase, required this.changePhoneUseCase, + required this.changeNoIdUseCase, required this.deletePassengerUseCase, required this.updatePassengerUseCase, // required this.changeEmailUseCase, @@ -206,6 +208,42 @@ class ProfilController extends GetxController { } } + Future changeNoId({ + required String oldPassword, + required String typeId, + required String noId, + }) async { + isChangingNoId.value = true; + try { + await changeNoIdUseCase( + oldPassword: oldPassword, + typeId: typeId, + noId: noId, + ); + + await _loadProfile(); + SnackbarHelper.showSuccess("Berhasil", "Nomor ID berhasil diperbarui."); + + return true; + } on FirebaseAuthException catch (e) { + if (e.code == 'wrong-password' || e.code == 'invalid-credential') { + SnackbarHelper.showError("Gagal", "Password lama salah."); + } else { + SnackbarHelper.showError("Gagal", e.message ?? "Terjadi kesalahan."); + } + return false; + } catch (e) { + if (e.toString().contains("Nomor ID sudah digunakan oleh pengguna lain")) { + SnackbarHelper.showError("Gagal", "Nomor ID sudah digunakan oleh pengguna lain."); + } else { + SnackbarHelper.showError("Gagal", "Terjadi kesalahan: $e"); + } + return false; + } finally { + isChangingNoId.value = false; + } + } + // Future changeEmail({ // required String oldPassword, // required String newEmail, diff --git a/lib/presentation/screens/profile/pages/change/change_no_id.dart b/lib/presentation/screens/profile/pages/change/change_no_id.dart index b4e1991..99e6803 100644 --- a/lib/presentation/screens/profile/pages/change/change_no_id.dart +++ b/lib/presentation/screens/profile/pages/change/change_no_id.dart @@ -1,4 +1,6 @@ import 'package:e_porter/_core/component/appbar/appbar_component.dart'; +import 'package:e_porter/_core/component/button/button_fill.dart'; +import 'package:e_porter/_core/component/card/custome_shadow_cotainner.dart'; import 'package:e_porter/_core/component/text/custom_text.dart'; import 'package:e_porter/_core/component/text_field/dropdown/dropdown_component.dart'; import 'package:e_porter/_core/component/text_field/text_input/text_field_component.dart'; @@ -7,6 +9,7 @@ import 'package:e_porter/_core/constants/typography.dart'; import 'package:e_porter/_core/validators/validators.dart'; import 'package:e_porter/presentation/screens/auth/component/Input_password.dart'; import 'package:e_porter/presentation/screens/profile/component/header_information.dart'; +import 'package:e_porter/presentation/controllers/profil_controller.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; @@ -23,6 +26,7 @@ class _ChangeNoIdState extends State { final _formKey = GlobalKey(); final _oldPassword = TextEditingController(); final _noIdController = TextEditingController(); + final ProfilController _profilController = Get.find(); String selectedTypeId = 'KTP'; @override @@ -109,31 +113,38 @@ class _ChangeNoIdState extends State { ), ), ), - // bottomNavigationBar: Obx( - // () { - // return CustomeShadowCotainner( - // child: _authController.isChangingPhone.value - // ? Center( - // child: CircularProgressIndicator(color: PrimaryColors.primary800), - // ) - // : ButtonFill( - // text: 'Simpan', - // textColor: Colors.white, - // onTap: () async { - // if (!_formKey.currentState!.validate()) return; - // final result = await _authController.changePhone( - // oldPassword: _oldPassword.text.trim(), - // newPhone: _phoneNumber.text.trim(), - // ); - // if (result) { - // _oldPassword.clear(); - // _phoneNumber.clear(); - // } - // }, - // ), - // ); - // }, - // ), + bottomNavigationBar: Obx(() { + return CustomeShadowCotainner( + child: _profilController.isChangingNoId.value + ? Center( + child: CircularProgressIndicator(color: PrimaryColors.primary800), + ) + : ButtonFill( + text: 'Simpan', + textColor: Colors.white, + onTap: () async { + if (!_formKey.currentState!.validate()) return; + final result = await _profilController.changeNoId( + oldPassword: _oldPassword.text.trim(), + noId: _noIdController.text.trim(), + typeId: selectedTypeId); + + if (result) { + _oldPassword.clear(); + _noIdController.clear(); + + await Future.delayed(Duration(milliseconds: 1500)); + + if (Get.isSnackbarOpen) { + Get.closeAllSnackbars(); + await Future.delayed(Duration(milliseconds: 300)); + } + Get.back(); + } + }, + ), + ); + }), ); } } diff --git a/lib/presentation/screens/profile/pages/change_number_screen.dart b/lib/presentation/screens/profile/pages/change_number_screen.dart index 2e7e822..1f1a367 100644 --- a/lib/presentation/screens/profile/pages/change_number_screen.dart +++ b/lib/presentation/screens/profile/pages/change_number_screen.dart @@ -20,7 +20,7 @@ class ChangeNumberScreen extends StatefulWidget { } class _ChangeNumberScreenState extends State { - final _authController = Get.find(); + final _profilController = Get.find(); final _formKey = GlobalKey(); final _oldPassword = TextEditingController(); final _phoneNumber = TextEditingController(); @@ -78,7 +78,7 @@ class _ChangeNumberScreenState extends State { bottomNavigationBar: Obx( () { return CustomeShadowCotainner( - child: _authController.isChangingPhone.value + child: _profilController.isChangingPhone.value ? Center( child: CircularProgressIndicator(color: PrimaryColors.primary800), ) @@ -87,7 +87,7 @@ class _ChangeNumberScreenState extends State { textColor: Colors.white, onTap: () async { if (!_formKey.currentState!.validate()) return; - final result = await _authController.changePhone( + final result = await _profilController.changePhone( oldPassword: _oldPassword.text.trim(), newPhone: _phoneNumber.text.trim(), );