From f620157de883c3263ef21591c035f956a3f56e33 Mon Sep 17 00:00:00 2001 From: pahmiudahgede Date: Sat, 3 May 2025 03:30:18 +0700 Subject: [PATCH] refact: device_id for manage access, and bug: userpin runned unsucces --- lib/core/getinfodevice.dart | 23 +++++ lib/core/router.dart | 8 +- lib/main.dart | 20 +++-- lib/model/auth_model.dart | 7 +- lib/model/userpin_model.dart | 29 +++--- lib/screen/auth/inputpin_screen.dart | 89 +++++++++++++++++++ lib/screen/auth/otp_screen.dart | 17 +++- lib/screen/auth/verifpin_screen.dart | 80 +++++++++++++++++ lib/viewmodel/auth_vmod.dart | 11 ++- lib/viewmodel/userpin_vmod.dart | 30 +++++++ macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 48 ++++++++++ pubspec.yaml | 2 + 13 files changed, 340 insertions(+), 26 deletions(-) create mode 100644 lib/core/getinfodevice.dart create mode 100644 lib/screen/auth/verifpin_screen.dart create mode 100644 lib/viewmodel/userpin_vmod.dart diff --git a/lib/core/getinfodevice.dart b/lib/core/getinfodevice.dart new file mode 100644 index 0000000..714ee13 --- /dev/null +++ b/lib/core/getinfodevice.dart @@ -0,0 +1,23 @@ +import 'package:device_info_plus/device_info_plus.dart'; +import 'package:uuid/uuid.dart'; +import 'dart:io'; + +Future getDeviceId() async { + final deviceInfo = DeviceInfoPlugin(); + String deviceID; + + if (Platform.isAndroid) { + var androidInfo = await deviceInfo.androidInfo; + + deviceID = androidInfo.id; + } else if (Platform.isIOS) { + var iosInfo = await deviceInfo.iosInfo; + + deviceID = iosInfo.identifierForVendor ?? ''; + } else { + var uuid = Uuid(); + deviceID = uuid.v4(); + } + + return deviceID; +} diff --git a/lib/core/router.dart b/lib/core/router.dart index 0e850ed..6ad4d78 100644 --- a/lib/core/router.dart +++ b/lib/core/router.dart @@ -5,8 +5,10 @@ import 'package:rijig_mobile/screen/app/cart/cart_screen.dart'; import 'package:rijig_mobile/screen/app/home/home_screen.dart'; import 'package:rijig_mobile/screen/app/profil/profil_screen.dart'; import 'package:rijig_mobile/screen/app/requestpick/requestpickup_screen.dart'; +import 'package:rijig_mobile/screen/auth/inputpin_screen.dart'; import 'package:rijig_mobile/screen/auth/login_screen.dart'; import 'package:rijig_mobile/screen/auth/otp_screen.dart'; +import 'package:rijig_mobile/screen/auth/verifpin_screen.dart'; import 'package:rijig_mobile/screen/launch/onboardingpage_screen.dart'; import 'package:rijig_mobile/screen/launch/splash_screen.dart'; @@ -25,10 +27,8 @@ final router = GoRouter( return VerifotpScreen(phone: phone!); }, ), - GoRoute( - path: '/inputpin', - builder: (context, state) => OnboardingPageScreen(), - ), + GoRoute(path: '/setpin', builder: (context, state) => UserPinScreen()), + GoRoute(path: '/verifpin', builder: (context, state) => VerifPinScreen()), GoRoute( path: '/navigasi', builder: (context, state) { diff --git a/lib/main.dart b/lib/main.dart index 873ec4d..585c7f6 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,6 +7,7 @@ import 'package:intl/date_symbol_data_local.dart'; import 'package:provider/provider.dart'; import 'package:rijig_mobile/core/router.dart'; import 'package:rijig_mobile/viewmodel/auth_vmod.dart'; +import 'package:rijig_mobile/viewmodel/userpin_vmod.dart'; // Import PinViewModel void main() async { await dotenv.load(fileName: "server/.env.dev"); @@ -24,14 +25,17 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { return ChangeNotifierProvider( create: (_) => AuthViewModel(), - child: ScreenUtilInit( - designSize: const Size(375, 812), - builder: (_, child) { - return MaterialApp.router( - debugShowCheckedModeBanner: false, - routerConfig: router, - ); - }, + child: ChangeNotifierProvider( + create: (_) => PinViewModel(), // Tambahkan PinViewModel + child: ScreenUtilInit( + designSize: const Size(375, 812), + builder: (_, child) { + return MaterialApp.router( + debugShowCheckedModeBanner: false, + routerConfig: router, + ); + }, + ), ), ); } diff --git a/lib/model/auth_model.dart b/lib/model/auth_model.dart index 8330b67..5769508 100644 --- a/lib/model/auth_model.dart +++ b/lib/model/auth_model.dart @@ -15,11 +15,16 @@ class AuthModel { } } - Future verifyOtp(String phone, String otp) async { + Future verifyOtp( + String phone, + String otp, + String deviceId, + ) async { try { var response = await _apiService.post('/authmasyarakat/verify-otp', { 'phone': phone, 'otp': otp, + 'device_id': deviceId, }); return ResponseModel.fromJson(response); } catch (e) { diff --git a/lib/model/userpin_model.dart b/lib/model/userpin_model.dart index 2783bf4..8441687 100644 --- a/lib/model/userpin_model.dart +++ b/lib/model/userpin_model.dart @@ -1,25 +1,34 @@ import 'package:rijig_mobile/core/api_services.dart'; -import 'package:rijig_mobile/model/response_model.dart'; -class AuthService { +class PinModel { final ApiService _apiService = ApiService(); - Future cekPinStatus(String userid) async { + Future checkPinStatus() async { try { var response = await _apiService.get('/cek-pin-status'); - return ResponseModel.fromJson(response); + if (response['meta']['status'] == 200) { + return true; + } else { + return false; + } } catch (e) { rethrow; } } - Future> verifyOtp(String phone, String otp) async { + Future setPin(String userPin) async { try { - var response = await _apiService.post('/authmasyarakat/verify-otp', { - 'phone': phone, - 'otp': otp, - }); - return response; + var response = await _apiService.post('/set-pin', {'userpin': userPin}); + return response['meta']['status'] == 201; + } catch (e) { + rethrow; + } + } + + Future verifyPin(String userPin) async { + try { + var response = await _apiService.post('/verif-pin', {'userpin': userPin}); + return response['meta']['status'] == 200; } catch (e) { rethrow; } diff --git a/lib/screen/auth/inputpin_screen.dart b/lib/screen/auth/inputpin_screen.dart index e69de29..e86f844 100644 --- a/lib/screen/auth/inputpin_screen.dart +++ b/lib/screen/auth/inputpin_screen.dart @@ -0,0 +1,89 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:rijig_mobile/core/guide.dart'; +import 'package:rijig_mobile/core/router.dart'; +import 'package:rijig_mobile/viewmodel/userpin_vmod.dart'; + +class UserPinScreen extends StatefulWidget { + const UserPinScreen({super.key}); + + @override + State createState() => _UserPinScreenState(); +} + +class _UserPinScreenState extends State { + final TextEditingController _pinController = TextEditingController(); + final _formKey = GlobalKey(); + String errorMessage = ''; + + @override + Widget build(BuildContext context) { + final pinViewModel = Provider.of(context); + + return Scaffold( + appBar: AppBar(title: const Text("Buat PIN Pengguna")), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Form( + key: _formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TextFormField( + controller: _pinController, + decoration: const InputDecoration(labelText: 'Masukkan PIN'), + obscureText: true, + maxLength: 6, + keyboardType: TextInputType.number, + validator: (value) { + if (value == null || value.isEmpty) { + return 'PIN harus diisi'; + } else if (value.length != 6) { + return 'PIN harus 6 digit'; + } + return null; + }, + ), + const SizedBox(height: 20), + ElevatedButton( + onPressed: () async { + if (_formKey.currentState?.validate() ?? false) { + final pin = _pinController.text; + + bool pinCreated = await pinViewModel.checkPinStatus(); + + if (!pinCreated) { + bool pinSet = await pinViewModel.setPin(pin); + if (pinSet) { + router.go('/navigasi'); + } else { + setState(() { + errorMessage = 'Gagal membuat PIN'; + }); + } + } else { + bool pinVerified = await pinViewModel.verifyPin(pin); + if (pinVerified) { + router.go('/navigasi'); + } else { + setState(() { + errorMessage = 'PIN yang anda masukkan salah'; + }); + } + } + } + }, + child: const Text('Kirim'), + ), + if (errorMessage.isNotEmpty) + Padding( + padding: const EdgeInsets.only(top: 20), + child: Text(errorMessage, style: TextStyle(color: redColor)), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/screen/auth/otp_screen.dart b/lib/screen/auth/otp_screen.dart index 4256f97..d4263d6 100644 --- a/lib/screen/auth/otp_screen.dart +++ b/lib/screen/auth/otp_screen.dart @@ -58,11 +58,26 @@ class _VerifotpScreenState extends State { horizontal: double.infinity, vertical: 50, onTap: () { + // if (_otpController.text.isNotEmpty) { + // viewModel.verifyOtp(widget.phone, _otpController.text).then( + // (_) { + // if (viewModel.errorMessage == null) { + // router.go('/navigasi'); + // } else { + // debugPrint(viewModel.errorMessage ?? ''); + // } + // }, + // ); + // } if (_otpController.text.isNotEmpty) { viewModel.verifyOtp(widget.phone, _otpController.text).then( (_) { if (viewModel.errorMessage == null) { - router.go('/navigasi'); + if (viewModel.pinExists == false) { + router.go('/setpin'); + } else { + router.go('/verifpin'); + } } else { debugPrint(viewModel.errorMessage ?? ''); } diff --git a/lib/screen/auth/verifpin_screen.dart b/lib/screen/auth/verifpin_screen.dart new file mode 100644 index 0000000..8bbda54 --- /dev/null +++ b/lib/screen/auth/verifpin_screen.dart @@ -0,0 +1,80 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:rijig_mobile/core/router.dart'; +import 'package:rijig_mobile/viewmodel/userpin_vmod.dart'; + +class VerifPinScreen extends StatefulWidget { + const VerifPinScreen({super.key}); + + @override + State createState() => _VerifPinScreenState(); +} + +class _VerifPinScreenState extends State { + final TextEditingController _pinController = TextEditingController(); + final _formKey = GlobalKey(); + String errorMessage = ''; + + @override + Widget build(BuildContext context) { + final pinViewModel = Provider.of(context); + + return Scaffold( + appBar: AppBar(title: const Text("Verifikasi PIN")), + body: SafeArea( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Form( + key: _formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TextFormField( + controller: _pinController, + decoration: const InputDecoration(labelText: 'Masukkan PIN'), + obscureText: true, + maxLength: 6, + keyboardType: TextInputType.number, + validator: (value) { + if (value == null || value.isEmpty) { + return 'PIN harus diisi'; + } else if (value.length != 6) { + return 'PIN harus 6 digit'; + } + return null; + }, + ), + const SizedBox(height: 20), + ElevatedButton( + onPressed: () async { + if (_formKey.currentState?.validate() ?? false) { + final pin = _pinController.text; + + bool pinVerified = await pinViewModel.verifyPin(pin); + if (pinVerified) { + router.go('/navigasi'); + } else { + setState(() { + errorMessage = 'PIN yang Anda masukkan salah'; + }); + } + } + }, + child: const Text('Kirim'), + ), + if (errorMessage.isNotEmpty) + Padding( + padding: const EdgeInsets.only(top: 20), + child: Text( + errorMessage, + style: const TextStyle(color: Colors.red), + ), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/viewmodel/auth_vmod.dart b/lib/viewmodel/auth_vmod.dart index a4b33ab..1ae0a8d 100644 --- a/lib/viewmodel/auth_vmod.dart +++ b/lib/viewmodel/auth_vmod.dart @@ -1,15 +1,19 @@ import 'package:flutter/material.dart'; +import 'package:rijig_mobile/core/getinfodevice.dart'; import 'package:rijig_mobile/model/response_model.dart'; import 'package:rijig_mobile/model/auth_model.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:rijig_mobile/model/userpin_model.dart'; import 'package:shared_preferences/shared_preferences.dart'; class AuthViewModel extends ChangeNotifier { final AuthModel _authModel = AuthModel(); + final PinModel _pinModel = PinModel(); final FlutterSecureStorage _secureStorage = FlutterSecureStorage(); bool isLoading = false; String? errorMessage; ResponseModel? authModel; + bool? pinExists; Future login(String phone) async { try { @@ -22,7 +26,6 @@ class AuthViewModel extends ChangeNotifier { if (response != null && response.status == 200) { SharedPreferences prefs = await SharedPreferences.getInstance(); await prefs.setBool('isLoggedIn', false); - authModel = response; } else { errorMessage = response?.message ?? 'Failed to send OTP'; @@ -41,7 +44,9 @@ class AuthViewModel extends ChangeNotifier { errorMessage = null; notifyListeners(); - var response = await _authModel.verifyOtp(phone, otp); + String deviceId = await getDeviceId(); + + var response = await _authModel.verifyOtp(phone, otp, deviceId); if (response != null && response.status == 200) { await _secureStorage.write( @@ -59,8 +64,10 @@ class AuthViewModel extends ChangeNotifier { SharedPreferences prefs = await SharedPreferences.getInstance(); await prefs.setBool('isLoggedIn', true); + pinExists = await _pinModel.checkPinStatus(); authModel = response; + notifyListeners(); } else { errorMessage = response?.message ?? 'Failed to verify OTP'; } diff --git a/lib/viewmodel/userpin_vmod.dart b/lib/viewmodel/userpin_vmod.dart new file mode 100644 index 0000000..b1c680d --- /dev/null +++ b/lib/viewmodel/userpin_vmod.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; +import 'package:rijig_mobile/model/userpin_model.dart'; + +class PinViewModel extends ChangeNotifier { + final PinModel _pinService = PinModel(); + + Future checkPinStatus() async { + try { + return await _pinService.checkPinStatus(); + } catch (e) { + throw Exception('Error checking PIN status: $e'); + } + } + + Future setPin(String userPin) async { + try { + return await _pinService.setPin(userPin); + } catch (e) { + throw Exception('Error setting PIN: $e'); + } + } + + Future verifyPin(String userPin) async { + try { + return await _pinService.verifyPin(userPin); + } catch (e) { + throw Exception('Error verifying PIN: $e'); + } + } +} diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 37af1fe..a05e955 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,11 +5,13 @@ import FlutterMacOS import Foundation +import device_info_plus import flutter_secure_storage_macos import path_provider_foundation import shared_preferences_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) diff --git a/pubspec.lock b/pubspec.lock index b8794c2..b4fd9d2 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -73,6 +73,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" + device_info_plus: + dependency: "direct main" + description: + name: device_info_plus + sha256: "0c6396126421b590089447154c5f98a5de423b70cfb15b1578fd018843ee6f53" + url: "https://pub.dev" + source: hosted + version: "11.4.0" + device_info_plus_platform_interface: + dependency: transitive + description: + name: device_info_plus_platform_interface + sha256: "0b04e02b30791224b31969eb1b50d723498f402971bff3630bca2ba839bd1ed2" + url: "https://pub.dev" + source: hosted + version: "7.0.2" fake_async: dependency: transitive description: @@ -97,6 +113,14 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.1" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" flutter: dependency: "direct main" description: flutter @@ -501,6 +525,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.1" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" stack_trace: dependency: transitive description: @@ -549,6 +581,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + uuid: + dependency: "direct main" + description: + name: uuid + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + url: "https://pub.dev" + source: hosted + version: "4.5.1" vector_graphics: dependency: transitive description: @@ -605,6 +645,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.12.0" + win32_registry: + dependency: transitive + description: + name: win32_registry + sha256: "6f1b564492d0147b330dd794fee8f512cec4977957f310f9951b5f9d83618dae" + url: "https://pub.dev" + source: hosted + version: "2.1.0" xdg_directories: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 1207389..4f98a0e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,6 +10,7 @@ environment: dependencies: concentric_transition: ^1.0.3 cupertino_icons: ^1.0.8 + device_info_plus: ^11.4.0 flutter: sdk: flutter flutter_dotenv: ^5.2.1 @@ -25,6 +26,7 @@ dependencies: pin_code_fields: ^8.0.1 provider: ^6.1.4 shared_preferences: ^2.3.3 + uuid: ^4.5.1 dev_dependencies: flutter_lints: ^5.0.0