From 52edb41fb2e0c21c0301a7b6a0237eea85737c63 Mon Sep 17 00:00:00 2001 From: orangdeso Date: Wed, 14 May 2025 13:42:51 +0700 Subject: [PATCH] Feat: update progress finishing features profile --- .dart_tool/package_config.json | 2 +- .../transaction_reassignment_service.dart | 2 +- .../repositories/profil_repository_impl.dart | 95 +++++++++++ .../statistic_repository_impl.dart | 89 ++++++++++ lib/domain/bindings/profil_binding.dart | 6 + lib/domain/bindings/statistic_binding.dart | 26 +++ .../repositories/profil_repository.dart | 16 ++ .../repositories/statistic_repository.dart | 21 +++ lib/domain/usecases/profil_usecase.dart | 45 ++++++ lib/domain/usecases/statistic_usecase.dart | 26 +++ .../controllers/profil_controller.dart | 153 +++++++++++++++++- .../controllers/statistic_controller.dart | 43 +++++ .../auth/pages/forget_password_screen.dart | 2 +- .../profile/component/header_information.dart | 2 +- .../pages/information_users_screen.dart | 65 +++++--- .../profile/pages/passenger_list_screen.dart | 97 +++++++++++ .../screens/profile/pages/profile_screen.dart | 8 +- .../screens/routes/app_rountes.dart | 22 +++ 18 files changed, 694 insertions(+), 26 deletions(-) create mode 100644 lib/data/repositories/statistic_repository_impl.dart create mode 100644 lib/domain/bindings/statistic_binding.dart create mode 100644 lib/domain/repositories/statistic_repository.dart create mode 100644 lib/domain/usecases/statistic_usecase.dart create mode 100644 lib/presentation/controllers/statistic_controller.dart create mode 100644 lib/presentation/screens/profile/pages/passenger_list_screen.dart diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json index 2cff169..3f4520a 100644 --- a/.dart_tool/package_config.json +++ b/.dart_tool/package_config.json @@ -680,7 +680,7 @@ "languageVersion": "3.4" } ], - "generated": "2025-05-12T05:36:15.239350Z", + "generated": "2025-05-14T06:38:43.763178Z", "generator": "pub", "generatorVersion": "3.5.0", "flutterRoot": "file:///D:/Flutter/flutter_sdk/flutter_3.24.0", diff --git a/lib/_core/service/transaction_reassignment_service.dart b/lib/_core/service/transaction_reassignment_service.dart index 8426d5e..cb81dc4 100644 --- a/lib/_core/service/transaction_reassignment_service.dart +++ b/lib/_core/service/transaction_reassignment_service.dart @@ -11,7 +11,7 @@ class TransactionReassignmentService { FirebaseFirestore? firestore, }) : _firestore = firestore ?? FirebaseFirestore.instance; - void startService({int intervalSeconds = 30}) { + void startService({int intervalSeconds = 60}) { log('[TransactionReassignmentService] Memulai service pengalihan transaksi dengan interval $intervalSeconds detik'); _timer?.cancel(); _timer = Timer.periodic(Duration(seconds: intervalSeconds), (_) { diff --git a/lib/data/repositories/profil_repository_impl.dart b/lib/data/repositories/profil_repository_impl.dart index 85cccca..dd08a96 100644 --- a/lib/data/repositories/profil_repository_impl.dart +++ b/lib/data/repositories/profil_repository_impl.dart @@ -1,5 +1,8 @@ +// ignore_for_file: deprecated_member_use + import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:e_porter/domain/repositories/profil_repository.dart'; +import 'package:firebase_auth/firebase_auth.dart'; import '../../_core/service/logger_service.dart'; import '../../domain/models/user_entity.dart'; @@ -42,4 +45,96 @@ class ProfilRepositoryImpl implements ProfilRepository { rethrow; } } + + @override + Future changePassword({ + required String oldPassword, + required String newPassword, + }) async { + final user = FirebaseAuth.instance.currentUser; + if (user == null) { + throw Exception("User belum login"); + } + + final cred = EmailAuthProvider.credential( + email: user.email!, + password: oldPassword, + ); + await user.reauthenticateWithCredential(cred); + await user.updatePassword(newPassword); + } + + @override + Future changePhone({ + required String oldPassword, + required String newPhone, + }) async { + final user = FirebaseAuth.instance.currentUser; + if (user == null) { + throw Exception("User belum login"); + } + + final cred = EmailAuthProvider.credential( + email: user.email!, + password: oldPassword, + ); + await user.reauthenticateWithCredential(cred); + await _firestore.collection('users').doc(user.uid).update({'phone': newPhone}); + } + + // @override + // Future changeEmail({ + // required String oldPassword, + // required String newEmail, + // }) async { + // final user = FirebaseAuth.instance.currentUser; + // if (user == null) throw Exception("User belum login"); + + // try { + // final cred = EmailAuthProvider.credential( + // email: user.email!, + // password: oldPassword, + // ); + // await user.reauthenticateWithCredential(cred); + // await user.updateEmail(newEmail); + // await FirebaseFirestore.instance.collection('users').doc(user.uid).update({'email': newEmail}); + + // await user.sendEmailVerification(); + + // log("Email berhasil diperbarui ke: $newEmail"); + // log("Email verifikasi telah dikirim"); + // } catch (e) { + // log("Error pada changeEmail: $e"); + // rethrow; + // } + // } + + // @override + // Future changeEmail({ + // required String oldPassword, + // required String newEmail, + // }) async { + // final user = FirebaseAuth.instance.currentUser; + // if (user == null) throw Exception("User belum login"); + + // // re-authenticate + // final cred = EmailAuthProvider.credential( + // email: user.email!, + // password: oldPassword, + // ); + // await user.reauthenticateWithCredential(cred); + + // // gunakan verifyBeforeUpdateEmail (ini kirim link + set newEmail di backend) + // await user.verifyBeforeUpdateEmail( + // newEmail, + // ActionCodeSettings( + // url: 'https://eporter.page.link/verifyEmail', + // handleCodeInApp: true, + // androidPackageName: 'com.example.e_porter', + // androidInstallApp: true, + // androidMinimumVersion: '1', + // dynamicLinkDomain: 'eporter.page.link', + // ), + // ); + // } } diff --git a/lib/data/repositories/statistic_repository_impl.dart b/lib/data/repositories/statistic_repository_impl.dart new file mode 100644 index 0000000..085df8e --- /dev/null +++ b/lib/data/repositories/statistic_repository_impl.dart @@ -0,0 +1,89 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:e_porter/domain/repositories/statistic_repository.dart'; + +class StatisticRepositoryImpl implements StatisticRepository { + final FirebaseFirestore _firestore; + StatisticRepositoryImpl(this._firestore); + + Timestamp _tsFromDate(DateTime d) => + Timestamp.fromDate(DateTime(d.year, d.month, d.day)); + Timestamp _tsToDate(DateTime d) => + Timestamp.fromDate(DateTime(d.year, d.month, d.day).add(const Duration(days: 1))); + + Query _baseQuery({ + required String porterId, + required String status, + required DateTime date, + }) { + return _firestore + .collection('porterTransactions') + .where('porterUserId', isEqualTo: porterId) + .where('status', isEqualTo: status) + .where('createdAt', isGreaterThanOrEqualTo: _tsFromDate(date)) + .where('createdAt', isLessThan: _tsToDate(date)); + } + + @override + Stream getIncomingOrdersCount({ + required String porterId, + required DateTime date, + }) { + return _baseQuery(porterId: porterId, status: 'pending', date: date) + .snapshots() + .map((snap) => snap.docs.length); + } + + @override + Stream getInProgressOrdersCount({ + required String porterId, + required DateTime date, + }) { + return _baseQuery(porterId: porterId, status: 'proses', date: date) + .snapshots() + .map((snap) => snap.docs.length); + } + + @override + Stream getCompletedOrdersCount({ + required String porterId, + required DateTime date, + }) { + return _baseQuery(porterId: porterId, status: 'selesai', date: date) + .snapshots() + .map((snap) => snap.docs.length); + } + + @override + Stream getRevenue({ + required String porterId, + required DateTime date, + }) { + + return _baseQuery(porterId: porterId, status: 'selesai', date: date) + .snapshots() + .asyncMap((snap) async { + double total = 0; + for (final doc in snap.docs) { + final data = doc.data(); + final ticketId = (data as Map?)?['ticketId'] ?? ''; + final transactionId = (data as Map?)?['transactionId'] ?? ''; + if (ticketId != null && transactionId != null) { + // join ke sub‐collection payments + final payDoc = await _firestore + .collection('tickets') + .doc(ticketId) + .collection('payments') + .doc(transactionId) + .get(); + if (payDoc.exists && payDoc.data()!.containsKey('porterServiceDetails')) { + final ps = payDoc['porterServiceDetails'] as Map; + final a = (ps['arrival']['price'] as num).toDouble(); + final b = (ps['departure']['price'] as num).toDouble(); + total += a + b; + } + } + } + return total; + }); + } +} diff --git a/lib/domain/bindings/profil_binding.dart b/lib/domain/bindings/profil_binding.dart index 8f961e8..e7333d5 100644 --- a/lib/domain/bindings/profil_binding.dart +++ b/lib/domain/bindings/profil_binding.dart @@ -13,12 +13,18 @@ class ProfilBinding extends Bindings { Get.lazyPut(() => CreatePassengerUseCase(Get.find())); Get.lazyPut(() => GetPassengerByIdUseCase(Get.find())); Get.lazyPut(() => GetUserByIdUseCase(Get.find())); + Get.lazyPut(() => ChangePasswordUseCase(Get.find())); + Get.lazyPut(() => ChangePhoneUseCase(Get.find())); + // Get.lazyPut(() => ChangeEmailUseCase(Get.find())); Get.lazyPut( () => ProfilController( createPassengerUseCase: Get.find(), getPassengerByIdUseCase: Get.find(), getUserByIdUseCase: Get.find(), + changePasswordUseCase: Get.find(), + changePhoneUseCase: Get.find(), + // changeEmailUseCase: Get.find(), ), ); } diff --git a/lib/domain/bindings/statistic_binding.dart b/lib/domain/bindings/statistic_binding.dart new file mode 100644 index 0000000..2ef4e63 --- /dev/null +++ b/lib/domain/bindings/statistic_binding.dart @@ -0,0 +1,26 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:e_porter/data/repositories/statistic_repository_impl.dart'; +import 'package:e_porter/domain/repositories/statistic_repository.dart'; +import 'package:e_porter/domain/usecases/statistic_usecase.dart'; +import 'package:e_porter/presentation/controllers/statistic_controller.dart'; +import 'package:firebase_auth/firebase_auth.dart'; +import 'package:get/get.dart'; + +class StatisticBinding extends Bindings { + @override + void dependencies() { + // Repository + Get.lazyPut( + () => StatisticRepositoryImpl(FirebaseFirestore.instance), + ); + + // UseCase + Get.lazyPut(() => StatisticUseCase(Get.find())); + + // Controller: di sini kita ambil current user ID langsung dari FirebaseAuth + Get.lazyPut(() => StatisticController( + useCase: Get.find(), + porterId: FirebaseAuth.instance.currentUser!.uid, + )); + } +} diff --git a/lib/domain/repositories/profil_repository.dart b/lib/domain/repositories/profil_repository.dart index 017e54f..e359a70 100644 --- a/lib/domain/repositories/profil_repository.dart +++ b/lib/domain/repositories/profil_repository.dart @@ -7,5 +7,21 @@ abstract class ProfilRepository { }); Future> getPassengerById(String userId); + Future getUserById(String userId); + + Future changePassword({ + required String oldPassword, + required String newPassword, + }); + + Future changePhone({ + required String oldPassword, + required String newPhone, + }); + + // Future changeEmail({ + // required String oldPassword, + // required String newEmail, + // }); } diff --git a/lib/domain/repositories/statistic_repository.dart b/lib/domain/repositories/statistic_repository.dart new file mode 100644 index 0000000..e9a707e --- /dev/null +++ b/lib/domain/repositories/statistic_repository.dart @@ -0,0 +1,21 @@ +abstract class StatisticRepository { + Stream getIncomingOrdersCount({ + required String porterId, + required DateTime date, + }); + + Stream getInProgressOrdersCount({ + required String porterId, + required DateTime date, + }); + + Stream getCompletedOrdersCount({ + required String porterId, + required DateTime date, + }); + + Stream getRevenue({ + required String porterId, + required DateTime date, + }); +} diff --git a/lib/domain/usecases/profil_usecase.dart b/lib/domain/usecases/profil_usecase.dart index 03448ed..4636296 100644 --- a/lib/domain/usecases/profil_usecase.dart +++ b/lib/domain/usecases/profil_usecase.dart @@ -33,3 +33,48 @@ class GetUserByIdUseCase { Future call(String userId) => profilRepository.getUserById(userId); } + +class ChangePasswordUseCase { + final ProfilRepository profilRepository; + ChangePasswordUseCase(this.profilRepository); + + Future call({ + required String oldPassword, + required String newPassword, + }) { + return profilRepository.changePassword( + oldPassword: oldPassword, + newPassword: newPassword, + ); + } +} + +class ChangePhoneUseCase { + final ProfilRepository profilRepository; + ChangePhoneUseCase(this.profilRepository); + + Future call({ + required String oldPassword, + required String newPhone, + }) { + return profilRepository.changePhone( + oldPassword: oldPassword, + newPhone: newPhone, + ); + } +} + +// class ChangeEmailUseCase { +// final ProfilRepository profilRepository; +// ChangeEmailUseCase(this.profilRepository); + +// Future call({ +// required String oldPassword, +// required String newEmail, +// }) { +// return profilRepository.changeEmail( +// oldPassword: oldPassword, +// newEmail: newEmail, +// ); +// } +// } diff --git a/lib/domain/usecases/statistic_usecase.dart b/lib/domain/usecases/statistic_usecase.dart new file mode 100644 index 0000000..d544a57 --- /dev/null +++ b/lib/domain/usecases/statistic_usecase.dart @@ -0,0 +1,26 @@ +import 'package:e_porter/domain/repositories/statistic_repository.dart'; + +class StatisticUseCase { + final StatisticRepository _repo; + StatisticUseCase(this._repo); + + Stream getIncomingOrders({ + required String porterId, + required DateTime date, + }) => _repo.getIncomingOrdersCount(porterId: porterId, date: date); + + Stream getInProgressOrders({ + required String porterId, + required DateTime date, + }) => _repo.getInProgressOrdersCount(porterId: porterId, date: date); + + Stream getCompletedOrders({ + required String porterId, + required DateTime date, + }) => _repo.getCompletedOrdersCount(porterId: porterId, date: date); + + Stream getRevenue({ + required String porterId, + required DateTime date, + }) => _repo.getRevenue(porterId: porterId, date: date); +} diff --git a/lib/presentation/controllers/profil_controller.dart b/lib/presentation/controllers/profil_controller.dart index 8553797..1d0d408 100644 --- a/lib/presentation/controllers/profil_controller.dart +++ b/lib/presentation/controllers/profil_controller.dart @@ -1,7 +1,7 @@ import 'dart:developer'; - import 'package:e_porter/_core/service/logger_service.dart'; 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'; @@ -12,15 +12,24 @@ class ProfilController extends GetxController { final CreatePassengerUseCase createPassengerUseCase; final GetPassengerByIdUseCase getPassengerByIdUseCase; final GetUserByIdUseCase getUserByIdUseCase; + final ChangePasswordUseCase changePasswordUseCase; + final ChangePhoneUseCase changePhoneUseCase; + // final ChangeEmailUseCase changeEmailUseCase; var passengerList = [].obs; var userData = Rxn(); var isLoading = false.obs; + var isChangingPassword = false.obs; + var isChangingPhone = false.obs; + // var isChangingEmail = false.obs; ProfilController({ required this.createPassengerUseCase, required this.getPassengerByIdUseCase, required this.getUserByIdUseCase, + required this.changePasswordUseCase, + required this.changePhoneUseCase, + // required this.changeEmailUseCase, }); @override @@ -29,6 +38,19 @@ class ProfilController extends GetxController { _loadProfile(); } + // Future reloadProfile() async { + // final user = FirebaseAuth.instance.currentUser; + // if (user == null) return; + // final uid = user.uid; + + // await FirebaseFirestore.instance.collection('users').doc(uid).update({'email': user.email}); + + // final fresh = await getUserByIdUseCase(uid); + // userData.value = fresh; + + // await PreferencesService.saveUserData(fresh); + // } + Future _loadProfile() async { isLoading.value = true; try { @@ -78,4 +100,133 @@ class ProfilController extends GetxController { logger.e("Error fetching passengers: $e"); } } + + Future changePassword({ + required String oldPassword, + required String newPassword, + }) async { + isChangingPassword.value = true; + try { + await changePasswordUseCase( + oldPassword: oldPassword, + newPassword: newPassword, + ); + SnackbarHelper.showSuccess("Berhasil", "Password berhasil diperbarui."); + return true; + } on FirebaseAuthException catch (e) { + if (e.code == 'wrong-password' || e.code == 'invalid-credential') { + SnackbarHelper.showError("Gagal", "Password lama salah."); + } else if (e.code == 'weak-password') { + SnackbarHelper.showError("Gagal", "Password baru terlalu lemah."); + } else { + SnackbarHelper.showError("Gagal", "Error: ${e.message}"); + } + return false; + } catch (e) { + SnackbarHelper.showError("Gagal", "Terjadi kesalahan."); + return false; + } finally { + isChangingPassword.value = false; + } + } + + Future changePhone({ + required String oldPassword, + required String newPhone, + }) async { + isChangingPhone.value = true; + try { + await changePhoneUseCase( + oldPassword: oldPassword, + newPhone: newPhone, + ); + await _loadProfile(); + SnackbarHelper.showSuccess("Berhasil", "Nomor telepon 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 (_) { + SnackbarHelper.showError("Gagal", "Terjadi kesalahan."); + return false; + } finally { + isChangingPhone.value = false; + } + } + + // Future changeEmail({ + // required String oldPassword, + // required String newEmail, + // }) async { + // isChangingEmail.value = true; + // try { + // await changeEmailUseCase( + // oldPassword: oldPassword, + // newEmail: newEmail, + // ); + + // await reloadProfile(); + + // SnackbarHelper.showSuccess("Berhasil", "Email berhasil diubah. Silakan cek $newEmail untuk verifikasi."); + // return true; + // } on FirebaseAuthException catch (e) { + // switch (e.code) { + // case 'wrong-password': + // case 'invalid-credential': + // SnackbarHelper.showError("Gagal", "Password lama salah."); + // break; + // case 'email-already-in-use': + // SnackbarHelper.showError("Gagal", "Email baru sudah digunakan."); + // break; + // case 'operation-not-allowed': + // SnackbarHelper.showError("Gagal", "Email/Password signin belum diaktifkan di Firebase Console."); + // break; + // default: + // SnackbarHelper.showError("Gagal", e.message ?? e.code); + // } + // return false; + // } finally { + // isChangingEmail.value = false; + // } + // } + + // Future changeEmail({ + // required String oldPassword, + // required String newEmail, + // }) async { + // isChangingEmail.value = true; + // try { + // await changeEmailUseCase(oldPassword: oldPassword, newEmail: newEmail); + // SnackbarHelper.showSuccess("Link Terkirim", "Silakan cek email $newEmail untuk verifikasi."); + // return true; + // } on FirebaseAuthException catch (e) { + // switch (e.code) { + // case 'wrong-password': + // case 'invalid-credential': + // SnackbarHelper.showError("Gagal", "Password lama salah."); + // break; + // case 'email-already-in-use': + // SnackbarHelper.showError("Gagal", "Email baru sudah digunakan."); + // break; + // case 'unauthorized-domain': + // SnackbarHelper.showError( + // "Gagal", + // "Domain verifikasi belum diizinkan. " + // "Tambahkan \"eporter.page.link\" di Firebase Console → Authentication → Settings → Authorized domains."); + // break; + // default: + // SnackbarHelper.showError("Gagal", e.message ?? e.code); + // } + // return false; + // } catch (_) { + // SnackbarHelper.showError("Gagal", "Terjadi kesalahan."); + // return false; + // } finally { + // isChangingEmail.value = false; + // } + // } } diff --git a/lib/presentation/controllers/statistic_controller.dart b/lib/presentation/controllers/statistic_controller.dart new file mode 100644 index 0000000..4f37cfb --- /dev/null +++ b/lib/presentation/controllers/statistic_controller.dart @@ -0,0 +1,43 @@ +import 'package:e_porter/domain/usecases/statistic_usecase.dart'; +import 'package:get/get.dart'; + +class StatisticController extends GetxController { + final StatisticUseCase useCase; + final String porterId; + + final date = DateTime.now().obs; + + final incoming = 0.obs; + final inProgress = 0.obs; + final completed = 0.obs; + final revenue = 0.0.obs; + + StatisticController({ + required this.useCase, + required this.porterId, + }); + + @override + void onInit() { + super.onInit(); + _bindAllStreams(); + ever(date, (_) => _bindAllStreams()); + } + + void _bindAllStreams() { + incoming.bindStream( + useCase.getIncomingOrders(porterId: porterId, date: date.value), + ); + inProgress.bindStream( + useCase.getInProgressOrders(porterId: porterId, date: date.value), + ); + completed.bindStream( + useCase.getCompletedOrders(porterId: porterId, date: date.value), + ); + revenue.bindStream( + useCase.getRevenue(porterId: porterId, date: date.value), + ); + } + + void changeDate(DateTime newDate) => date.value = newDate; +} diff --git a/lib/presentation/screens/auth/pages/forget_password_screen.dart b/lib/presentation/screens/auth/pages/forget_password_screen.dart index 1d17c29..331766a 100644 --- a/lib/presentation/screens/auth/pages/forget_password_screen.dart +++ b/lib/presentation/screens/auth/pages/forget_password_screen.dart @@ -74,7 +74,7 @@ class _ForgetPasswordScreenState extends State { mainAxisSize: MainAxisSize.min, children: [ Obx(() { - if (!authController.isLoading.value) { + if (authController.isLoading.value) { return SizedBox( width: double.infinity, height: 48.h, diff --git a/lib/presentation/screens/profile/component/header_information.dart b/lib/presentation/screens/profile/component/header_information.dart index 8a79d9e..f53f95e 100644 --- a/lib/presentation/screens/profile/component/header_information.dart +++ b/lib/presentation/screens/profile/component/header_information.dart @@ -26,7 +26,7 @@ class HeaderInformation extends StatelessWidget { child: TypographyStyles.caption( title, color: GrayColors.gray500, - maxlines: 5, + maxlines: 6, fontWeight: FontWeight.w400, ), ), diff --git a/lib/presentation/screens/profile/pages/information_users_screen.dart b/lib/presentation/screens/profile/pages/information_users_screen.dart index a5b14c1..a313bd2 100644 --- a/lib/presentation/screens/profile/pages/information_users_screen.dart +++ b/lib/presentation/screens/profile/pages/information_users_screen.dart @@ -6,9 +6,11 @@ import 'package:e_porter/_core/constants/typography.dart'; import 'package:e_porter/domain/models/user_entity.dart'; import 'package:e_porter/presentation/controllers/profil_controller.dart'; import 'package:e_porter/presentation/screens/profile/component/header_information.dart'; +import 'package:e_porter/presentation/screens/routes/app_rountes.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; +import 'package:zoom_tap_animation/zoom_tap_animation.dart'; class InformationUsersScreen extends StatefulWidget { const InformationUsersScreen({super.key}); @@ -73,9 +75,18 @@ class _InformationUsersScreenState extends State { child: CustomeShadowCotainner( child: Column( children: [ - itemWithButton(label: 'Nomor Telepon', value: userData.phone ?? '-'), + itemWithButton( + label: 'Email', + value: userData.email ?? '-', + ), SizedBox(height: 24.h), - itemWithButton(label: 'Email', value: userData.email ?? '-'), + itemWithButton( + label: 'Nomor Telepon', + value: userData.phone ?? '-', + onTap: () { + Get.toNamed(Routes.CHANGENUMBER); + }, + ), SizedBox(height: 24.h), itemDoubleWithButton( label1: 'Tipe ID', value1: userData.tipeId ?? '-', label2: 'No ID', value2: userData.noId ?? '-'), @@ -92,7 +103,12 @@ class _InformationUsersScreenState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - itemWithButton(label: 'Nama Lengkap', value: userData.name ?? '-', isDivider: false), + itemWithButton( + label: 'Nama Lengkap', + value: userData.name ?? '-', + isDivider: false, + onTap: () {}, + ), SizedBox(height: 24.h), Row( children: [ @@ -113,7 +129,12 @@ class _InformationUsersScreenState extends State { ); } - Widget itemWithButton({required String label, required String value, bool isDivider = true}) { + Widget itemWithButton({ + required String label, + required String value, + bool isDivider = true, + VoidCallback? onTap, + }) { return Column( children: [ Row( @@ -136,22 +157,28 @@ class _InformationUsersScreenState extends State { ], ), ), - Container( - padding: EdgeInsets.symmetric(vertical: 6.h, horizontal: 10.w), - decoration: BoxDecoration( - color: PrimaryColors.primary100, - borderRadius: BorderRadius.circular(10.r), - ), - child: Row( - children: [ - Icon(Icons.edit, color: PrimaryColors.primary800, size: 14.sp), - Padding( - padding: EdgeInsets.only(left: 8.w), - child: TypographyStyles.small('Ubah', color: PrimaryColors.primary800), + if (onTap != null) + Container( + padding: EdgeInsets.symmetric(vertical: 6.h, horizontal: 10.w), + decoration: BoxDecoration( + color: PrimaryColors.primary100, + borderRadius: BorderRadius.circular(10.r), + ), + child: ZoomTapAnimation( + child: GestureDetector( + onTap: onTap, + child: Row( + children: [ + Icon(Icons.edit, color: PrimaryColors.primary800, size: 14.sp), + Padding( + padding: EdgeInsets.only(left: 8.w), + child: TypographyStyles.small('Ubah', color: PrimaryColors.primary800), + ), + ], + ), ), - ], - ), - ) + ), + ) ], ), if (isDivider) diff --git a/lib/presentation/screens/profile/pages/passenger_list_screen.dart b/lib/presentation/screens/profile/pages/passenger_list_screen.dart new file mode 100644 index 0000000..68751b3 --- /dev/null +++ b/lib/presentation/screens/profile/pages/passenger_list_screen.dart @@ -0,0 +1,97 @@ +// ignore_for_file: deprecated_member_use +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/constants/colors.dart'; +import 'package:e_porter/_core/constants/typography.dart'; +import 'package:e_porter/presentation/screens/routes/app_rountes.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:get/get.dart'; +import 'package:zoom_tap_animation/zoom_tap_animation.dart'; + +class PassengerListScreen extends StatefulWidget { + const PassengerListScreen({super.key}); + + @override + State createState() => _PassengerListScreenState(); +} + +class _PassengerListScreenState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: GrayColors.gray50, + appBar: DefaultAppbarComponent( + title: 'Daftar Penumpang', + textColor: Colors.white, + backgroundColors: PrimaryColors.primary800, + onTab: () { + Get.back(); + }, + ), + body: SafeArea( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 20.h), + child: ListView.builder( + itemCount: 2, + itemBuilder: (context, index) { + return Padding( + padding: EdgeInsets.only(bottom: 16.h), + child: _buildCardItem( + value: 'Name', + onTap: () { + // Get.toNamed(Routes.passengerDetail); + }, + ), + ); + }, + )), + ), + bottomNavigationBar: CustomeShadowCotainner( + child: ButtonFill( + text: 'Tambah Penumpang', + textColor: Colors.white, + onTap: () { + Get.toNamed(Routes.ADDPASSENGER); + }, + ), + ), + ); + } + + Widget _buildCardItem({required String value, required VoidCallback onTap}) { + return ZoomTapAnimation( + child: GestureDetector( + onTap: onTap, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(10.r), + border: Border.all(color: GrayColors.gray200), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TypographyStyles.caption('Dewasa', fontWeight: FontWeight.w400, color: GrayColors.gray500), + TypographyStyles.body(value, color: GrayColors.gray800) + ], + ), + SvgPicture.asset( + 'assets/icons/ic_more_than.svg', + color: GrayColors.gray500, + width: 20.w, + height: 20.h, + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/presentation/screens/profile/pages/profile_screen.dart b/lib/presentation/screens/profile/pages/profile_screen.dart index 8d3e729..295a3d6 100644 --- a/lib/presentation/screens/profile/pages/profile_screen.dart +++ b/lib/presentation/screens/profile/pages/profile_screen.dart @@ -107,7 +107,9 @@ class _ProfileScreenState extends State { ProfileMenu( label: 'Tambah Penumpang', svgIcon: 'assets/icons/ic_add_user_female.svg', - onTap: () {}, + onTap: () { + Get.toNamed(Routes.PASSENGERLIST); + }, ), Padding( padding: EdgeInsets.symmetric(vertical: 20.h), @@ -234,7 +236,9 @@ class _ProfileScreenState extends State { ProfileMenu( label: 'Ganti Kata Sandi', svgIcon: 'assets/icons/ic_lock.svg', - onTap: () {}, + onTap: () { + Get.toNamed(Routes.CHANGEPASSWORD); + }, ), Padding( padding: EdgeInsets.symmetric(vertical: 20.h), diff --git a/lib/presentation/screens/routes/app_rountes.dart b/lib/presentation/screens/routes/app_rountes.dart index 4622177..12a33a7 100644 --- a/lib/presentation/screens/routes/app_rountes.dart +++ b/lib/presentation/screens/routes/app_rountes.dart @@ -5,6 +5,7 @@ import 'package:e_porter/domain/bindings/porter_queue_binding.dart'; import 'package:e_porter/domain/bindings/porter_service_binding.dart'; import 'package:e_porter/domain/bindings/profil_binding.dart'; import 'package:e_porter/domain/bindings/search_flight_binding.dart'; +import 'package:e_porter/domain/bindings/statistic_binding.dart'; import 'package:e_porter/domain/bindings/ticket_binding.dart'; import 'package:e_porter/domain/bindings/transaction_binding.dart'; import 'package:e_porter/domain/bindings/transaction_porter_binding.dart'; @@ -34,8 +35,11 @@ import 'package:e_porter/presentation/screens/home/pages/ticket_booking_step4_sc import 'package:e_porter/presentation/screens/home/pages/upload_file_screen.dart'; import 'package:e_porter/presentation/screens/navigation/main_navigation.dart'; import 'package:e_porter/presentation/screens/onboarding/onboarding_screen.dart'; +import 'package:e_porter/presentation/screens/profile/pages/change_email_screen.dart'; +import 'package:e_porter/presentation/screens/profile/pages/change_number_screen.dart'; import 'package:e_porter/presentation/screens/profile/pages/change_password_screen.dart'; import 'package:e_porter/presentation/screens/profile/pages/information_users_screen.dart'; +import 'package:e_porter/presentation/screens/profile/pages/passenger_list_screen.dart'; import 'package:e_porter/presentation/screens/profile/pages/profile_screen.dart'; import 'package:e_porter/presentation/screens/profile/pages/add_passenger_screen.dart'; import 'package:e_porter/presentation/screens/splash/splash_screen.dart'; @@ -53,6 +57,7 @@ class AppRoutes { HistoryBinding(), PorterQueueBinding(), TransactionPorterBinding(), + StatisticBinding() ], ), GetPage( @@ -203,6 +208,20 @@ class AppRoutes { GetPage( name: Routes.CHANGEPASSWORD, page: () => ChangePasswordScreen(), + binding: ProfilBinding(), + ), + GetPage( + name: Routes.CHANGENUMBER, + page: () => ChangeNumberScreen(), + ), + GetPage( + name: Routes.CHANGEEMAIL, + page: () => ChangeEmailScreen(), + binding: ProfilBinding(), + ), + GetPage( + name: Routes.PASSENGERLIST, + page: () => PassengerListScreen(), ), ]; } @@ -249,4 +268,7 @@ class Routes { static const ADDPASSENGER = '/add_passenger'; static const INFORMATIONS = '/informations'; static const CHANGEPASSWORD = '/change_password'; + static const CHANGENUMBER = '/change_number'; + static const CHANGEEMAIL = '/change_email'; + static const PASSENGERLIST = '/passenger_list'; }