From f03266674a6365c0685d4e546ab5d54b2c16b1b9 Mon Sep 17 00:00:00 2001 From: Stephen Date: Fri, 19 Jan 2024 14:44:20 +0700 Subject: [PATCH] [Change Pass] 1. add Change Password Fiture --- app_petty_cash/lib/app/route.dart | 15 + .../lib/repository/auth_repository.dart | 31 +- .../change_password_provider.dart | 79 ++++ .../change_password_screen.dart | 396 ++++++++++++++++++ app_petty_cash/lib/widget/custom_drawer.dart | 22 + 5 files changed, 522 insertions(+), 21 deletions(-) create mode 100644 app_petty_cash/lib/screen/change_pasword/change_password_provider.dart create mode 100644 app_petty_cash/lib/screen/change_pasword/change_password_screen.dart diff --git a/app_petty_cash/lib/app/route.dart b/app_petty_cash/lib/app/route.dart index 41863f5..26a1c90 100644 --- a/app_petty_cash/lib/app/route.dart +++ b/app_petty_cash/lib/app/route.dart @@ -1,3 +1,5 @@ +import 'package:app_petty_cash/screen/change_pasword/change_password_screen.dart'; + import '../screen/change_company/change_company.dart'; import 'package:app_petty_cash/screen/camera/coba_camera.dart'; import 'package:app_petty_cash/screen/camera/example.dart'; @@ -20,6 +22,7 @@ const transaksiRoute = "/transaksiRoute"; const userRoute = "/userRoute"; const reportRoute = "/reportRoute"; const changeCompanyRoute = "/changeCompanyRoute"; +const changePasswordRoute = "/changePasswordRoute"; const historyTransaksiRoute = "/historyTransaksiRoute"; const cameraExampleRoute = "/cameraExampleRoute"; const cobaCameraRoute = "/cobaCameraRoute"; @@ -119,6 +122,18 @@ class AppRoute { ); }); } + // transaksi screen + if (settings.name == changePasswordRoute) { + return MaterialPageRoute(builder: (context) { + return MediaQuery( + data: MediaQuery.of(context).copyWith( + textScaleFactor: 1.0, + padding: EdgeInsets.all(0), + ), + child: ChangePasswordScreen(), + ); + }); + } // test file picker screen if (settings.name == testFilePickerRoute) { diff --git a/app_petty_cash/lib/repository/auth_repository.dart b/app_petty_cash/lib/repository/auth_repository.dart index 44a7238..bb1540a 100644 --- a/app_petty_cash/lib/repository/auth_repository.dart +++ b/app_petty_cash/lib/repository/auth_repository.dart @@ -59,35 +59,24 @@ class AuthRepository extends BaseRepository { } } - Future change_password({ + Future changePassword({ + required String userID, required String token, - required String M_UserID, - required String username, - required String doctor_id, - required String new_password, - required String confirm_password, + required String oldPassword, + required String newPassword, + required String confirmPassword, }) async { final param = { "token": token, - "M_UserID": M_UserID, - "username": username, - "doctor_id": doctor_id, - "new_password": new_password, - "confirm_password": confirm_password + "M_UserID": userID, + "old_password": oldPassword, + "new_password": newPassword, + "confirm_password": confirmPassword - // "username": "alhadad", - // "doctor_id": "3101210841", - // "password": "riau123" }; final service = "${Constant.baseUrlDevone}auth/change_password"; - final resp = await post(param: param, service: service); - - // final result = AuthModel( - // token: resp["data"]["token"], - // model: AuthDoctorModel.fromJson(resp["data"]["user"]), - // ); - // return result; + final resp = await post(param: param, service: service, token: token); if (resp["status"] == "OK") { return resp['message']; diff --git a/app_petty_cash/lib/screen/change_pasword/change_password_provider.dart b/app_petty_cash/lib/screen/change_pasword/change_password_provider.dart new file mode 100644 index 0000000..20f3a8c --- /dev/null +++ b/app_petty_cash/lib/screen/change_pasword/change_password_provider.dart @@ -0,0 +1,79 @@ +import 'package:app_petty_cash/model/auth_model.dart'; +import 'package:app_petty_cash/model/company_model.dart'; +import 'package:app_petty_cash/repository/auth_repository.dart'; +import 'package:app_petty_cash/repository/company_repository.dart'; + +import '../../model/list_category_model.dart'; +import '../../repository/transaksi_repository.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../provider/dio_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class ChangePasswordState extends Equatable { + final DateTime date; + const ChangePasswordState(this.date); + @override + List get props => [date]; +} + +class ChangePasswordStateInit extends ChangePasswordState { + ChangePasswordStateInit() : super(DateTime.now()); +} + +class ChangePasswordStateLoading extends ChangePasswordState { + ChangePasswordStateLoading() : super(DateTime.now()); +} + +class ChangePasswordStateError extends ChangePasswordState { + final String message; + ChangePasswordStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class ChangePasswordStateDone extends ChangePasswordState { + final String model; + ChangePasswordStateDone({ + required this.model, + }) : super(DateTime.now()); +} + + +//notifier +class ChangePasswordNotifier extends StateNotifier { + final Ref ref; + ChangePasswordNotifier({ + required this.ref, + }) : super(ChangePasswordStateInit()); + + void postChangePassword({ + required String userID, + required String userToken, + required String oldPassword, + required String newPassword, + required String confirmPassword, + + }) async { + try { + state = ChangePasswordStateLoading(); + final dio = ref.read(dioProvider); + final resp = + await AuthRepository(dio: dio).changePassword(userID: userID,token: userToken, oldPassword: oldPassword, newPassword: newPassword, confirmPassword: confirmPassword); + state = ChangePasswordStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = ChangePasswordStateError(message: e.message.toString()); + } else { + state = ChangePasswordStateError(message: e.toString()); + } + } + } +} + +//provider +final ChangePasswordProvider = + StateNotifierProvider( + (ref) => ChangePasswordNotifier(ref: ref), +); \ No newline at end of file diff --git a/app_petty_cash/lib/screen/change_pasword/change_password_screen.dart b/app_petty_cash/lib/screen/change_pasword/change_password_screen.dart new file mode 100644 index 0000000..6a58a1d --- /dev/null +++ b/app_petty_cash/lib/screen/change_pasword/change_password_screen.dart @@ -0,0 +1,396 @@ +import 'dart:async'; + +import 'package:app_petty_cash/app/constant.dart'; +import 'package:app_petty_cash/provider/current_menu_provider.dart'; +import 'package:app_petty_cash/screen/change_pasword/change_password_provider.dart'; +import 'package:app_petty_cash/screen/login/logout_provider.dart'; +import 'package:app_petty_cash/widget/custom_drawer.dart'; +import 'package:app_petty_cash/widget/sankbar_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +import '../../app/route.dart'; +import '../../provider/current_user_provider.dart'; + +class ChangePasswordScreen extends HookConsumerWidget { + const ChangePasswordScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final selectedUser = ref.read(currentUserProvider); + //menampung user token jika ada + final userToken = ref.read(currentUserProvider)?.token ?? ""; + final userID = ref.read(currentUserProvider)?.model.M_UserID ?? "0"; + + final isLoading = useState(false); + final ctrlOldPassword = useTextEditingController(text: ""); + final ctrlNewPassword = useTextEditingController(text: ""); + final ctrlConfirmPassword = useTextEditingController(text: ""); + final loading = useState(false); + + // inisialisasi agar bisa cek apakah user sudah login sukses apa belum + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + final userID = ref.read(currentUserProvider)?.model.M_UserID ?? "0"; + if (userID == "0") { + //not login + Navigator.of(context) + .pushNamedAndRemoveUntil(loginRoute, (route) => true); + + // Navigator.popAndPushNamed(context, loginRoute); + return; + } + }); + return () {}; + }, []); + Logout() async { + final shared = await SharedPreferences.getInstance(); + final bearerString = shared.get(Constant.bearerName).toString(); + // print(bearerString); + if (bearerString.isNotEmpty) { + shared.remove(bearerString); + shared.clear(); + // Navigator.popAndPushNamed(context, loginRoute); + ref.read(currentUserProvider.notifier).state = null; + Navigator.of(context) + .pushNamedAndRemoveUntil(loginRoute, (route) => false); + } + } + + // read provider home + ref.listen( + ChangePasswordProvider, + (previous, next) { + if (next is ChangePasswordStateLoading) { + loading.value = true; + } else if (next is ChangePasswordStateError) { + loading.value = false; + SanckbarWidget(context, next.message, snackbarType.error); + } else if (next is ChangePasswordStateDone) { + loading.value = false; + SanckbarWidget( + context, + "Ubah Password Berhasil!\nSilahkan Login dengan password baru", + snackbarType.success, + ); + Logout(); + } + }, + ); + + return Padding( + padding: EdgeInsets.only( + top: Constant.getActualYPhone(context: context, y: 30)), + child: Scaffold( + appBar: AppBar( + // centerTitle: true, + title: Text( + 'Change Password', + style: TextStyle(color: Constant.textWhite), + ), + backgroundColor: Constant.pcBtnBackgroundColor, + iconTheme: IconThemeData( + color: Constant.textWhite, + ), + ), + drawer: CustomDrawer(), + body: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(20), + child: Container( + width: Constant.getActualXPhone(context: context, x: 390), + height: Constant.getActualYPhone(context: context, y: 844), + child: Column( + children: [ + //Old Password + // inputan email + Padding( + padding: EdgeInsets.only( + // top: Constant.getActualYPhone(context: context, y: 63), + left: Constant.getActualXPhone(context: context, x: 20), + right: Constant.getActualXPhone(context: context, x: 20), + ), + child: Align( + alignment: Alignment.centerLeft, + child: Text( + 'Old Password', + style: Constant.body1_400_dibulan(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.textBlack), + ), + ), + ), + SizedBox( + height: Constant.getActualYPhone(context: context, y: 4), + ), + Padding( + padding: EdgeInsets.only( + top: Constant.getActualYPhone(context: context, y: 5), + left: Constant.getActualXPhone(context: context, x: 20), + right: Constant.getActualXPhone(context: context, x: 20), + ), + child: TextField( + readOnly: loading.value, + controller: ctrlOldPassword, + obscureText: false, + decoration: InputDecoration( + hintStyle: + Constant.body2_400(context: context).copyWith( + color: Constant.textGreyv2, + ), + labelStyle: + Constant.body2_400(context: context).copyWith( + color: Constant.textGreyv2, + ), + border: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.orange, + width: 1, + ), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Constant.textGreyv2, + width: 1, + ), + ), + labelText: "Your Old Password", + hintText: 'Input Your Old Password', + ), + ), + ), + SizedBox( + height: Constant.getActualYPhone(context: context, y: 40), + ), + //New password + Padding( + padding: EdgeInsets.only( + // top: Constant.getActualYPhone(context: context, y: 63), + left: Constant.getActualXPhone(context: context, x: 20), + right: Constant.getActualXPhone(context: context, x: 20), + ), + child: Align( + alignment: Alignment.centerLeft, + child: Text( + 'New Password', + style: Constant.body1_400_dibulan(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.textBlack), + ), + ), + ), + SizedBox( + height: Constant.getActualYPhone(context: context, y: 4), + ), + Padding( + padding: EdgeInsets.only( + top: Constant.getActualYPhone(context: context, y: 5), + left: Constant.getActualXPhone(context: context, x: 20), + right: Constant.getActualXPhone(context: context, x: 20), + ), + child: TextField( + readOnly: loading.value, + controller: ctrlNewPassword, + obscureText: false, + decoration: InputDecoration( + hintStyle: + Constant.body2_400(context: context).copyWith( + color: Constant.textGreyv2, + ), + labelStyle: + Constant.body2_400(context: context).copyWith( + color: Constant.textGreyv2, + ), + border: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.orange, + width: 1, + ), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Constant.textGreyv2, + width: 1, + ), + ), + labelText: "Your New Password", + hintText: 'Input Your New Password', + ), + ), + ), + SizedBox( + height: Constant.getActualYPhone(context: context, y: 40), + ), + //Confirm Password + Padding( + padding: EdgeInsets.only( + // top: Constant.getActualYPhone(context: context, y: 63), + left: Constant.getActualXPhone(context: context, x: 20), + right: Constant.getActualXPhone(context: context, x: 20), + ), + child: Align( + alignment: Alignment.centerLeft, + child: Text( + 'Confirm Password', + style: Constant.body1_400_dibulan(context: context) + .copyWith( + fontWeight: FontWeight.w600, + color: Constant.textBlack), + ), + ), + ), + SizedBox( + height: Constant.getActualYPhone(context: context, y: 4), + ), + Padding( + padding: EdgeInsets.only( + top: Constant.getActualYPhone(context: context, y: 5), + left: Constant.getActualXPhone(context: context, x: 20), + right: Constant.getActualXPhone(context: context, x: 20), + ), + child: TextField( + readOnly: loading.value, + controller: ctrlConfirmPassword, + obscureText: false, + decoration: InputDecoration( + hintStyle: + Constant.body2_400(context: context).copyWith( + color: Constant.textGreyv2, + ), + labelStyle: + Constant.body2_400(context: context).copyWith( + color: Constant.textGreyv2, + ), + border: OutlineInputBorder( + borderSide: BorderSide( + color: Colors.orange, + width: 1, + ), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: Constant.textGreyv2, + width: 1, + ), + ), + labelText: "Your Confirm Password", + hintText: 'Input Your Confirm Password', + ), + ), + ), + + SizedBox( + height: Constant.getActualYPhone(context: context, y: 40), + ), + //Button Update Password + Padding( + padding: EdgeInsets.only( + left: Constant.getActualXPhone(context: context, x: 20), + right: Constant.getActualXPhone(context: context, x: 20), + ), + child: SizedBox( + width: Constant.getActualXPhone(context: context, x: 390), + height: Constant.getActualYPhone(context: context, y: 50), + child: ElevatedButton( + // onPressed: () { + // Navigator.of(context).pushNamed(homeRoute); + // }, + onPressed: (loading.value == true) + ? null + : () { + if (ctrlNewPassword.text != + ctrlConfirmPassword.text) { + SanckbarWidget( + context, + 'New Password dan dan confirm password tidak sama!', + snackbarType.warning); + return; + } + if (ctrlOldPassword.text == + ctrlConfirmPassword.text) { + SanckbarWidget( + context, + 'Password lama dan baru tidak boleh sama', + snackbarType.warning); + return; + } + if (ctrlOldPassword.text.isEmpty || + ctrlNewPassword.text.isEmpty || + ctrlConfirmPassword.text.isEmpty) { + isLoading.value = true; + SanckbarWidget(context, 'Inputan harus diisi', + snackbarType.warning); + } else { + ref + .read(ChangePasswordProvider.notifier) + .postChangePassword( + userToken: userToken, + userID: userID, + oldPassword: ctrlOldPassword.text, + newPassword: ctrlNewPassword.text, + confirmPassword: + ctrlConfirmPassword.text, + ); + } + }, + style: ButtonStyle( + backgroundColor: MaterialStateColor.resolveWith( + (st) => (loading.value == true) + ? Constant.textGrey + : Constant.pcBtnBackgroundColor), + shape: + MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide( + color: Constant.pcBtnBackgroundColor, + ), + ), + ), + shadowColor: + MaterialStateProperty.all(Color(0xffff48423d)), + elevation: MaterialStateProperty.all(4.0), + ), + + child: Stack( + children: [ + (isLoading.value) + ? SizedBox( + width: Constant.getActualXPhone( + context: context, x: 24), + height: Constant.getActualYPhone( + context: context, y: 32), + child: Center( + child: CircularProgressIndicator( + color: Colors.white, + ), + ), + ) + : Align( + alignment: Alignment.center, + child: Text( + 'Submit', + style: Constant.titleH3_700( + context: context) + .copyWith( + color: Constant.textLoginColor), + ), + ), + ], + ), + ), + ), + ), + ], + ), + ), + ), + ), + ), + ); + } +} diff --git a/app_petty_cash/lib/widget/custom_drawer.dart b/app_petty_cash/lib/widget/custom_drawer.dart index b689133..d9c591a 100644 --- a/app_petty_cash/lib/widget/custom_drawer.dart +++ b/app_petty_cash/lib/widget/custom_drawer.dart @@ -272,6 +272,28 @@ class CustomDrawer extends HookConsumerWidget { ref.read(currentPageProvider.state).update((state) => 4); Navigator.pushNamed(context, changeCompanyRoute); }, + ), + ListTile( + leading: Icon( + Icons.lock_reset, + color: (currentMenu == 5) + ? Constant.pcBtnBackgroundColor + : Constant.textGreyv2, + ), + title: Text( + 'Change Password', + style: TextStyle( + color: (currentMenu == 5) + ? Constant.pcBtnBackgroundColor + : Constant.textGreyv2, + ), + ), + onTap: () { + // Handle navigation to Transaksi screen + Navigator.pop(context); + ref.read(currentPageProvider.state).update((state) => 5); + Navigator.pushNamed(context, changePasswordRoute); + }, ), ListTile( leading: Icon(