diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 6c6f67d..1b657c3 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -2,7 +2,7 @@ (); + + final Map builders = { + DialogType.gantiPasswordDialogView: (context, request, completer) => + GantiPasswordDialogView(request: request, completer: completer), + }; + + dialogService.registerCustomDialogBuilders(builders); +} diff --git a/lib/app/themes/app_theme.dart b/lib/app/themes/app_theme.dart index f52d219..eefa661 100755 --- a/lib/app/themes/app_theme.dart +++ b/lib/app/themes/app_theme.dart @@ -1,5 +1,3 @@ -// ignore_for_file: deprecated_member_use - import 'package:flutter/material.dart'; import 'app_colors.dart'; @@ -17,9 +15,9 @@ ThemeData appTheme = ThemeData( centerTitle: true, ), textTheme: TextTheme( - headline1: regularTextStyle.copyWith(fontSize: 32), - headline2: regularTextStyle.copyWith(fontSize: 20), - headline3: regularTextStyle.copyWith(fontSize: 18), + displayLarge: regularTextStyle.copyWith(fontSize: 32), + displayMedium: regularTextStyle.copyWith(fontSize: 20), + displaySmall: regularTextStyle.copyWith(fontSize: 18), ), elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( diff --git a/lib/main.dart b/lib/main.dart index a60b0bc..4d85c1e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; @@ -5,12 +7,14 @@ import 'package:stacked_services/stacked_services.dart'; // import 'app/app.bottomsheets.dart'; import 'app/app.bottomsheets.dart'; +import 'app/app.dialogs.dart'; import 'app/app.locator.dart'; import 'app/app.router.dart'; import 'app/themes/app_theme.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); + HttpOverrides.global = MyHttpOverrides(); // await Future.wait([ // dotenv.load(fileName: ".env"), // setupLocator(), @@ -27,7 +31,7 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( - title: 'Cek Suara', + title: 'Cek Suara App', theme: appTheme, debugShowCheckedModeBanner: false, navigatorKey: StackedService.navigatorKey, @@ -39,7 +43,16 @@ class MyApp extends StatelessWidget { Future setupAllLocator() async { await setupLocator(); - // setupDialogUi(); + setupDialogUi(); setupBottomSheetUi(); // setupSnackbarUi(); } + +class MyHttpOverrides extends HttpOverrides { + @override + HttpClient createHttpClient(SecurityContext? context) { + return super.createHttpClient(context) + ..badCertificateCallback = + (X509Certificate cert, String host, int port) => true; + } +} diff --git a/lib/model/area_model.dart b/lib/model/area_model.dart index 94c5f76..ba3731c 100644 --- a/lib/model/area_model.dart +++ b/lib/model/area_model.dart @@ -1,14 +1,14 @@ -class AreaListModel { - List? area; +class KecamatanDetail { + List? kecamatan; int? jumlah; - AreaListModel({this.area, this.jumlah}); + KecamatanDetail({this.kecamatan, this.jumlah}); - AreaListModel.fromJson(Map json) { - if (json['area'] != null) { - area = []; - json['area'].forEach((v) { - area!.add(AreaModel.fromJson(v)); + KecamatanDetail.fromJson(Map json) { + if (json['kecamatan'] != null) { + kecamatan = []; + json['kecamatan'].forEach((v) { + kecamatan!.add(KecamatanModel.fromJson(v)); }); } jumlah = json['jumlah']; @@ -16,29 +16,77 @@ class AreaListModel { Map toJson() { final Map data = {}; - if (area != null) { - data['area'] = area!.map((v) => v.toJson()).toList(); + if (kecamatan != null) { + data['kecamatan'] = kecamatan!.map((v) => v.toJson()).toList(); } data['jumlah'] = jumlah; return data; } } -class AreaModel { - int? idArea; - String? namaArea; +class KecamatanModel { + String? kecamatanId; + String? name; - AreaModel({this.idArea, this.namaArea}); + KecamatanModel({this.kecamatanId, this.name}); - AreaModel.fromJson(Map json) { - idArea = json['id_area']; - namaArea = json['nama_area']; + KecamatanModel.fromJson(Map json) { + kecamatanId = json['kecamatan_id']; + name = json['name']; } Map toJson() { final Map data = {}; - data['id_area'] = idArea; - data['nama_area'] = namaArea; + data['kecamatan_id'] = kecamatanId; + data['name'] = name; + return data; + } +} + +class KelurahanDetail { + List? kelurahan; + int? jumlah; + + KelurahanDetail({this.kelurahan, this.jumlah}); + + KelurahanDetail.fromJson(Map json) { + if (json['kelurahan'] != null) { + kelurahan = []; + json['kelurahan'].forEach((v) { + kelurahan!.add(KelurahanModel.fromJson(v)); + }); + } + jumlah = json['jumlah']; + } + + Map toJson() { + final Map data = {}; + if (kelurahan != null) { + data['kelurahan'] = kelurahan!.map((v) => v.toJson()).toList(); + } + data['jumlah'] = jumlah; + return data; + } +} + +class KelurahanModel { + String? kelurahanId; + String? kecamatanId; + String? name; + + KelurahanModel({this.kelurahanId, this.kecamatanId, this.name}); + + KelurahanModel.fromJson(Map json) { + kelurahanId = json['kelurahan_id']; + kecamatanId = json['kecamatan_id']; + name = json['name']; + } + + Map toJson() { + final Map data = {}; + data['kelurahan_id'] = kelurahanId; + data['kecamatan_id'] = kecamatanId; + data['name'] = name; return data; } } diff --git a/lib/model/pemilih_model.dart b/lib/model/pemilih_model.dart index 25644a5..b06cbfe 100644 --- a/lib/model/pemilih_model.dart +++ b/lib/model/pemilih_model.dart @@ -1,13 +1,13 @@ import '../app/app.locator.dart'; import '../services/other_function.dart'; -class PemilihDetailModel { +class PemilihDetail { List? pemilihModel; int? jumlah; - PemilihDetailModel({this.pemilihModel, this.jumlah}); + PemilihDetail({this.pemilihModel, this.jumlah}); - PemilihDetailModel.fromJson(Map json) { + PemilihDetail.fromJson(Map json) { if (json['data'] != null) { pemilihModel = []; json['data'].forEach((v) { @@ -36,7 +36,9 @@ class PemilihModel { String? nikTimSurvei; String? namaTimSurvei; String? namaCaleg; - String? namaArea; + String? kecamatan; + String? kelurahan; + int? tps; String? createdAt; PemilihModel( @@ -46,7 +48,9 @@ class PemilihModel { this.nikTimSurvei, this.namaTimSurvei, this.namaCaleg, - this.namaArea, + this.kecamatan, + this.kelurahan, + this.tps, this.createdAt}); PemilihModel.fromJson(Map json) { @@ -56,7 +60,9 @@ class PemilihModel { nikTimSurvei = json['nik_tim_survei']; namaTimSurvei = json['nama_tim_survei']; namaCaleg = json['nama_caleg']; - namaArea = json['nama_area']; + kecamatan = json['kecamatan']; + kelurahan = json['kelurahan']; + tps = json['tps']; createdAt = myFunction.convertDateTime(json['created_at']); } @@ -68,7 +74,9 @@ class PemilihModel { data['nik_tim_survei'] = nikTimSurvei; data['nama_tim_survei'] = namaTimSurvei; data['nama_caleg'] = namaCaleg; - data['nama_area'] = namaArea; + data['kecamatan'] = kecamatan; + data['kelurahan'] = kelurahan; + data['tps'] = tps; data['created_at'] = createdAt; return data; } diff --git a/lib/ui/views/login_screen/login_screen_view_model.dart b/lib/ui/views/login_screen/login_screen_view_model.dart index 8afbb62..3779f08 100644 --- a/lib/ui/views/login_screen/login_screen_view_model.dart +++ b/lib/ui/views/login_screen/login_screen_view_model.dart @@ -1,13 +1,11 @@ -// import '../../../app/app.router.dart'; - -import 'package:cek_suara_app/app/app.router.dart'; -import 'package:cek_suara_app/model/my_response.model.dart'; -import 'package:cek_suara_app/model/tim_survei_model.dart'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; +import '../../../app/app.router.dart'; import '../../../app/app.logger.dart'; import '../../../app/core/custom_base_view_model.dart'; +import '../../../model/my_response.model.dart'; +import '../../../model/tim_survei_model.dart'; class LoginScreenViewModel extends CustomBaseViewModel { final log = getLogger('LoginScreenViewModel'); @@ -42,6 +40,7 @@ class LoginScreenViewModel extends CustomBaseViewModel { mySharedPrefs.setString('nama', timSurveiModel.nama!); mySharedPrefs.setString('id_caleg', timSurveiModel.idCaleg.toString()); mySharedPrefs.setString('level', 'tim_survei'); + mySharedPrefs.setString('password', passwordController.text); snackbarService.showSnackbar( message: 'Selamat datang kembali ${timSurveiModel.nama}', title: 'Login Berhasil', diff --git a/lib/ui/views/tim_survei_index_tracking/first_page/first_page_view.dart b/lib/ui/views/tim_survei_index_tracking/first_page/first_page_view.dart index 56d8a7b..e726f6d 100644 --- a/lib/ui/views/tim_survei_index_tracking/first_page/first_page_view.dart +++ b/lib/ui/views/tim_survei_index_tracking/first_page/first_page_view.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; +import 'package:url_launcher/url_launcher.dart'; import '../../../../app/themes/app_colors.dart'; import '../../../../app/themes/app_text.dart'; @@ -124,11 +125,33 @@ class FirstPageView extends StatelessWidget { ], ), ), + GestureDetector( + onTap: () async { + final url = Uri.parse('https://www.kicap-karan.com'); + if (!await launchUrl(url)) { + throw 'Could not launch $url'; + } + }, + child: Text( + 'www.kicap-karan.com', + style: boldTextStyle.copyWith( + color: mainColor, + ), + ), + ), ], ), ), ), ), + floatingActionButton: FloatingActionButton( + backgroundColor: warningColor, + onPressed: () { + model.gantiPassword(); + }, + child: const Icon(Icons.settings, color: fontColor), + ), + floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, ); }, ); diff --git a/lib/ui/views/tim_survei_index_tracking/first_page/first_page_view_model.dart b/lib/ui/views/tim_survei_index_tracking/first_page/first_page_view_model.dart index 760a22d..531db43 100644 --- a/lib/ui/views/tim_survei_index_tracking/first_page/first_page_view_model.dart +++ b/lib/ui/views/tim_survei_index_tracking/first_page/first_page_view_model.dart @@ -1,3 +1,4 @@ +import '../../../../app/app.dialogs.dart'; import '../../../../app/app.logger.dart'; import '../../../../app/core/custom_base_view_model.dart'; import '../../../../model/my_response.model.dart'; @@ -27,4 +28,17 @@ class FirstPageViewModel extends CustomBaseViewModel { setBusy(false); } } + + gantiPassword() async { + var res = await dialogService.showCustomDialog( + variant: DialogType.gantiPasswordDialogView, + title: 'Ganti Password', + mainButtonTitle: 'Simpan', + barrierDismissible: false, + ); + + if (res!.confirmed) { + snackbarService.showSnackbar(message: 'Password berhasil diubah'); + } + } } diff --git a/lib/ui/views/tim_survei_index_tracking/halaman_history/detail_suara_pemilih_bottom_sheet/detail_suara_pemilih_bottom_sheet_view.dart b/lib/ui/views/tim_survei_index_tracking/halaman_history/detail_suara_pemilih_bottom_sheet/detail_suara_pemilih_bottom_sheet_view.dart index 13b8c36..213665b 100644 --- a/lib/ui/views/tim_survei_index_tracking/halaman_history/detail_suara_pemilih_bottom_sheet/detail_suara_pemilih_bottom_sheet_view.dart +++ b/lib/ui/views/tim_survei_index_tracking/halaman_history/detail_suara_pemilih_bottom_sheet/detail_suara_pemilih_bottom_sheet_view.dart @@ -96,6 +96,18 @@ class DetailSuaraPemilihBottomSheetView extends StatelessWidget { title: 'Tanggal/\nWaktu', value: request.data!.createdAt!, ), + _DetailChildWidget( + title: 'Kecamatan', + value: request.data!.kecamatan!, + ), + _DetailChildWidget( + title: 'Kelurahan/\nDesa', + value: request.data!.kelurahan!, + ), + _DetailChildWidget( + title: 'TPS', + value: request.data!.tps!.toString(), + ), ], ), ), diff --git a/lib/ui/views/tim_survei_index_tracking/halaman_history/halaman_history_view.dart b/lib/ui/views/tim_survei_index_tracking/halaman_history/halaman_history_view.dart index e354393..a791c34 100644 --- a/lib/ui/views/tim_survei_index_tracking/halaman_history/halaman_history_view.dart +++ b/lib/ui/views/tim_survei_index_tracking/halaman_history/halaman_history_view.dart @@ -104,8 +104,26 @@ class HalamanHistoryView extends StatelessWidget { model.listPemilih[i].namaPemilih!, style: boldTextStyle, ), - subtitle: Text( - model.listPemilih[i].namaArea!, + subtitle: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: + CrossAxisAlignment.start, + mainAxisAlignment: + MainAxisAlignment.start, + children: [ + CardWidget( + title: 'Kec', + value: model + .listPemilih[i].kecamatan!), + CardWidget( + title: 'Kel / Desa', + value: model + .listPemilih[i].kelurahan!), + CardWidget( + title: 'TPS', + value: model.listPemilih[i].tps! + .toString()), + ], ), trailing: IconButton( icon: const Icon( @@ -151,3 +169,48 @@ class HalamanHistoryView extends StatelessWidget { ); } } + +class CardWidget extends StatelessWidget { + const CardWidget({ + super.key, + required this.title, + required this.value, + }); + + final String title; + final String value; + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Expanded( + flex: 3, + child: Text( + title, + style: italicTextStyle.copyWith( + fontSize: 12, + ), + overflow: TextOverflow.ellipsis, + ), + ), + const Expanded( + flex: 1, + child: Text( + ' : ', + ), + ), + Expanded( + flex: 6, + child: Text( + value, + style: boldTextStyle.copyWith( + fontSize: 12, + ), + overflow: TextOverflow.ellipsis, + ), + ), + ], + ); + } +} diff --git a/lib/ui/views/tim_survei_index_tracking/halaman_history/halaman_history_view_model.dart b/lib/ui/views/tim_survei_index_tracking/halaman_history/halaman_history_view_model.dart index e50060f..02c4da3 100644 --- a/lib/ui/views/tim_survei_index_tracking/halaman_history/halaman_history_view_model.dart +++ b/lib/ui/views/tim_survei_index_tracking/halaman_history/halaman_history_view_model.dart @@ -1,9 +1,8 @@ -import 'package:cek_suara_app/model/my_response.model.dart'; -import 'package:cek_suara_app/model/pemilih_model.dart'; - import '../../../../app/app.bottomsheets.dart'; import '../../../../app/app.logger.dart'; import '../../../../app/core/custom_base_view_model.dart'; +import '../../../../model/my_response.model.dart'; +import '../../../../model/pemilih_model.dart'; class HalamanHistoryViewModel extends CustomBaseViewModel { final log = getLogger('HalamanHistoryViewModel'); @@ -13,6 +12,7 @@ class HalamanHistoryViewModel extends CustomBaseViewModel { String namaCaleg = '...'; Future init() async { + globalVar.backPressed = 'exitApp'; await getData(); } @@ -23,13 +23,13 @@ class HalamanHistoryViewModel extends CustomBaseViewModel { String? nik = await mySharedPrefs.getString('nik'); var response = await httpService.get('tim_survei/$nik'); MyResponseModel myResponseModel = MyResponseModel.fromJson(response.data); - PemilihDetailModel pemilihDetailModel = - PemilihDetailModel.fromJson(myResponseModel.data); - listPemilih = pemilihDetailModel.pemilihModel!; - log.i('listPemilih: $listPemilih'); - namaCaleg = listPemilih[0].namaCaleg!; - counter = pemilihDetailModel.jumlah!; - log.i('counter: $counter'); + PemilihDetail pemilihDetail = + PemilihDetail.fromJson(myResponseModel.data); + listPemilih = pemilihDetail.pemilihModel ?? []; + // log.i('listPemilih: $listPemilih'); + counter = pemilihDetail.jumlah!; + namaCaleg = counter > 0 ? listPemilih[0].namaCaleg! : '...'; + // log.i('counter: $counter'); status = true; } catch (e) { log.e(e.toString()); diff --git a/lib/ui/views/tim_survei_index_tracking/halaman_pengaturan/ganti_password_dialog/ganti_password_dialog_view.dart b/lib/ui/views/tim_survei_index_tracking/halaman_pengaturan/ganti_password_dialog/ganti_password_dialog_view.dart new file mode 100644 index 0000000..1832fe8 --- /dev/null +++ b/lib/ui/views/tim_survei_index_tracking/halaman_pengaturan/ganti_password_dialog/ganti_password_dialog_view.dart @@ -0,0 +1,190 @@ +import 'package:flutter/material.dart'; +import 'package:stacked/stacked.dart'; +import 'package:stacked_services/stacked_services.dart'; +import 'package:validatorless/validatorless.dart'; + +import '../../../../../app/themes/app_colors.dart'; +import '../../../../../app/themes/app_text.dart'; +import '../../../../widgets/my_textformfield.dart'; +import './ganti_password_dialog_view_model.dart'; + +class GantiPasswordDialogView extends StatelessWidget { + final DialogRequest? request; + final Function(DialogResponse)? completer; + + const GantiPasswordDialogView({ + Key? key, + this.request, + this.completer, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return ViewModelBuilder.reactive( + viewModelBuilder: () => GantiPasswordDialogViewModel(), + onViewModelReady: (GantiPasswordDialogViewModel model) async { + await model.init(); + }, + builder: ( + BuildContext context, + GantiPasswordDialogViewModel model, + Widget? child, + ) { + return Dialog( + child: Container( + padding: const EdgeInsets.all(20), + child: Form( + key: model.globalKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Ganti Password', + style: boldTextStyle.copyWith(fontSize: 18), + ), + const SizedBox(height: 20), + MyTextFormField( + labelText: 'Password Lama', + hintText: 'Masukkan password lama', + obscureText: model.isPasswordLamaObscure, + suffixIcon: IconButton( + onPressed: () { + model.isPasswordLamaObscure = + !model.isPasswordLamaObscure; + model.notifyListeners(); + }, + icon: Icon( + model.isPasswordLamaObscure + ? Icons.visibility_off + : Icons.visibility, + ), + ), + controller: model.passwordLamaController, + validator: Validatorless.multiple( + [ + Validatorless.required( + 'Password lama tidak boleh kosong'), + Validatorless.min( + 8, 'Password lama minimal 8 karakter'), + Validatorless.compare( + model.thePasswordLamaController, + 'Password lama tidak sama', + ), + ], + ), + ), + const SizedBox(height: 10), + MyTextFormField( + labelText: 'Password Baru', + hintText: 'Masukkan password baru', + obscureText: model.isPasswordBaruObscure, + controller: model.passwordBaruController, + suffixIcon: IconButton( + onPressed: () { + model.isPasswordBaruObscure = + !model.isPasswordBaruObscure; + model.notifyListeners(); + }, + icon: Icon( + model.isPasswordBaruObscure + ? Icons.visibility_off + : Icons.visibility, + ), + ), + validator: Validatorless.multiple( + [ + Validatorless.required( + 'Password baru tidak boleh kosong'), + Validatorless.min( + 8, 'Password baru minimal 8 karakter'), + ], + ), + ), + const SizedBox(height: 10), + MyTextFormField( + labelText: 'Konfirmasi Password Baru', + hintText: 'Masukkan konfirmasi password baru', + obscureText: model.isKonfirmasiPasswordBaruObscure, + controller: model.konfirmasiPasswordBaruController, + suffixIcon: IconButton( + onPressed: () { + model.isKonfirmasiPasswordBaruObscure = + !model.isKonfirmasiPasswordBaruObscure; + model.notifyListeners(); + }, + icon: Icon( + model.isKonfirmasiPasswordBaruObscure + ? Icons.visibility_off + : Icons.visibility, + ), + ), + validator: Validatorless.multiple( + [ + Validatorless.required( + 'Konfirmasi password baru tidak boleh kosong'), + Validatorless.min( + 8, 'Konfirmasi password baru minimal 8 karakter'), + Validatorless.compare( + model.passwordBaruController, + 'Password baru tidak sama', + ), + ], + ), + ), + const SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + onPressed: () { + if (model.globalKey.currentState!.validate()) { + // remove keyboard + FocusScope.of(context).unfocus(); + + model.dialogService + .showConfirmationDialog( + title: 'Konfirmasi', + description: + 'Apakah anda yakin ingin mengubah password?', + cancelTitle: 'Batal', + confirmationTitle: 'Ya', + ) + .then( + (response) async { + if (response!.confirmed) { + // completer!(DialogResponse(confirmed: true)); + // model.log.i('Password berhasil diubah'); + bool res = await model.gantiPassword(); + // model.log.i('res: $res'); + if (res) { + completer!(DialogResponse(confirmed: true)); + model.log.i('Password berhasil diubah'); + } + } else { + model.log.i('Password gagal diubah'); + } + }, + ); + } + }, + child: const Text('Simpan'), + ), + TextButton( + onPressed: () => + completer!(DialogResponse(confirmed: false)), + child: const Text( + 'Batal', + style: TextStyle(color: dangerColor), + ), + ), + ], + ), + ], + ), + ), + ), + ); + }, + ); + } +} diff --git a/lib/ui/views/tim_survei_index_tracking/halaman_pengaturan/ganti_password_dialog/ganti_password_dialog_view_model.dart b/lib/ui/views/tim_survei_index_tracking/halaman_pengaturan/ganti_password_dialog/ganti_password_dialog_view_model.dart new file mode 100644 index 0000000..f1a3264 --- /dev/null +++ b/lib/ui/views/tim_survei_index_tracking/halaman_pengaturan/ganti_password_dialog/ganti_password_dialog_view_model.dart @@ -0,0 +1,51 @@ +import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; + +import '../../../../../app/app.logger.dart'; +import '../../../../../app/core/custom_base_view_model.dart'; + +class GantiPasswordDialogViewModel extends CustomBaseViewModel { + final log = getLogger('GantiPasswordDialogViewModel'); + + // form variable + final globalKey = GlobalKey(); + TextEditingController passwordLamaController = TextEditingController(); + TextEditingController passwordBaruController = TextEditingController(); + TextEditingController konfirmasiPasswordBaruController = + TextEditingController(); + + TextEditingController thePasswordLamaController = TextEditingController(); + + bool isPasswordLamaObscure = true; + bool isPasswordBaruObscure = true; + bool isKonfirmasiPasswordBaruObscure = true; + + // String? passwordLama; + + Future init() async { + globalVar.backPressed = 'exitApp'; + String? passwordLama = await mySharedPrefs.getString('password'); + thePasswordLamaController.text = passwordLama!; + } + + Future gantiPassword() async { + setBusy(true); + + try { + String? nik = await mySharedPrefs.getString('nik'); + var formData = FormData.fromMap({ + 'nik': nik, + 'password_lama': passwordLamaController.text, + 'password_baru': passwordBaruController.text, + }); + await httpService.postWithFormData( + 'login/ganti_pass_tim_survei', formData); + return true; + } catch (e) { + log.e(e.toString()); + return false; + } finally { + setBusy(false); + } + } +} diff --git a/lib/ui/views/tim_survei_index_tracking/halaman_pengaturan/halaman_pengaturan_view.dart b/lib/ui/views/tim_survei_index_tracking/halaman_pengaturan/halaman_pengaturan_view.dart index 28a8a31..44073f2 100644 --- a/lib/ui/views/tim_survei_index_tracking/halaman_pengaturan/halaman_pengaturan_view.dart +++ b/lib/ui/views/tim_survei_index_tracking/halaman_pengaturan/halaman_pengaturan_view.dart @@ -1,7 +1,7 @@ -import 'package:cek_suara_app/ui/views/tim_survei_index_tracking/first_page/first_page_view.dart'; import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; +import '../first_page/first_page_view.dart'; import './halaman_pengaturan_view_model.dart'; class HalamanPengaturanView extends StatelessWidget { diff --git a/lib/ui/views/tim_survei_index_tracking/halaman_survei/bottom_sheet_cari_area/bottom_sheet_cari_area_view.dart b/lib/ui/views/tim_survei_index_tracking/halaman_survei/bottom_sheet_cari_area/bottom_sheet_cari_area_view.dart index c7b1752..faf7531 100644 --- a/lib/ui/views/tim_survei_index_tracking/halaman_survei/bottom_sheet_cari_area/bottom_sheet_cari_area_view.dart +++ b/lib/ui/views/tim_survei_index_tracking/halaman_survei/bottom_sheet_cari_area/bottom_sheet_cari_area_view.dart @@ -1,8 +1,8 @@ -import 'package:cek_suara_app/ui/widgets/my_textformfield.dart'; import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; import 'package:stacked_services/stacked_services.dart'; +import '../../../../widgets/my_textformfield.dart'; import './bottom_sheet_cari_area_view_model.dart'; class BottomSheetCariAreaView extends StatelessWidget { diff --git a/lib/ui/views/tim_survei_index_tracking/halaman_survei/halaman_survei_view.dart b/lib/ui/views/tim_survei_index_tracking/halaman_survei/halaman_survei_view.dart index 5d1ea5e..6a7268a 100644 --- a/lib/ui/views/tim_survei_index_tracking/halaman_survei/halaman_survei_view.dart +++ b/lib/ui/views/tim_survei_index_tracking/halaman_survei/halaman_survei_view.dart @@ -120,11 +120,11 @@ class HalamanSurveiView extends StatelessWidget { ), const SizedBox(height: 20), const Text( - ' Pilih Area', + ' Pilih Kecamatan', ), if (model.isBusy) const Center(child: CircularProgressIndicator()), - if (!model.isBusy && model.selectedArea != null) + if (!model.isBusy && model.selectedKecamatan != null) Container( width: double.infinity, height: 60, @@ -135,49 +135,100 @@ class HalamanSurveiView extends StatelessWidget { color: sixthGrey, ), ), - child: Row( - children: [ - // icon search - IconButton( - onPressed: () async => - await model.searchArea(), - icon: const Icon( - Icons.search, - color: sixthGrey, - ), - ), - const SizedBox(width: 10), - Expanded( - child: DropdownButtonHideUnderline( - child: DropdownButton( - isExpanded: true, - value: model.selectedArea!, - icon: const Icon(Icons.arrow_drop_down), - iconSize: 24, - elevation: 16, - style: const TextStyle( - color: Colors.black), - onChanged: (String? newValue) { - model.log.i(newValue); - model.selectedArea = newValue!; - // model.changeArea(newValue); - model.notifyListeners(); - }, - items: model.listAreaString - .map>( - (String value) { - return DropdownMenuItem( - value: value, - child: Text(value, - overflow: - TextOverflow.ellipsis), - ); - }).toList()), - ), - ), - ], + child: DropdownButtonHideUnderline( + child: DropdownButton( + isExpanded: true, + value: model.selectedKecamatan!, + icon: const Icon(Icons.arrow_drop_down), + iconSize: 24, + elevation: 16, + style: const TextStyle(color: Colors.black), + onChanged: (String? newValue) { + model.log.i(newValue); + model.selectedKecamatan = newValue!; + String kecamatanId = model + .listKecamatanModel[model + .listKecamatanString + .indexOf(newValue)] + .kecamatanId!; + model.getKelurahan(kecamatanId); + // model.changeArea(newValue); + model.notifyListeners(); + }, + items: model.listKecamatanString + .map>( + (String value) { + return DropdownMenuItem( + value: value, + child: Text(value, + overflow: TextOverflow.ellipsis), + ); + }).toList()), ), ), + + const SizedBox(height: 20), + const Text( + ' Pilih Kelurahan / Desa', + ), + if (model.isBusy) + const Center(child: CircularProgressIndicator()), + if (!model.isBusy && model.selectedKelurahan != null) + Container( + width: double.infinity, + height: 60, + padding: const EdgeInsets.symmetric(horizontal: 10), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(25), + border: Border.all( + color: sixthGrey, + ), + ), + child: DropdownButtonHideUnderline( + child: DropdownButton( + isExpanded: true, + value: model.selectedKelurahan!, + icon: const Icon(Icons.arrow_drop_down), + iconSize: 24, + elevation: 16, + style: const TextStyle(color: Colors.black), + onChanged: (String? newValue) { + model.log.i(newValue); + model.selectedKelurahan = newValue!; + // model.changeArea(newValue); + model.notifyListeners(); + }, + items: model.listKelurahanString + .map>( + (String value) { + return DropdownMenuItem( + value: value, + child: Text(value, + overflow: TextOverflow.ellipsis), + ); + }).toList()), + ), + ), + + const SizedBox(height: 20), + MyTextFormField( + hintText: 'Masukkan No TPS', + labelText: 'Nomor TPS', + maxLength: 2, + suffixIcon: const Icon(Icons.account_balance), + controller: model.noTPScontroller, + keyboardType: TextInputType.number, + validator: Validatorless.multiple( + [ + Validatorless.required( + 'Nomor TPS tidak boleh kosong'), + Validatorless.number('Nomor TPS harus angka'), + Validatorless.min(1, 'Nomor TPS minimal 1 digit'), + Validatorless.max( + 2, 'Nomor TPS maksimal 2 digit'), + ], + ), + ), // const SizedBox(height: 20), // const Text( // ' Pilih Caleg', diff --git a/lib/ui/views/tim_survei_index_tracking/halaman_survei/halaman_survei_view_model.dart b/lib/ui/views/tim_survei_index_tracking/halaman_survei/halaman_survei_view_model.dart index 13b8442..488fce3 100644 --- a/lib/ui/views/tim_survei_index_tracking/halaman_survei/halaman_survei_view_model.dart +++ b/lib/ui/views/tim_survei_index_tracking/halaman_survei/halaman_survei_view_model.dart @@ -4,7 +4,6 @@ import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:http_parser/http_parser.dart'; import 'package:image_picker/image_picker.dart'; -import '../../../../app/app.bottomsheets.dart'; import '../../../../app/app.logger.dart'; import '../../../../app/core/custom_base_view_model.dart'; import '../../../../model/area_model.dart'; @@ -17,6 +16,7 @@ class HalamanSurveiViewModel extends CustomBaseViewModel { final formKey = GlobalKey(); TextEditingController ktpController = TextEditingController(); TextEditingController namaController = TextEditingController(); + TextEditingController noTPScontroller = TextEditingController(); // image picker String? _imagePath; @@ -25,11 +25,14 @@ class HalamanSurveiViewModel extends CustomBaseViewModel { Uint8List? imageBytes; // area - List listAreaModel = []; - List listAreaString = []; - List allListAreaModel = []; - String? selectedArea; - int areaIndex = 0; + List listKecamatanModel = []; + List listKecamatanString = []; + List listKelurahanModel = []; + List listKelurahanString = []; + // List allListKecamatanModel = []; + String? selectedKecamatan; + String? selectedKelurahan; + // int areaIndex = 0; // // caleg // List listCalegModel = []; @@ -59,17 +62,31 @@ class HalamanSurveiViewModel extends CustomBaseViewModel { // String? nik = await mySharedPrefs.getString('nik'); var response = await httpService.get('area/cek_area/$nik'); - log.i(response.data); + // log.i(response.data); MyResponseModel myResponseModel = MyResponseModel.fromJson(response.data); - AreaListModel areaListModel = - AreaListModel.fromJson(myResponseModel.data); - listAreaModel = areaListModel.area!; - allListAreaModel = areaListModel.area!; - for (var element in listAreaModel) { - listAreaString.add(element.namaArea!); + KecamatanDetail kecamatanDetail = + KecamatanDetail.fromJson(myResponseModel.data); + + listKecamatanModel = kecamatanDetail.kecamatan!; + selectedKecamatan = listKecamatanModel[0].name!; + for (var element in listKecamatanModel) { + listKecamatanString.add(element.name!); } - selectedArea = listAreaString[0]; - // int idArea = listAreaModel[0].idArea!; + + // log.i('kecamatanDetail: ${kecamatanDetail.kecamatan}'); + + String idKecamatan = listKecamatanModel[0].kecamatanId!; + await getKelurahan(idKecamatan); + + // AreaListModel areaListModel = + // AreaListModel.fromJson(myResponseModel.data); + // listKecamatanModel = areaListModel.area!; + // allListKecamatanModel = areaListModel.area!; + // for (var element in listKecamatanModel) { + // listKecamatanString.add(element.namaArea!); + // } + // selectedKecamatan = listKecamatanString[0]; + // // int idArea = listKecamatanModel[0].idArea!; // await getCaleg(idArea); // getCaleg() @@ -81,40 +98,30 @@ class HalamanSurveiViewModel extends CustomBaseViewModel { } } - // getCaleg(int idArea) async { - // log.i('getCaleg'); - // log.i('idArea: $idArea'); - // selectedCaleg = null; - // listCalegModel = []; - // listCalegString = []; - // setBusy(true); - // try { - // var response = await httpService.get('caleg/area/$idArea'); - // log.i(response.data); - // MyResponseModel myResponseModel = MyResponseModel.fromJson(response.data); - // // log.i(myResponseModel.data); - // CalegListModel calegListModel = - // CalegListModel.fromJson(myResponseModel.data); - // listCalegModel = calegListModel.caleg!; - // for (var element in listCalegModel) { - // listCalegString.add(element.namaCaleg!); - // } - // selectedCaleg = listCalegString[0]; - // // log.i('listCalegModel: $listCalegModel'); - // // log.i('listCalegString: $listCalegString'); - // // log.i('selectedCaleg: $selectedCaleg'); - // } catch (e) { - // log.e(e); - // } finally { - // setBusy(false); - // } - // } - - // changeArea(String? value) async { - // int idArea = listAreaModel[listAreaString.indexOf(value!)].idArea!; - // // log.i('idArea: $idArea'); - // await getCaleg(idArea); - // } + getKelurahan(String idKecamatan) async { + log.i('getKelurahan'); + listKelurahanModel = []; + listKelurahanString = []; + selectedKelurahan = null; + setBusy(true); + try { + var response = await httpService.get('area/kelurahan/$idKecamatan'); + // log.i(response.data); + MyResponseModel myResponseModel = MyResponseModel.fromJson(response.data); + KelurahanDetail kelurahanDetail = + KelurahanDetail.fromJson(myResponseModel.data); + // log.i('kelurahanDetail: ${kelurahanDetail.kelurahan}'); + listKelurahanModel = kelurahanDetail.kelurahan!; + for (var element in listKelurahanModel) { + listKelurahanString.add(element.name!); + } + selectedKelurahan = listKelurahanString[0]; + } catch (e) { + log.e(e); + } finally { + setBusy(false); + } + } void addImage() async { try { @@ -132,48 +139,20 @@ class HalamanSurveiViewModel extends CustomBaseViewModel { } } - searchArea() async { - var res = await bottomSheetService.showCustomSheet( - variant: BottomSheetType.bottomSheetCariAreaView, - ignoreSafeArea: false, - isScrollControlled: true, - ); - - if (res!.confirmed) { - log.i('res.data: ${res.data}'); - - String area = res.data; - if (area == '') { - listAreaModel = allListAreaModel; - } else { - listAreaModel = []; - for (var element in allListAreaModel) { - if (element.namaArea!.toLowerCase().contains(area.toLowerCase())) { - listAreaModel.add(element); - } - } - } - - listAreaString = []; - for (var element in listAreaModel) { - listAreaString.add(element.namaArea!); - } - selectedArea = listAreaString[0]; - // int idArea = listAreaModel[0].idArea!; - // await getCaleg(idArea); - notifyListeners(); - } - } - uploadData() async { log.i('uploadData'); setBusy(true); easyLoading.customLoading('Uploading data...'); globalVar.backPressed = 'cantBack'; try { - String idArea = listAreaModel[listAreaString.indexOf(selectedArea!)] - .idArea - .toString(); + String idKecamatan = + listKecamatanModel[listKecamatanString.indexOf(selectedKecamatan!)] + .kecamatanId + .toString(); + String idKelurahan = + listKelurahanModel[listKelurahanString.indexOf(selectedKelurahan!)] + .kelurahanId + .toString(); var fomData = FormData.fromMap( { 'ktp': ktpController.text, @@ -184,8 +163,10 @@ class HalamanSurveiViewModel extends CustomBaseViewModel { filename: imageFile!.name, contentType: MediaType('image', 'jpg'), ), - 'idArea': idArea, + 'idKecamatan': idKecamatan, + 'idKelurahan': idKelurahan, 'nik': nik, + 'noTPS': noTPScontroller.text, }, ); await httpService.postWithFormData('tim_survei', fomData); @@ -200,6 +181,7 @@ class HalamanSurveiViewModel extends CustomBaseViewModel { _imagePath = null; imageFile = null; imageBytes = null; + noTPScontroller.clear(); notifyListeners(); } catch (e) { log.e(e); diff --git a/lib/ui/views/tim_survei_index_tracking/tim_survei_index_tracking_view.dart b/lib/ui/views/tim_survei_index_tracking/tim_survei_index_tracking_view.dart index 3142611..3f4f06f 100644 --- a/lib/ui/views/tim_survei_index_tracking/tim_survei_index_tracking_view.dart +++ b/lib/ui/views/tim_survei_index_tracking/tim_survei_index_tracking_view.dart @@ -26,6 +26,26 @@ class TimSurveiIndexTrackingView extends StatelessWidget { ) { return SafeArea( child: Scaffold( + appBar: AppBar( + title: Text( + model.header, + style: const TextStyle( + color: fontColor, + fontSize: 20, + ), + ), + backgroundColor: warningColor, + elevation: 0, + automaticallyImplyLeading: false, + actions: [ + IconButton( + onPressed: () { + model.logout(); + }, + icon: const Icon(Icons.logout, color: fontColor), + ), + ], + ), extendBody: false, body: ExtendedNavigator( router: TimSurveiIndexTrackingViewRouter(), diff --git a/lib/ui/views/tim_survei_index_tracking/tim_survei_index_tracking_view_model.dart b/lib/ui/views/tim_survei_index_tracking/tim_survei_index_tracking_view_model.dart index 6a93c53..736b8b0 100644 --- a/lib/ui/views/tim_survei_index_tracking/tim_survei_index_tracking_view_model.dart +++ b/lib/ui/views/tim_survei_index_tracking/tim_survei_index_tracking_view_model.dart @@ -14,6 +14,7 @@ class TimSurveiIndexTrackingViewModel extends IndexTrackingViewModel { final navigationService = locator(); final snackbarService = locator(); final globalVar = locator(); + final dialogService = locator(); final _bottomNavBarList = [ { @@ -68,4 +69,20 @@ class TimSurveiIndexTrackingViewModel extends IndexTrackingViewModel { id: 5, ); } + + logout() async { + dialogService + .showConfirmationDialog( + title: 'Konfirmasi', + description: 'Apakah anda yakin ingin keluar?', + cancelTitle: 'Batal', + confirmationTitle: 'Keluar', + ) + .then((value) async { + if (value!.confirmed) { + await mySharedPrefs.clear(); + navigationService.clearStackAndShow(Routes.loginScreenView); + } + }); + } } diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 64a0ece..7299b5c 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -7,9 +7,13 @@ #include "generated_plugin_registrant.h" #include +#include void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); file_selector_plugin_register_with_registrar(file_selector_linux_registrar); + g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); + url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); } diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 2db3c22..786ff5c 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST file_selector_linux + url_launcher_linux ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 4b4e1ac..d993eb7 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -8,9 +8,11 @@ import Foundation import file_selector_macos import path_provider_foundation import shared_preferences_foundation +import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) + UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index 6ea4ff5..ced18d2 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -877,6 +877,70 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.2" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + sha256: "47e208a6711459d813ba18af120d9663c20bdf6985d6ad39fe165d2538378d27" + url: "https://pub.dev" + source: hosted + version: "6.1.14" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + sha256: "31222ffb0063171b526d3e569079cf1f8b294075ba323443fdc690842bfd4def" + url: "https://pub.dev" + source: hosted + version: "6.2.0" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + sha256: "4ac97281cf60e2e8c5cc703b2b28528f9b50c8f7cebc71df6bdf0845f647268a" + url: "https://pub.dev" + source: hosted + version: "6.2.0" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: "9f2d390e096fdbe1e6e6256f97851e51afc2d9c423d3432f1d6a02a8a9a8b9fd" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234 + url: "https://pub.dev" + source: hosted + version: "3.1.0" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "980e8d9af422f477be6948bdfb68df8433be71f5743a188968b0c1b887807e50" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: ba140138558fcc3eead51a1c42e92a9fb074a1b1149ed3c73e66035b2ccd94f2 + url: "https://pub.dev" + source: hosted + version: "2.0.19" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: "7754a1ad30ee896b265f8d14078b0513a4dba28d358eabb9d5f339886f4a1adc" + url: "https://pub.dev" + source: hosted + version: "3.1.0" validatorless: dependency: "direct main" description: @@ -935,4 +999,4 @@ packages: version: "3.1.2" sdks: dart: ">=3.1.0-163.1.beta <4.0.0" - flutter: ">=3.7.0" + flutter: ">=3.10.0" diff --git a/pubspec.yaml b/pubspec.yaml index 4dfea00..c209e44 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -42,17 +42,13 @@ dependencies: path_provider: ^2.0.9 dio: flutter_easyloading: - # location: ^4.4.0 - # flutter_inappwebview: - # webview_flutter: ^3.0.4 - # google_fonts: - # flutter_svg: stylish_bottom_bar: validatorless: ^1.2.3 http_parser: intl: shared_preferences: easy_image_viewer: + url_launcher: dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 77ab7a0..043a96f 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -7,8 +7,11 @@ #include "generated_plugin_registrant.h" #include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { FileSelectorWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("FileSelectorWindows")); + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index a423a02..a95e267 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST file_selector_windows + url_launcher_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST