From 1af974881aa2c19a69138a04157ddb6c672dee58 Mon Sep 17 00:00:00 2001 From: sindhu Date: Fri, 26 Jan 2024 10:53:46 +0700 Subject: [PATCH] step 10 : check distance selfie or not from back end --- lib/app/route.dart | 14 +- lib/main.dart | 4 +- lib/model/check_distance_model.dart | 36 +++ .../current_check_distance_provider.dart | 4 + lib/repository/presensi_repository.dart | 45 +++ lib/screen/home/home_screen.dart | 276 +++++++++++++----- lib/screen/login/login_screen.dart | 154 +++++----- .../presensi/check_distance_provider.dart | 81 +++++ lib/screen/presensi/presensi_screen.dart | 248 ++++++++++++++-- .../presensi/presensi_selfie_screen.dart | 48 +++ 10 files changed, 751 insertions(+), 159 deletions(-) create mode 100644 lib/model/check_distance_model.dart create mode 100644 lib/provider/current_check_distance_provider.dart create mode 100644 lib/repository/presensi_repository.dart create mode 100644 lib/screen/presensi/check_distance_provider.dart create mode 100644 lib/screen/presensi/presensi_selfie_screen.dart diff --git a/lib/app/route.dart b/lib/app/route.dart index f9bd7c3..1804f00 100644 --- a/lib/app/route.dart +++ b/lib/app/route.dart @@ -1,5 +1,6 @@ import 'package:absensi_sas_flutter/screen/home/home_screen_v1.dart'; import 'package:absensi_sas_flutter/screen/presensi/presensi_screen.dart'; +import 'package:absensi_sas_flutter/screen/presensi/presensi_selfie_screen.dart'; import 'package:flutter/material.dart'; import '../screen/home/home_screen.dart'; import '../test_flutter_map.dart'; @@ -11,10 +12,10 @@ const splashRoute = "/splashRoute"; const testFlutterMapRoute = "/testFlutterMapRoute"; const homeRoute = "/homeRoute"; const presensiRoute = "/presensiRoute"; +const presensiSelfieRoute = "/presensiSelfieRoute"; class AppRoute { static Route generateRoute(RouteSettings settings) { - // test flutter map if (settings.name == testFlutterMapRoute) { return MaterialPageRoute(builder: (context) { @@ -48,6 +49,17 @@ class AppRoute { }); } + // presensi selfie route + if (settings.name == presensiSelfieRoute) { + return MaterialPageRoute(builder: (context) { + return MediaQuery( + data: MediaQuery.of(context) + .copyWith(textScaleFactor: 1.0, padding: EdgeInsets.all(0)), + child: PresensiSelfieScreen(), + ); + }); + } + // splash screen if (settings.name == splashRoute) { return MaterialPageRoute(builder: (context) { diff --git a/lib/main.dart b/lib/main.dart index ee89c99..bbc2766 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -45,8 +45,8 @@ class MyApp extends StatelessWidget { primarySwatch: Colors.orange, ), // home: TestMap(), - // initialRoute: loginRoute, - initialRoute: presensiRoute, + initialRoute: loginRoute, + // initialRoute: presensiRoute, // initialRoute: testFlutterMapRoute, onGenerateRoute: AppRoute.generateRoute, ); diff --git a/lib/model/check_distance_model.dart b/lib/model/check_distance_model.dart new file mode 100644 index 0000000..1389418 --- /dev/null +++ b/lib/model/check_distance_model.dart @@ -0,0 +1,36 @@ +class CheckDistanceModel { + String? status; + String? message; + String? selfie; + String? unit; + String? maxDistance; + String? currentDistance; + + CheckDistanceModel( + {this.status, + this.message, + this.selfie, + this.unit, + this.maxDistance, + this.currentDistance}); + + CheckDistanceModel.fromJson(Map json) { + status = json['status']; + message = json['message']; + selfie = json['selfie']; + unit = json['unit']; + maxDistance = json['max_distance']; + currentDistance = json['current_distance']; + } + + Map toJson() { + final Map data = new Map(); + data['status'] = this.status; + data['message'] = this.message; + data['selfie'] = this.selfie; + data['unit'] = this.unit; + data['max_distance'] = this.maxDistance; + data['current_distance'] = this.currentDistance; + return data; + } +} \ No newline at end of file diff --git a/lib/provider/current_check_distance_provider.dart b/lib/provider/current_check_distance_provider.dart new file mode 100644 index 0000000..96e7a28 --- /dev/null +++ b/lib/provider/current_check_distance_provider.dart @@ -0,0 +1,4 @@ +import 'package:absensi_sas_flutter/model/check_distance_model.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +final currentCheckDistanceProvider = StateProvider((ref) => null); \ No newline at end of file diff --git a/lib/repository/presensi_repository.dart b/lib/repository/presensi_repository.dart new file mode 100644 index 0000000..8537b56 --- /dev/null +++ b/lib/repository/presensi_repository.dart @@ -0,0 +1,45 @@ +import 'package:absensi_sas_flutter/model/check_distance_model.dart'; +import 'base_repository.dart'; + +class PresensiRepository extends BaseRepository { + PresensiRepository({required super.graphql, required super.dio}); + + // check distance + Future checkDistance( + String M_StaffID, + String M_CompanyID, + String CurrentLatitude, + String CurrentLongitude, + ) async { + const String query = + r'''query($M_StaffID:String!, $M_CompanyID:String!, $CurrentLatitude:String!, $CurrentLongitude:String!){ queryCheckDistance(M_StaffID:$M_StaffID, M_CompanyID:$M_CompanyID, CurrentLatitude:$CurrentLatitude, CurrentLongitude:$CurrentLongitude){ status message selfie unit max_distance current_distance } }'''; + + Map inpVariables = { + "M_StaffID": M_StaffID, + "M_CompanyID": M_CompanyID, + "CurrentLatitude": CurrentLatitude, + "CurrentLongitude": CurrentLongitude, + }; + final resp = await postGraphQlMutation(query, inpVariables); + // final loginData = AuthModel.fromJson(resp['userLogin']); + + print(inpVariables); + + print('obj queryCheckDistance : ${resp["queryCheckDistance"]}'); + + // final result = AuthModel( + // token: resp["loginAttendance"]["token"], + // model: StaffModel.fromJson(resp["loginAttendance"]), + // ); + + final result = CheckDistanceModel( + currentDistance: resp['queryCheckDistance']['current_distance'], + maxDistance: resp['queryCheckDistance']['max_distance'], + message: resp['queryCheckDistance']['message'], + selfie: resp['queryCheckDistance']['selfie'], + status: resp['queryCheckDistance']['status'], + unit: resp['queryCheckDistance']['unit'] + ); + return result; + } +} diff --git a/lib/screen/home/home_screen.dart b/lib/screen/home/home_screen.dart index 6e1521b..f777421 100644 --- a/lib/screen/home/home_screen.dart +++ b/lib/screen/home/home_screen.dart @@ -1,16 +1,134 @@ import 'package:absensi_sas_flutter/app/constant.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:geocoding/geocoding.dart'; +import 'package:geolocator/geolocator.dart'; +import 'package:google_sign_in/google_sign_in.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:permission_handler/permission_handler.dart'; +import '../../app/route.dart'; +import '../../provider/current_check_distance_provider.dart'; +import '../../provider/current_user_provider.dart'; +import '../../provider/google_login_provider.dart'; import '../../widget/real_date.dart'; import '../../widget/real_time.dart'; +import '../../widget/sankbar_widget.dart'; +import '../presensi/check_distance_provider.dart'; class HomeScreen extends HookConsumerWidget { const HomeScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { - + final selectedUser = ref.read(currentUserProvider); + final isLoadingProsesCheckDistance = useState(false); + final varCurrentDistanceProvider = ref.watch(currentCheckDistanceProvider); + final positionLatitude = useState(""); + final positionLongitude = useState(""); + GoogleSignInAccount? currentUserGoogle = + ref.watch(currentUserGoogleProvider); + + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + final staffID = ref.read(currentUserProvider)?.model.staffId ?? "0"; + if (staffID == "0") { + //not login + Navigator.of(context) + .pushNamedAndRemoveUntil(loginRoute, (route) => true); + + // Navigator.popAndPushNamed(context, loginRoute); + return; + } + }); + return () {}; + }, []); + + Future getAddressFromLocation() async { + try { + isLoadingProsesCheckDistance.value = true; + // Mendapatkan posisi pengguna + LocationPermission permission = await Geolocator.requestPermission(); + + if (permission == LocationPermission.denied) { + isLoadingProsesCheckDistance.value = false; + SanckbarWidget(context, 'Izin lokasi ditolak', snackbarType.error); + // Handle jika pengguna menolak izin lokasi + print("Izin lokasi ditolak"); + return; + } + + Position position = await Geolocator.getCurrentPosition( + desiredAccuracy: LocationAccuracy.high); + + // Mendapatkan alamat dari posisi + List placemarks = await placemarkFromCoordinates( + position.latitude, position.longitude); + + if (placemarks.isNotEmpty) { + isLoadingProsesCheckDistance.value = false; + Placemark placemark = placemarks.first; + // String address = + // "${placemark.thoroughfare}, ${placemark.locality}, ${placemark.administrativeArea}, ${placemark.country},"; + + String address = + "${placemark.street}, ${placemark.subLocality}, ${placemark.subAdministrativeArea}, ${placemark.postalCode}"; + print("Alamat: $address"); + + positionLatitude.value = position.latitude.toString(); + positionLongitude.value = position.longitude.toString(); + + // panggil check distance provider + ref.read(checkDistanceProvider.notifier).checkDistance( + selectedUser?.model.staffId ?? "", + selectedUser?.model.companyId ?? "", + positionLatitude.value, + positionLongitude.value, + ); + } else { + isLoadingProsesCheckDistance.value = false; + SanckbarWidget( + context, 'Tidak dapat menemukan alamat.', snackbarType.error); + print("Tidak dapat menemukan alamat."); + } + } catch (e) { + print("Error: $e"); + isLoadingProsesCheckDistance.value = false; + SanckbarWidget(context, 'Error : $e', snackbarType.error); + } + } + + Future requestLocationPermission() async { + var status = await Permission.location.request(); + isLoadingProsesCheckDistance.value = true; + if (status.isGranted) { + // Izin diberikan, lanjutkan dengan mendapatkan lokasi + getAddressFromLocation(); + } else { + isLoadingProsesCheckDistance.value = false; + // Izin ditolak, berikan pemberitahuan atau instruksi + // print('Izin lokasi ditolak'); + SanckbarWidget(context, 'Izin Ditolak', snackbarType.error); + } + } + + // check distance provider + ref.listen(checkDistanceProvider, (prev, next) { + if (next is CheckDistanceStateLoading) { + isLoadingProsesCheckDistance.value = true; + } else if (next is CheckDistanceStateError) { + isLoadingProsesCheckDistance.value = false; + SanckbarWidget(context, next.message, snackbarType.warning); + } else if (next is CheckDistanceStateDone) { + isLoadingProsesCheckDistance.value = false; + + if (varCurrentDistanceProvider?.selfie == "TRUE") { + Navigator.pushNamed(context, presensiSelfieRoute); + } else { + Navigator.pushNamed(context, presensiRoute); + } + } + }); return Scaffold( floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, @@ -18,21 +136,33 @@ class HomeScreen extends HookConsumerWidget { width: Constant.getActualXPhone(context: context, x: 100), height: Constant.getActualYPhone(context: context, y: 100), child: FittedBox( - child: FloatingActionButton( - onPressed: () {}, - backgroundColor: Color(0xFFFFFFFF), - shape: CircleBorder(), - child: Container( - width: Constant.getActualXPhone(context: context, x: 50), - height: Constant.getActualYPhone(context: context, y: 50), - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage( - 'images/finger_tap_orange_botnav.png'), // Ganti dengan path gambar Anda + child: (isLoadingProsesCheckDistance.value) + ? SizedBox( + width: Constant.getActualXPhone(context: context, x: 50), + height: Constant.getActualYPhone(context: context, y: 50), + child: Center( + child: CircularProgressIndicator( + color: Constant.textOrange, + ), + ), + ) + : FloatingActionButton( + onPressed: () async { + requestLocationPermission(); + }, + backgroundColor: Color(0xFFFFFFFF), + shape: CircleBorder(), + child: Container( + width: Constant.getActualXPhone(context: context, x: 50), + height: Constant.getActualYPhone(context: context, y: 50), + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage( + 'images/finger_tap_orange_botnav.png'), // Ganti dengan path gambar Anda + ), + ), + ), ), - ), - ), - ), ), ), bottomNavigationBar: Container( @@ -102,61 +232,73 @@ class HomeScreen extends HookConsumerWidget { right: Constant.getActualXPhone(context: context, x: 27), ), child: Container( - child: ListTile( - leading: Container( - width: - Constant.getActualXPhone(context: context, x: 36), - height: - Constant.getActualYPhone(context: context, y: 36), - child: Image( - image: AssetImage('images/avatar_c.png'), - )), - title: Text( - "Stephen Kusumo", - overflow: TextOverflow.ellipsis, - style: Constant.titleH1_700(context: context) - ..copyWith( - color: Constant.textBlack, - ), - ), - subtitle: Text( - "Step@example.com", - style: - Constant.subtitle_500_12(context: context).copyWith( - color: Constant.textLightGrey, - ), - ), - trailing: Container( - width: - Constant.getActualXPhone(context: context, x: 36), - height: - Constant.getActualYPhone(context: context, y: 36), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12.0), - color: Colors.white, - shape: BoxShape.rectangle, - boxShadow: [ - BoxShadow( - offset: Offset(0, 12), - blurRadius: 24, - color: Color.fromRGBO(145, 158, 171, 0.12), + child: (currentUserGoogle == null) + ? Center( + child: CircularProgressIndicator(), + ) + : ListTile( + // leading: Container( + // width: Constant.getActualXPhone( + // context: context, x: 36), + // height: Constant.getActualYPhone( + // context: context, y: 36), + // child: Image( + // image: AssetImage('images/avatar_c.png'), + // ), + // ), + leading: GoogleUserCircleAvatar( + identity: currentUserGoogle, ), - ], - ), - child: IconButton( - onPressed: () {}, - icon: Container( - width: Constant.getActualXPhone( - context: context, x: 20), - height: Constant.getActualYPhone( - context: context, y: 20), - child: Image( - image: AssetImage('images/alert_badge.png'), + title: Text( + // "Stephen Kusumo", + // selectedUser?.model.name ?? "", + currentUserGoogle.displayName ?? "", + overflow: TextOverflow.ellipsis, + style: Constant.titleH1_700(context: context) + ..copyWith( + color: Constant.textBlack, + ), + ), + subtitle: Text( + // "Step@example.com", + // currentUserGoogle?.email ?? "", + selectedUser?.model.email ?? "", + style: Constant.subtitle_500_12(context: context) + .copyWith( + color: Constant.textLightGrey, + ), + ), + trailing: Container( + width: Constant.getActualXPhone( + context: context, x: 36), + height: Constant.getActualYPhone( + context: context, y: 36), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12.0), + color: Colors.white, + shape: BoxShape.rectangle, + boxShadow: [ + BoxShadow( + offset: Offset(0, 12), + blurRadius: 24, + color: Color.fromRGBO(145, 158, 171, 0.12), + ), + ], + ), + child: IconButton( + onPressed: () {}, + icon: Container( + width: Constant.getActualXPhone( + context: context, x: 20), + height: Constant.getActualYPhone( + context: context, y: 20), + child: Image( + image: AssetImage('images/alert_badge.png'), + ), + ), + ), ), ), - ), - ), - ), ), ), diff --git a/lib/screen/login/login_screen.dart b/lib/screen/login/login_screen.dart index 093c903..accdeef 100644 --- a/lib/screen/login/login_screen.dart +++ b/lib/screen/login/login_screen.dart @@ -163,51 +163,47 @@ class LoginScreen extends HookConsumerWidget { SizedBox( height: Constant.getActualYPhone(context: context, y: 100), ), - (currentUserGoogle != null) - ? Container( - child: ListTile( - leading: GoogleUserCircleAvatar( - identity: currentUserGoogle, - ), - title: Text( - currentUserGoogle.displayName ?? "", - ), - subtitle: Text( - currentUserGoogle.email, - ), - trailing: IconButton( - icon: Icon(Icons.logout_outlined), - onPressed: () async { - await googleSignIn.disconnect(); - }, - ), - ), - ) - : - // Logo Landscape - Padding( - padding: EdgeInsets.only( - top: Constant.getActualYPhone(context: context, y: 120), - left: Constant.getActualXPhone(context: context, x: 91), - right: - Constant.getActualXPhone(context: context, x: 90), - // bottom: Constant.getActualYPhone(context: context, y: y) - ), - child: Container( - width: - Constant.getActualXPhone(context: context, x: 209), - height: - Constant.getActualYPhone(context: context, y: 70), - decoration: BoxDecoration( - // color: Colors.green, - image: DecorationImage( - // fit: BoxFit.contain, - image: AssetImage( - 'images/logo_sismedika_landscape.png'), - ), - ), - ), + // (currentUserGoogle != null) + // ? Container( + // child: ListTile( + // leading: GoogleUserCircleAvatar( + // identity: currentUserGoogle, + // ), + // title: Text( + // currentUserGoogle.displayName ?? "", + // ), + // subtitle: Text( + // currentUserGoogle.email, + // ), + // trailing: IconButton( + // icon: Icon(Icons.logout_outlined), + // onPressed: () async { + // await googleSignIn.disconnect(); + // }, + // ), + // ), + // ) + // : + // Logo Landscape + Padding( + padding: EdgeInsets.only( + top: Constant.getActualYPhone(context: context, y: 120), + left: Constant.getActualXPhone(context: context, x: 91), + right: Constant.getActualXPhone(context: context, x: 90), + // bottom: Constant.getActualYPhone(context: context, y: y) + ), + child: Container( + width: Constant.getActualXPhone(context: context, x: 209), + height: Constant.getActualYPhone(context: context, y: 70), + decoration: BoxDecoration( + // color: Colors.green, + image: DecorationImage( + // fit: BoxFit.contain, + image: AssetImage('images/logo_sismedika_landscape.png'), ), + ), + ), + ), SizedBox( height: Constant.getActualYPhone(context: context, y: 100), @@ -282,33 +278,51 @@ class LoginScreen extends HookConsumerWidget { onPressed: () async { await handleSignInGmail(); }, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, + child: Stack( children: [ - Container( - width: - Constant.getActualXPhone(context: context, x: 24), - height: - Constant.getActualYPhone(context: context, y: 24), - decoration: BoxDecoration( - // color: Colors.green, - image: DecorationImage( - // fit: BoxFit.contain, - image: AssetImage('images/icon_google.png'), - ), - ), - ), - SizedBox( - width: - Constant.getActualXPhone(context: context, x: 2), - ), - Text( - 'Continue with Google', - style: Constant.logintitle_700(context: context) - .copyWith( - color: Constant.textBlack, - ), - ), + (isLoading.value) + ? SizedBox( + width: Constant.getActualXPhone( + context: context, x: 24), + height: Constant.getActualYPhone( + context: context, y: 24), + child: Center( + child: CircularProgressIndicator( + color: Constant.textTrueBlack, + ), + ), + ) + : Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: Constant.getActualXPhone( + context: context, x: 24), + height: Constant.getActualYPhone( + context: context, y: 24), + decoration: BoxDecoration( + // color: Colors.green, + image: DecorationImage( + // fit: BoxFit.contain, + image: AssetImage( + 'images/icon_google.png'), + ), + ), + ), + SizedBox( + width: Constant.getActualXPhone( + context: context, x: 2), + ), + Text( + 'Continue with Google', + style: Constant.logintitle_700( + context: context) + .copyWith( + color: Constant.textBlack, + ), + ), + ], + ), ], ), style: ButtonStyle( diff --git a/lib/screen/presensi/check_distance_provider.dart b/lib/screen/presensi/check_distance_provider.dart new file mode 100644 index 0000000..b0e5c4d --- /dev/null +++ b/lib/screen/presensi/check_distance_provider.dart @@ -0,0 +1,81 @@ +import 'package:absensi_sas_flutter/repository/presensi_repository.dart'; + +import 'package:equatable/equatable.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../model/check_distance_model.dart'; +import '../../provider/current_check_distance_provider.dart'; +import '../../provider/dio_provider.dart'; +import '../../provider/graphql_provider.dart'; +import '../../repository/base_repository.dart'; + +abstract class CheckDistanceState extends Equatable { + final DateTime date; + const CheckDistanceState(this.date); + @override + List get props => [date]; +} + +class CheckDistanceStateInit extends CheckDistanceState { + CheckDistanceStateInit() : super(DateTime.now()); +} + +class CheckDistanceStateLoading extends CheckDistanceState { + CheckDistanceStateLoading() : super(DateTime.now()); +} + +class CheckDistanceStateError extends CheckDistanceState { + final String message; + CheckDistanceStateError({ + required this.message, + }) : super(DateTime.now()); +} + +class CheckDistanceStateDone extends CheckDistanceState { + final CheckDistanceModel model; + CheckDistanceStateDone({ + required this.model, + }) : super(DateTime.now()); +} + +//notifier +class CheckDistanceNotifier extends StateNotifier { + final Ref ref; + CheckDistanceNotifier({ + required this.ref, + }) : super(CheckDistanceStateInit()); + + void checkDistance( + String M_StaffID, + String M_CompanyID, + String CurrentLatitude, + String CurrentLongitude, + ) async { + try { + state = CheckDistanceStateLoading(); + final graphql = ref.read(graphqlProvider('')); + final dio = ref.read(dioProvider); + final resp = + await PresensiRepository(graphql: graphql, dio: dio).checkDistance( + M_StaffID, + M_CompanyID, + CurrentLatitude, + CurrentLongitude, + ); + // set ke global state provider + ref.read(currentCheckDistanceProvider.notifier).update((state) => resp); + state = CheckDistanceStateDone(model: resp); + } catch (e) { + if (e is BaseRepositoryException) { + state = CheckDistanceStateError(message: e.message ?? ""); + } else { + state = CheckDistanceStateError(message: e.toString()); + } + } + } +} + +// provider +final checkDistanceProvider = + StateNotifierProvider( + (ref) => CheckDistanceNotifier(ref: ref), +); diff --git a/lib/screen/presensi/presensi_screen.dart b/lib/screen/presensi/presensi_screen.dart index 3123056..4899b30 100644 --- a/lib/screen/presensi/presensi_screen.dart +++ b/lib/screen/presensi/presensi_screen.dart @@ -1,3 +1,4 @@ +import 'package:absensi_sas_flutter/screen/presensi/check_distance_provider.dart'; import 'package:absensi_sas_flutter/widget/real_date.dart'; import 'package:absensi_sas_flutter/widget/sankbar_widget.dart'; import 'package:flutter/material.dart'; @@ -7,8 +8,10 @@ import 'package:geolocator/geolocator.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:permission_handler/permission_handler.dart'; - import '../../app/constant.dart'; +import '../../app/route.dart'; +import '../../provider/current_check_distance_provider.dart'; +import '../../provider/current_user_provider.dart'; import '../../widget/real_time.dart'; class PresensiScreen extends HookConsumerWidget { @@ -18,6 +21,13 @@ class PresensiScreen extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final currentAddressUserLocation = useState(""); final isLoadingAddressUserLocation = useState(false); + final isLoadingProsesCheckDistance = useState(false); + final varCurrentDistanceProvider = ref.watch(currentCheckDistanceProvider); + final selectedUser = ref.read(currentUserProvider); + + final positionLatitude = useState(""); + final positionLongitude = useState(""); + // final tTransactionCurrentDistance = useState("NULL"); Future getAddressFromLocation() async { try { @@ -40,24 +50,42 @@ class PresensiScreen extends HookConsumerWidget { List placemarks = await placemarkFromCoordinates( position.latitude, position.longitude); - if (placemarks.isNotEmpty) { - isLoadingAddressUserLocation.value = false; - Placemark placemark = placemarks.first; - // String address = - // "${placemark.thoroughfare}, ${placemark.locality}, ${placemark.administrativeArea}, ${placemark.country},"; + if (positionLongitude.value.isEmpty && positionLatitude.value.isEmpty) { + if (placemarks.isNotEmpty) { + isLoadingAddressUserLocation.value = false; + Placemark placemark = placemarks.first; + // String address = + // "${placemark.thoroughfare}, ${placemark.locality}, ${placemark.administrativeArea}, ${placemark.country},"; - String address = - "${placemark.street}, ${placemark.subLocality}, ${placemark.subAdministrativeArea}, ${placemark.postalCode}"; - print("Alamat: $address"); + String address = + "${placemark.street}, ${placemark.subLocality}, ${placemark.subAdministrativeArea}, ${placemark.postalCode}"; + print("Alamat: $address"); - if (address != "") { - currentAddressUserLocation.value = address; + positionLatitude.value = position.latitude.toString(); + positionLongitude.value = position.longitude.toString(); + + if (address != "") { + currentAddressUserLocation.value = address; + } + } else { + isLoadingAddressUserLocation.value = false; + SanckbarWidget( + context, 'Tidak dapat menemukan alamat.', snackbarType.error); + print("Tidak dapat menemukan alamat."); } } else { - isLoadingAddressUserLocation.value = false; - SanckbarWidget( - context, 'Tidak dapat menemukan alamat.', snackbarType.error); - print("Tidak dapat menemukan alamat."); + // jika sudah dapat latitude dan logitude baru panggil check distance provider + if (positionLatitude.value.isNotEmpty && + positionLongitude.value.isNotEmpty) { + print('check distance provider'); + // panggil check distance provider + ref.read(checkDistanceProvider.notifier).checkDistance( + selectedUser?.model.staffId ?? "", + selectedUser?.model.companyId ?? "", + positionLatitude.value, + positionLongitude.value, + ); + } } } catch (e) { print("Error: $e"); @@ -66,7 +94,7 @@ class PresensiScreen extends HookConsumerWidget { } } - Future _requestLocationPermission() async { + Future requestLocationPermission() async { var status = await Permission.location.request(); isLoadingAddressUserLocation.value = true; if (status.isGranted) { @@ -76,17 +104,161 @@ class PresensiScreen extends HookConsumerWidget { isLoadingAddressUserLocation.value = false; // Izin ditolak, berikan pemberitahuan atau instruksi // print('Izin lokasi ditolak'); - SanckbarWidget(context, 'Izin Ditolak', snackbarType.error); + SanckbarWidget(context, 'Izin Ditolak', snackbarType.error); } } + // check distance provider + ref.listen(checkDistanceProvider, (prev, next) { + print('status check distance ' + next.toString()); + if (next is CheckDistanceStateLoading) { + isLoadingProsesCheckDistance.value = true; + } else if (next is CheckDistanceStateError) { + isLoadingProsesCheckDistance.value = false; + SanckbarWidget(context, next.message, snackbarType.warning); + } else if (next is CheckDistanceStateDone) { + isLoadingProsesCheckDistance.value = false; + + // tTransactionCurrentDistance.value = + // varCurrentDistanceProvider?.currentDistance ?? ""; + + // print("distance XXX : "+tTransactionCurrentDistance.value); + + if (varCurrentDistanceProvider?.selfie == "TRUE") { + Navigator.pushNamed(context, presensiSelfieRoute); + } + } + }); + useEffect(() { WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { - getAddressFromLocation(); + final staffID = ref.read(currentUserProvider)?.model.staffId ?? "0"; + if (staffID == "0") { + //not login + Navigator.of(context) + .pushNamedAndRemoveUntil(loginRoute, (route) => true); + + // Navigator.popAndPushNamed(context, loginRoute); + return; + } + + // requestLocationPermission(); }); return () {}; }, []); + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + requestLocationPermission(); + + // // jika sudah dapat latitude dan logitude baru panggil check distance provider + // if (positionLatitude.value.isNotEmpty && + // positionLongitude.value.isNotEmpty) { + // // panggil check distance provider + // ref.read(checkDistanceProvider.notifier).checkDistance( + // selectedUser?.model.staffId ?? "", + // selectedUser?.model.companyId ?? "", + // positionLatitude.value, + // positionLongitude.value, + // ); + // } + }); + return () {}; + }, []); + + // show konfirmasi dialog + Future showConfirmationDialog( + BuildContext context, + String T_TransactionM_StaffID, + String T_TransactionM_CompanyID, + String T_TransactionCurrentLatitude, + String T_TransactionCurrentLongitude, + String T_TransactionCurrentDistance, + String T_TransactionSelfiePhoto, + String token, + ) async { + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text('Konfirmasi'), + content: Text('Apakah anda yakin untuk melakukan Clock In?'), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), + actions: [ + ElevatedButton( + onPressed: () { + T_TransactionSelfiePhoto = ""; + + var param = { + "T_TransactionM_StaffID": T_TransactionM_StaffID, + "T_TransactionM_CompanyID": T_TransactionM_CompanyID, + "T_TransactionCurrentLatitude": + T_TransactionCurrentLatitude, + "T_TransactionCurrentLongitude": + T_TransactionCurrentLongitude, + "T_TransactionCurrentDistance": + T_TransactionCurrentDistance, + "T_TransactionSelfiePhoto": T_TransactionSelfiePhoto, + "token":token, + }; + print(param); + Navigator.of(context).pop(); + }, + child: Text( + 'Yakin', + style: Constant.logintitle_700(context: context).copyWith( + fontWeight: FontWeight.w600, + color: Constant.textWhite, + ), + ), + style: ButtonStyle( + backgroundColor: MaterialStateColor.resolveWith( + (st) => Constant.textOrange), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide( + color: Constant.textOrange, + ), + ), + ), + shadowColor: MaterialStateProperty.all(Color(0xffff48423d)), + elevation: MaterialStateProperty.all(4.0), + ), + ), + TextButton( + onPressed: () { + Navigator.of(context).pop(); // Tutup dialog + }, + style: ButtonStyle( + backgroundColor: MaterialStateColor.resolveWith( + (st) => Constant.textWhite), + shape: MaterialStateProperty.all( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide( + color: Constant.textBlack, + ), + ), + ), + shadowColor: MaterialStateProperty.all(Color(0xffff48423d)), + elevation: MaterialStateProperty.all(4.0), + ), + child: Text( + 'Batal', + style: TextStyle( + color: Constant.textTrueBlack, + ), + ), + ), + ], + ); + }, + ); + } + return Padding( padding: EdgeInsets.only( top: Constant.getActualYPhone(context: context, y: 30), @@ -121,6 +293,7 @@ class PresensiScreen extends HookConsumerWidget { height: MediaQuery.of(context).size.height, child: Column( children: [ + // Text(ref.watch(currentCheckDistanceProvider)?.currentDistance ?? "NULL"), // Spacer(), // tanggal sekarang RealTimeFormattedDate(), @@ -198,6 +371,19 @@ class PresensiScreen extends HookConsumerWidget { InkWell( onTap: () async { getAddressFromLocation(); + // jika sudah dapat latitude dan logitude baru panggil check distance provider + if (positionLatitude.value.isNotEmpty && + positionLongitude.value.isNotEmpty) { + // panggil check distance provider + ref + .read(checkDistanceProvider.notifier) + .checkDistance( + selectedUser?.model.staffId ?? "", + selectedUser?.model.companyId ?? "", + positionLatitude.value, + positionLongitude.value, + ); + } }, child: Container( width: Constant.getActualXPhone( @@ -235,7 +421,31 @@ class PresensiScreen extends HookConsumerWidget { height: Constant.getActualYPhone(context: context, y: 130), child: FittedBox( child: FloatingActionButton( - onPressed: () {}, + onPressed: () async { + final T_TransactionM_StaffID = + selectedUser?.model.staffId ?? ""; + final T_TransactionM_CompanyID = + selectedUser?.model.companyId ?? ""; + final T_TransactionCurrentLatitude = + positionLatitude.value; + final T_TransactionCurrentLongitude = + positionLongitude.value; + final T_TransactionCurrentDistance = + varCurrentDistanceProvider?.currentDistance ?? ""; + final T_TransactionSelfiePhoto = ""; + final token = selectedUser?.token ?? ""; + + showConfirmationDialog( + context, + T_TransactionM_StaffID, + T_TransactionM_CompanyID, + T_TransactionCurrentLatitude, + T_TransactionCurrentLongitude, + T_TransactionCurrentDistance, + T_TransactionSelfiePhoto, + token, + ); + }, backgroundColor: Color(0xFFFFFFFF), shape: CircleBorder(), child: Container( diff --git a/lib/screen/presensi/presensi_selfie_screen.dart b/lib/screen/presensi/presensi_selfie_screen.dart new file mode 100644 index 0000000..fed93e3 --- /dev/null +++ b/lib/screen/presensi/presensi_selfie_screen.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; + +import '../../app/constant.dart'; + +class PresensiSelfieScreen extends StatelessWidget { + const PresensiSelfieScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: EdgeInsets.only( + top: Constant.getActualYPhone(context: context, y: 30), + ), + child: Scaffold( + appBar: AppBar( + title: Text( + // 'Home Screen', + 'Presensi', + overflow: TextOverflow.ellipsis, + style: TextStyle( + color: Constant.textBlack, + ), + ), + backgroundColor: Constant.textWhite, + iconTheme: IconThemeData( + color: Constant.textBlack, + ), + // elevation: 1.0, + elevation: 0.5, + ), + body: SafeArea( + child: Padding( + padding: EdgeInsets.only( + top: Constant.getActualYPhone(context: context, y: 58), + left: Constant.getActualXPhone(context: context, x: 33), + right: Constant.getActualXPhone(context: context, x: 27), + ), + child: Container( + width: Constant.getActualXPhone(context: context, x: 390), + height: 100, + color: Colors.green, + ), + ), + ), + ), + ); + } +}