From 949474b8ef1b3aa0307c37364144e7f082f42a2d Mon Sep 17 00:00:00 2001 From: kicap Date: Thu, 18 May 2023 16:04:03 +0800 Subject: [PATCH] added other --- .env | 4 +- lib/app/app.dart | 6 + lib/app/app.dialogs.dart | 25 ++ lib/app/app.router.dart | 96 ++++++-- lib/main.dart | 3 +- lib/services/http_services.dart | 3 + .../add_siswa_dialog_view.dart | 229 ++++++++++++++++++ .../add_siswa_dialog_view_model.dart | 113 +++++++++ .../admin_index/admin_index_view.dart | 7 +- .../admin_index_tracking_view_model.dart | 6 + .../dana_sosial_admin_view.dart | 166 ++++++------- .../dana_sosial_admin_view_model.dart | 8 +- .../data_siswa/data_siswa_view.dart | 103 +++++++- .../data_siswa/data_siswa_view_model.dart | 14 ++ .../views/login_screen/login_screen_view.dart | 100 ++++---- .../tambah_dana_sosial_view.dart | 134 ++++++++++ .../tambah_dana_sosial_view_model.dart | 31 +++ lib/ui/widgets/my_textformfield.dart | 9 + pubspec.lock | 24 ++ pubspec.yaml | 3 + 20 files changed, 913 insertions(+), 171 deletions(-) create mode 100644 lib/app/app.dialogs.dart create mode 100644 lib/ui/views/admin_index_tracking/add_siswa_dialog/add_siswa_dialog/add_siswa_dialog_view.dart create mode 100644 lib/ui/views/admin_index_tracking/add_siswa_dialog/add_siswa_dialog/add_siswa_dialog_view_model.dart create mode 100644 lib/ui/views/tambah_dana_sosial/tambah_dana_sosial_view.dart create mode 100644 lib/ui/views/tambah_dana_sosial/tambah_dana_sosial_view_model.dart diff --git a/.env b/.env index a6b97f1..9bf3caa 100644 --- a/.env +++ b/.env @@ -1,2 +1,2 @@ -url = 'http://20.20.20.25/perumahan/' -api_url = 'http://20.20.20.25/perumahan/api/' \ No newline at end of file +url = 'http://20.20.20.25/panti_asuhan2/' +api_url = 'http://20.20.20.25/panti_asuhan2/api/' \ No newline at end of file diff --git a/lib/app/app.dart b/lib/app/app.dart index 1b49f16..ea5aef1 100644 --- a/lib/app/app.dart +++ b/lib/app/app.dart @@ -2,11 +2,13 @@ import 'package:panti_asuhan/ui/views/admin_index_tracking/admin_index/admin_ind import 'package:panti_asuhan/ui/views/admin_index_tracking/dana_sosial_admin/dana_sosial_admin_view.dart'; import 'package:panti_asuhan/ui/views/admin_index_tracking/data_siswa/data_siswa_view.dart'; import 'package:panti_asuhan/ui/views/admin_index_tracking/profil/profil_view.dart'; +import 'package:panti_asuhan/ui/views/tambah_dana_sosial/tambah_dana_sosial_view.dart'; import 'package:stacked_services/stacked_services.dart'; import 'package:stacked/stacked_annotations.dart'; import '../services/http_services.dart'; import '../services/my_easyloading.dart'; +import '../ui/views/admin_index_tracking/add_siswa_dialog/add_siswa_dialog/add_siswa_dialog_view.dart'; import '../ui/views/admin_index_tracking/admin_index_tracking_view.dart'; import '../ui/views/login_screen/login_screen_view.dart'; import '../ui/views/splash_screen/splash_screen_view.dart'; @@ -24,6 +26,10 @@ import '../ui/views/splash_screen/splash_screen_view.dart'; MaterialRoute(page: ProfilView), ], ), + MaterialRoute(page: TambahDanaSosialView), + ], + dialogs: [ + StackedDialog(classType: AddSiswaDialogView), ], dependencies: [ LazySingleton(classType: NavigationService), diff --git a/lib/app/app.dialogs.dart b/lib/app/app.dialogs.dart new file mode 100644 index 0000000..0557da3 --- /dev/null +++ b/lib/app/app.dialogs.dart @@ -0,0 +1,25 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +// ************************************************************************** +// StackedDialogGenerator +// ************************************************************************** + +import 'package:stacked_services/stacked_services.dart'; + +import 'app.locator.dart'; +import '../ui/views/admin_index_tracking/add_siswa_dialog/add_siswa_dialog/add_siswa_dialog_view.dart'; + +enum DialogType { + addSiswaDialogView, +} + +void setupDialogUi() { + final dialogService = locator(); + + final Map builders = { + DialogType.addSiswaDialogView: (context, request, completer) => + AddSiswaDialogView(request: request, completer: completer), + }; + + dialogService.registerCustomDialogBuilders(builders); +} diff --git a/lib/app/app.router.dart b/lib/app/app.router.dart index 3c15539..0649b37 100644 --- a/lib/app/app.router.dart +++ b/lib/app/app.router.dart @@ -5,24 +5,26 @@ // ************************************************************************** // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'package:flutter/material.dart' as _i5; +import 'package:flutter/material.dart' as _i6; import 'package:flutter/material.dart'; import 'package:panti_asuhan/ui/views/admin_index_tracking/admin_index/admin_index_view.dart' - as _i6; + as _i7; import 'package:panti_asuhan/ui/views/admin_index_tracking/admin_index_tracking_view.dart' as _i4; import 'package:panti_asuhan/ui/views/admin_index_tracking/dana_sosial_admin/dana_sosial_admin_view.dart' - as _i7; -import 'package:panti_asuhan/ui/views/admin_index_tracking/data_siswa/data_siswa_view.dart' as _i8; -import 'package:panti_asuhan/ui/views/admin_index_tracking/profil/profil_view.dart' +import 'package:panti_asuhan/ui/views/admin_index_tracking/data_siswa/data_siswa_view.dart' as _i9; +import 'package:panti_asuhan/ui/views/admin_index_tracking/profil/profil_view.dart' + as _i10; import 'package:panti_asuhan/ui/views/login_screen/login_screen_view.dart' as _i3; import 'package:panti_asuhan/ui/views/splash_screen/splash_screen_view.dart' as _i2; +import 'package:panti_asuhan/ui/views/tambah_dana_sosial/tambah_dana_sosial_view.dart' + as _i5; import 'package:stacked/stacked.dart' as _i1; -import 'package:stacked_services/stacked_services.dart' as _i10; +import 'package:stacked_services/stacked_services.dart' as _i11; class Routes { static const splashScreenView = '/'; @@ -31,10 +33,13 @@ class Routes { static const adminIndexTrackingView = '/admin-index-tracking-view'; + static const tambahDanaSosialView = '/tambah-dana-sosial-view'; + static const all = { splashScreenView, loginScreenView, adminIndexTrackingView, + tambahDanaSosialView, }; } @@ -52,30 +57,41 @@ class StackedRouter extends _i1.RouterBase { Routes.adminIndexTrackingView, page: _i4.AdminIndexTrackingView, ), + _i1.RouteDef( + Routes.tambahDanaSosialView, + page: _i5.TambahDanaSosialView, + ), ]; final _pagesMap = { _i2.SplashScreenView: (data) { - return _i5.MaterialPageRoute( + return _i6.MaterialPageRoute( builder: (context) => const _i2.SplashScreenView(), settings: data, maintainState: false, ); }, _i3.LoginScreenView: (data) { - return _i5.MaterialPageRoute( + return _i6.MaterialPageRoute( builder: (context) => const _i3.LoginScreenView(), settings: data, maintainState: false, ); }, _i4.AdminIndexTrackingView: (data) { - return _i5.MaterialPageRoute( + return _i6.MaterialPageRoute( builder: (context) => const _i4.AdminIndexTrackingView(), settings: data, maintainState: false, ); }, + _i5.TambahDanaSosialView: (data) { + return _i6.MaterialPageRoute( + builder: (context) => _i5.TambahDanaSosialView(), + settings: data, + maintainState: false, + ); + }, }; @override @@ -105,47 +121,47 @@ class AdminIndexTrackingViewRouter extends _i1.RouterBase { final _routes = <_i1.RouteDef>[ _i1.RouteDef( AdminIndexTrackingViewRoutes.adminIndexView, - page: _i6.AdminIndexView, + page: _i7.AdminIndexView, ), _i1.RouteDef( AdminIndexTrackingViewRoutes.danaSosialAdminView, - page: _i7.DanaSosialAdminView, + page: _i8.DanaSosialAdminView, ), _i1.RouteDef( AdminIndexTrackingViewRoutes.dataSiswaView, - page: _i8.DataSiswaView, + page: _i9.DataSiswaView, ), _i1.RouteDef( AdminIndexTrackingViewRoutes.profilView, - page: _i9.ProfilView, + page: _i10.ProfilView, ), ]; final _pagesMap = { - _i6.AdminIndexView: (data) { - return _i5.MaterialPageRoute( - builder: (context) => const _i6.AdminIndexView(), + _i7.AdminIndexView: (data) { + return _i6.MaterialPageRoute( + builder: (context) => const _i7.AdminIndexView(), settings: data, maintainState: false, ); }, - _i7.DanaSosialAdminView: (data) { - return _i5.MaterialPageRoute( - builder: (context) => const _i7.DanaSosialAdminView(), + _i8.DanaSosialAdminView: (data) { + return _i6.MaterialPageRoute( + builder: (context) => const _i8.DanaSosialAdminView(), settings: data, maintainState: false, ); }, - _i8.DataSiswaView: (data) { - return _i5.MaterialPageRoute( - builder: (context) => const _i8.DataSiswaView(), + _i9.DataSiswaView: (data) { + return _i6.MaterialPageRoute( + builder: (context) => const _i9.DataSiswaView(), settings: data, maintainState: false, ); }, - _i9.ProfilView: (data) { - return _i5.MaterialPageRoute( - builder: (context) => const _i9.ProfilView(), + _i10.ProfilView: (data) { + return _i6.MaterialPageRoute( + builder: (context) => const _i10.ProfilView(), settings: data, maintainState: false, ); @@ -158,7 +174,7 @@ class AdminIndexTrackingViewRouter extends _i1.RouterBase { Map get pagesMap => _pagesMap; } -extension NavigatorStateExtension on _i10.NavigationService { +extension NavigatorStateExtension on _i11.NavigationService { Future navigateToSplashScreenView([ int? routerId, bool preventDuplicates = true, @@ -201,6 +217,20 @@ extension NavigatorStateExtension on _i10.NavigationService { transition: transition); } + Future navigateToTambahDanaSosialView([ + int? routerId, + bool preventDuplicates = true, + Map? parameters, + Widget Function(BuildContext, Animation, Animation, Widget)? + transition, + ]) async { + return navigateTo(Routes.tambahDanaSosialView, + id: routerId, + preventDuplicates: preventDuplicates, + parameters: parameters, + transition: transition); + } + Future navigateToNestedAdminIndexViewInAdminIndexTrackingViewRouter([ int? routerId, bool preventDuplicates = true, @@ -300,6 +330,20 @@ extension NavigatorStateExtension on _i10.NavigationService { transition: transition); } + Future replaceWithTambahDanaSosialView([ + int? routerId, + bool preventDuplicates = true, + Map? parameters, + Widget Function(BuildContext, Animation, Animation, Widget)? + transition, + ]) async { + return replaceWith(Routes.tambahDanaSosialView, + id: routerId, + preventDuplicates: preventDuplicates, + parameters: parameters, + transition: transition); + } + Future replaceWithNestedAdminIndexViewInAdminIndexTrackingViewRouter([ int? routerId, diff --git a/lib/main.dart b/lib/main.dart index fe23d96..12f19db 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,6 +3,7 @@ import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:stacked_services/stacked_services.dart'; +import 'app/app.dialogs.dart'; import 'app/app.locator.dart'; import 'app/app.router.dart'; import 'app/themes/app_theme.dart'; @@ -33,7 +34,7 @@ class MyApp extends StatelessWidget { Future setupAllLocator() async { await setupLocator(); - // setupDialogUi(); + setupDialogUi(); // setupBottomsheetUi(); // setupSnackbarUi(); } diff --git a/lib/services/http_services.dart b/lib/services/http_services.dart index 66abb02..0544446 100644 --- a/lib/services/http_services.dart +++ b/lib/services/http_services.dart @@ -1,7 +1,10 @@ import 'package:dio/dio.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; +import '../app/app.logger.dart'; + class MyHttpServices { + final log = getLogger('MyHttpServices'); final _options = BaseOptions( baseUrl: dotenv.env['api_url']!, connectTimeout: const Duration(seconds: 60), diff --git a/lib/ui/views/admin_index_tracking/add_siswa_dialog/add_siswa_dialog/add_siswa_dialog_view.dart b/lib/ui/views/admin_index_tracking/add_siswa_dialog/add_siswa_dialog/add_siswa_dialog_view.dart new file mode 100644 index 0000000..60e29fb --- /dev/null +++ b/lib/ui/views/admin_index_tracking/add_siswa_dialog/add_siswa_dialog/add_siswa_dialog_view.dart @@ -0,0 +1,229 @@ +import 'package:flutter/material.dart'; +import 'package:panti_asuhan/app/themes/app_text.dart'; +import 'package:panti_asuhan/ui/widgets/my_textformfield.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 './add_siswa_dialog_view_model.dart'; + +class DataSiswa { + final String? nama; + + DataSiswa({required this.nama, r}); +} + +class AddSiswaDialogView extends StatelessWidget { + final DialogRequest request; + final Function(DialogResponse) completer; + + const AddSiswaDialogView({ + Key? key, + required DialogRequest request, + required this.completer, + }) : request = request as DialogRequest, + super(key: key); + + @override + Widget build(BuildContext context) { + return ViewModelBuilder.reactive( + viewModelBuilder: () => AddSiswaDialogViewModel(), + onViewModelReady: (AddSiswaDialogViewModel model) async { + await model.init(); + }, + builder: ( + BuildContext context, + AddSiswaDialogViewModel model, + Widget? child, + ) { + return Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + ), + padding: const EdgeInsets.all(10), + child: SingleChildScrollView( + child: Form( + key: model.formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Tambah Siswa', + style: boldTextStyle.copyWith( + fontSize: 16, + ), + ), + const SizedBox(height: 10), + // create circle avatar + Stack( + children: [ + CircleAvatar( + radius: 50, + backgroundColor: fontParagraphColor, + child: model.imageBytes == null + ? const Icon( + Icons.person, + size: 50, + color: Colors.white, + ) + : ClipRRect( + borderRadius: BorderRadius.circular(50), + child: Image.memory( + model.imageBytes!, + width: 100, + height: 100, + fit: BoxFit.cover, + ), + ), + ), + Positioned( + bottom: 0, + right: 0, + child: CircleAvatar( + radius: 15, + backgroundColor: blueColor, + child: IconButton( + onPressed: () { + model.addImage(); + }, + icon: const Icon( + Icons.add, + color: lightColor, + size: 15, + )), + ), + ), + ], + ), + const SizedBox(height: 10), + MyTextFormField( + labelText: 'NIS', + controller: model.nisController, + keyboardType: TextInputType.number, + validator: Validatorless.multiple([ + Validatorless.required('NIS tidak boleh kosong'), + Validatorless.number('NIS harus angka'), + ]), + ), + const SizedBox(height: 10), + MyTextFormField( + labelText: 'Nama', + controller: model.namaController, + validator: + Validatorless.required('Nama tidak boleh kosong'), + ), + const SizedBox(height: 10), + MyTextFormField( + labelText: 'Tanggal Lahir', + controller: model.tanggalLahirController, + readOnly: true, + validator: Validatorless.required( + 'Tanggal lahir tidak boleh kosong'), + onTap: () { + model.changeDate(context); + }, + ), + const SizedBox(height: 10), + // create dropdown button + Container( + width: double.infinity, + height: 60, + padding: const EdgeInsets.symmetric(horizontal: 10), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(25), + border: Border.all( + color: mainColor, + ), + ), + child: DropdownButtonHideUnderline( + child: DropdownButton( + value: model.jenisKelamin, + onChanged: (String? newValue) { + // model.setSelectedJenisKelamin(newValue!); + model.log.i(newValue); + model.jenisKelamin = newValue!; + model.notifyListeners(); + }, + items: model.jenisKelaminList.map((String value) { + return DropdownMenuItem( + value: value, + child: Text( + value, + style: regularTextStyle.copyWith( + fontSize: 16, + ), + ), + ); + }).toList(), + ), + ), + ), + const SizedBox(height: 10), + MyTextFormField( + labelText: 'Alamat', + controller: model.alamatController, + maxLines: 2, + validator: + Validatorless.required('Alamat tidak boleh kosong'), + ), + const SizedBox(height: 10), + MyTextFormField( + labelText: 'Keahlian', + controller: model.keahlianController, + maxLines: 4, + validator: Validatorless.required( + 'Keahlian tidak boleh kosong'), + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + onPressed: () => completer( + DialogResponse( + confirmed: false, + ), + ), + child: const Text( + 'Batal', + style: TextStyle( + color: dangerColor, + ), + ), + ), + TextButton( + onPressed: () async { + if (model.formKey.currentState!.validate()) { + bool res = await model.postData(); + model.log.i("res: $res"); + // if (res) { + // completer( + // DialogResponse( + // confirmed: true, + // ), + // ); + // } + } + }, + child: const Text( + 'Simpan', + style: TextStyle( + color: blueColor, + ), + ), + ), + ], + ), + ], + ), + ), + )), + ); + }, + ); + } +} diff --git a/lib/ui/views/admin_index_tracking/add_siswa_dialog/add_siswa_dialog/add_siswa_dialog_view_model.dart b/lib/ui/views/admin_index_tracking/add_siswa_dialog/add_siswa_dialog/add_siswa_dialog_view_model.dart new file mode 100644 index 0000000..2bae6b7 --- /dev/null +++ b/lib/ui/views/admin_index_tracking/add_siswa_dialog/add_siswa_dialog/add_siswa_dialog_view_model.dart @@ -0,0 +1,113 @@ +import 'dart:typed_data'; + +import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_holo_date_picker/flutter_holo_date_picker.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:panti_asuhan/app/app.locator.dart'; +import 'package:panti_asuhan/app/core/custom_base_view_model.dart'; + +import '../../../../../app/app.logger.dart'; +import '../../../../../services/http_services.dart'; +import '../../../../../services/my_easyloading.dart'; + +class AddSiswaDialogViewModel extends CustomBaseViewModel { + final log = getLogger('AddSiswaDialogViewModel'); + final _httpService = locator(); + final easyLoading = locator(); + List jenisKelaminList = ['Laki-laki', 'Perempuan']; + + String jenisKelamin = 'Laki-laki'; + + // image picker + String? _imagePath; + final ImagePicker _picker = ImagePicker(); + XFile? imageFile; + Uint8List? imageBytes; + + // form and text controller + final formKey = GlobalKey(); + + TextEditingController nisController = TextEditingController(); + TextEditingController namaController = TextEditingController(); + TextEditingController tanggalLahirController = TextEditingController(); + TextEditingController alamatController = TextEditingController(); + TextEditingController keahlianController = TextEditingController(); + + Future init() async {} + + void changeDate(BuildContext context) async { + // get today's date + var datePicked = await DatePicker.showSimpleDatePicker( + context, + initialDate: DateTime(2010), + firstDate: DateTime(2000), + lastDate: DateTime(2015), + dateFormat: "dd-MMMM-yyyy", + locale: DateTimePickerLocale.id, + looping: true, + ); + + if (datePicked != null) { + String date = datePicked.toString().split(' ')[0]; + tanggalLahirController.text = date; + } + } + + void addImage() async { + try { + final XFile? image = await _picker.pickImage(source: ImageSource.gallery); + if (image != null) { + imageFile = image; + _imagePath = image.path; + imageBytes = await image.readAsBytes(); + + log.i('image path: $_imagePath'); + notifyListeners(); + } + } catch (e) { + log.e(e); + } + } + + Future postData() async { + if (imageBytes == null) { + easyLoading.showError('Foto belum dipilih'); + return false; + } + + easyLoading.customLoading('Menambahkan data...'); + try { + var formData = FormData.fromMap({ + 'nis': nisController.text, + 'nama': namaController.text, + 'jenis_kelamin': jenisKelamin, + 'tanggal_lahir': tanggalLahirController.text, + 'alamat': alamatController.text, + 'keahlian': keahlianController.text, + 'foto': await MultipartFile.fromFile(_imagePath!), + }); + + var response = await _httpService.postWithFormData('siswa', formData); + easyLoading.dismissLoading(); + log.i(response.data); + easyLoading.showSuccess('Siswa berhasil ditambahkan'); + return true; + } catch (e) { + easyLoading.dismissLoading(); + snackbarService.showSnackbar( + message: 'Terjadi kesalahan', + title: 'Gagal', + duration: const Duration(seconds: 4), + ); + log.e(e); + return false; + } + + // completer( + // DialogResponse( + // confirmed: true, + // ), + // ); + } +} diff --git a/lib/ui/views/admin_index_tracking/admin_index/admin_index_view.dart b/lib/ui/views/admin_index_tracking/admin_index/admin_index_view.dart index 3ee0d50..fe26e13 100644 --- a/lib/ui/views/admin_index_tracking/admin_index/admin_index_view.dart +++ b/lib/ui/views/admin_index_tracking/admin_index/admin_index_view.dart @@ -20,10 +20,9 @@ class AdminIndexView extends StatelessWidget { ) { return const Scaffold( body: Center( - child: Text( - 'AdminIndexView asdas asda aasdsda a', - ), - ), + child: CircularProgressIndicator( + color: Colors.grey, + )), ); }, ); diff --git a/lib/ui/views/admin_index_tracking/admin_index_tracking_view_model.dart b/lib/ui/views/admin_index_tracking/admin_index_tracking_view_model.dart index 98867d3..9fd4826 100644 --- a/lib/ui/views/admin_index_tracking/admin_index_tracking_view_model.dart +++ b/lib/ui/views/admin_index_tracking/admin_index_tracking_view_model.dart @@ -36,6 +36,12 @@ class AdminIndexTrackingViewModel extends IndexTrackingViewModel { Future init() async { setIndex(1); + // await 2 seconds to make sure the view is loaded + await Future.delayed(const Duration(milliseconds: 500)); + _navigationService.navigateTo( + _views[1], + id: 3, + ); } void handleNavigation(int index) { diff --git a/lib/ui/views/admin_index_tracking/dana_sosial_admin/dana_sosial_admin_view.dart b/lib/ui/views/admin_index_tracking/dana_sosial_admin/dana_sosial_admin_view.dart index b85cf4a..d12eddd 100644 --- a/lib/ui/views/admin_index_tracking/dana_sosial_admin/dana_sosial_admin_view.dart +++ b/lib/ui/views/admin_index_tracking/dana_sosial_admin/dana_sosial_admin_view.dart @@ -21,14 +21,62 @@ class DanaSosialAdminView extends StatelessWidget { Widget? child, ) { return Scaffold( - body: Column( - children: [ - Container( - padding: - const EdgeInsets.symmetric(horizontal: 15, vertical: 10), - width: double.infinity, + body: Column( + children: [ + Container( + padding: + const EdgeInsets.symmetric(horizontal: 15, vertical: 10), + width: double.infinity, + decoration: BoxDecoration( + color: mainColor, + borderRadius: BorderRadius.circular(10), + boxShadow: [ + BoxShadow( + color: mainGrey.withOpacity(0.5), + spreadRadius: 5, + blurRadius: 7, + offset: const Offset(0, 3), // changes position of shadow + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Dana Sosial Bulan Ini', + style: boldTextStyle.copyWith( + color: Colors.white, + fontSize: 20, + ), + ), + const SizedBox(height: 10), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Total Dana Sosial', + style: regularTextStyle.copyWith( + color: Colors.white, + fontSize: 15, + ), + ), + Text( + 'Rp. 1.000.000', + style: regularTextStyle.copyWith( + color: Colors.white, + fontSize: 15, + ), + ), + ], + ), + ], + ), + ), + const SizedBox(height: 25), + Expanded( + child: Container( decoration: BoxDecoration( - color: mainColor, + color: Colors.white, borderRadius: BorderRadius.circular(10), boxShadow: [ BoxShadow( @@ -40,84 +88,38 @@ class DanaSosialAdminView extends StatelessWidget { ), ], ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Dana Sosial Bulan Ini', - style: boldTextStyle.copyWith( - color: Colors.white, - fontSize: 20, + child: ListView.builder( + padding: const EdgeInsets.symmetric( + horizontal: 15, vertical: 10), + itemCount: 20, + itemBuilder: (context, index) { + return Card( + child: GestureDetector( + onTap: () { + model.log.i('Card $index tapped'); + }, + child: ListTile( + title: Text('1/02/15 - 10.00 am', + style: boldTextStyle.copyWith( + fontSize: 13, color: mainColor)), + subtitle: Text('Progress $index'), + trailing: Text('Pembangunan $index'), + ), ), - ), - const SizedBox(height: 10), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - 'Total Dana Sosial', - style: regularTextStyle.copyWith( - color: Colors.white, - fontSize: 15, - ), - ), - Text( - 'Rp. 1.000.000', - style: regularTextStyle.copyWith( - color: Colors.white, - fontSize: 15, - ), - ), - ], - ), - ], + ); + }, ), ), - const SizedBox(height: 25), - Expanded( - child: Container( - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(10), - boxShadow: [ - BoxShadow( - color: mainGrey.withOpacity(0.5), - spreadRadius: 5, - blurRadius: 7, - offset: - const Offset(0, 3), // changes position of shadow - ), - ], - ), - child: ListView.builder( - padding: const EdgeInsets.symmetric( - horizontal: 15, vertical: 10), - itemCount: 20, - itemBuilder: (context, index) { - return Card( - child: GestureDetector( - onTap: () { - model.log.i('Card $index tapped'); - }, - child: ListTile( - title: Text('1/02/15 - 10.00 am', - style: boldTextStyle.copyWith( - fontSize: 13, color: mainColor)), - subtitle: Text('Progress $index'), - trailing: Text('Pembangunan $index'), - ), - ), - ); - }, - ), - ), - ), - ], - ), - floatingActionButton: const FloatingActionButton( - onPressed: null, - child: Icon(Icons.add), - )); + ), + ], + ), + floatingActionButton: FloatingActionButton( + onPressed: () { + model.goToTambahDanaSosial(); + }, + child: const Icon(Icons.add), + ), + ); }, ); } diff --git a/lib/ui/views/admin_index_tracking/dana_sosial_admin/dana_sosial_admin_view_model.dart b/lib/ui/views/admin_index_tracking/dana_sosial_admin/dana_sosial_admin_view_model.dart index 038f91a..ec0dbfe 100644 --- a/lib/ui/views/admin_index_tracking/dana_sosial_admin/dana_sosial_admin_view_model.dart +++ b/lib/ui/views/admin_index_tracking/dana_sosial_admin/dana_sosial_admin_view_model.dart @@ -1,8 +1,12 @@ -import 'package:panti_asuhan/app/core/custom_base_view_model.dart'; - import '../../../../app/app.logger.dart'; +import '../../../../app/app.router.dart'; +import '../../../../app/core/custom_base_view_model.dart'; class DanaSosialAdminViewModel extends CustomBaseViewModel { final log = getLogger('DanaSosialAdminViewModel'); Future init() async {} + + goToTambahDanaSosial() { + navigationService.navigateTo(Routes.tambahDanaSosialView); + } } diff --git a/lib/ui/views/admin_index_tracking/data_siswa/data_siswa_view.dart b/lib/ui/views/admin_index_tracking/data_siswa/data_siswa_view.dart index 2aa96df..eb39c74 100644 --- a/lib/ui/views/admin_index_tracking/data_siswa/data_siswa_view.dart +++ b/lib/ui/views/admin_index_tracking/data_siswa/data_siswa_view.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; +import '../../../../app/themes/app_colors.dart'; +import '../../../../app/themes/app_text.dart'; import './data_siswa_view_model.dart'; class DataSiswaView extends StatelessWidget { @@ -18,11 +20,102 @@ class DataSiswaView extends StatelessWidget { DataSiswaViewModel model, Widget? child, ) { - return const Scaffold( - body: Center( - child: Text( - 'DataSiswaView', - ), + return Scaffold( + body: Column( + children: [ + Container( + padding: + const EdgeInsets.symmetric(horizontal: 15, vertical: 10), + width: double.infinity, + decoration: BoxDecoration( + color: mainColor, + borderRadius: BorderRadius.circular(10), + boxShadow: [ + BoxShadow( + color: mainGrey.withOpacity(0.5), + spreadRadius: 5, + blurRadius: 7, + offset: const Offset(0, 3), // changes position of shadow + ), + ], + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Total Siswa', + style: regularTextStyle.copyWith( + color: Colors.white, + fontSize: 15, + ), + ), + Text( + '20 orang', + style: regularTextStyle.copyWith( + color: Colors.white, + fontSize: 15, + ), + ), + ], + ), + ), + const SizedBox(height: 25), + Expanded( + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(10), + boxShadow: [ + BoxShadow( + color: mainGrey.withOpacity(0.5), + spreadRadius: 5, + blurRadius: 7, + offset: + const Offset(0, 3), // changes position of shadow + ), + ], + ), + child: ListView.builder( + padding: const EdgeInsets.symmetric( + horizontal: 15, vertical: 10), + itemCount: 20, + itemBuilder: (context, index) { + return Card( + child: GestureDetector( + onTap: () { + model.log.i('Card $index tapped'); + }, + child: ListTile( + title: Text('Namanya', + style: boldTextStyle.copyWith( + fontSize: 13, color: mainColor)), + subtitle: Text('Umurnya : $index'), + // circle avatar + trailing: Container( + width: 50, + height: 50, + decoration: BoxDecoration( + color: mainColor, + borderRadius: BorderRadius.circular(50), + ), + child: const Icon( + Icons.person, + color: Colors.white, + ), + )), + ), + ); + }, + ), + ), + ), + ], + ), + floatingActionButton: FloatingActionButton( + onPressed: () { + model.addSiswa(); + }, + child: const Icon(Icons.add), ), ); }, diff --git a/lib/ui/views/admin_index_tracking/data_siswa/data_siswa_view_model.dart b/lib/ui/views/admin_index_tracking/data_siswa/data_siswa_view_model.dart index d302054..fd5d3b6 100644 --- a/lib/ui/views/admin_index_tracking/data_siswa/data_siswa_view_model.dart +++ b/lib/ui/views/admin_index_tracking/data_siswa/data_siswa_view_model.dart @@ -1,5 +1,19 @@ import 'package:panti_asuhan/app/core/custom_base_view_model.dart'; +import '../../../../app/app.dialogs.dart'; +import '../../../../app/app.logger.dart'; +import '../add_siswa_dialog/add_siswa_dialog/add_siswa_dialog_view.dart'; + class DataSiswaViewModel extends CustomBaseViewModel { + final log = getLogger('DataSiswaViewModel'); Future init() async {} + + void addSiswa() async { + final res = await dialogService.showCustomDialog( + variant: DialogType.addSiswaDialogView, + data: DataSiswa(nama: null), + ); + + if (res?.confirmed != true) return; + } } diff --git a/lib/ui/views/login_screen/login_screen_view.dart b/lib/ui/views/login_screen/login_screen_view.dart index 47ee646..e2002be 100644 --- a/lib/ui/views/login_screen/login_screen_view.dart +++ b/lib/ui/views/login_screen/login_screen_view.dart @@ -24,58 +24,60 @@ class LoginScreenView extends StatelessWidget { return Scaffold( body: Padding( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 20), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox( - height: MediaQuery.of(context).size.height * 0.2, - ), - // show the logo.png - const Center( - child: Image( - image: AssetImage("assets/logo.png"), - width: 150, - height: 150, + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + height: MediaQuery.of(context).size.height * 0.2, ), - ), - const SizedBox(height: 10), - Text( - "SILAHKAN LOGIN", - style: boldTextStyle.copyWith( - fontSize: 18, + // show the logo.png + const Center( + child: Image( + image: AssetImage("assets/logo.png"), + width: 150, + height: 150, + ), ), - ), - const SizedBox( - height: 10, - ), - const MyTextFormField( - hintText: "Username", - prefixIcon: Icon(Icons.person), - // controller: model.usernameController, - ), - const SizedBox( - height: 10, - ), - const MyTextFormField( - hintText: "Password", - prefixIcon: Icon(Icons.lock), - // controller: model.passwordController, - ), - const SizedBox( - height: 10, - ), - SizedBox( - width: MediaQuery.of(context).size.width * 0.5, - child: MyButton( - text: "LOGIN", - onPressed: () { - model.goToAdmin(); - }, + const SizedBox(height: 10), + Text( + "SILAHKAN LOGIN", + style: boldTextStyle.copyWith( + fontSize: 18, + ), ), - ), - ], + const SizedBox( + height: 10, + ), + const MyTextFormField( + hintText: "Username", + prefixIcon: Icon(Icons.person), + // controller: model.usernameController, + ), + const SizedBox( + height: 10, + ), + const MyTextFormField( + hintText: "Password", + prefixIcon: Icon(Icons.lock), + // controller: model.passwordController, + ), + const SizedBox( + height: 10, + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.5, + child: MyButton( + text: "LOGIN", + onPressed: () { + model.goToAdmin(); + }, + ), + ), + ], + ), ), ), ); diff --git a/lib/ui/views/tambah_dana_sosial/tambah_dana_sosial_view.dart b/lib/ui/views/tambah_dana_sosial/tambah_dana_sosial_view.dart new file mode 100644 index 0000000..6b84d96 --- /dev/null +++ b/lib/ui/views/tambah_dana_sosial/tambah_dana_sosial_view.dart @@ -0,0 +1,134 @@ +import 'package:flutter/material.dart'; +import 'package:panti_asuhan/app/themes/app_colors.dart'; +import 'package:panti_asuhan/app/themes/app_text.dart'; +import 'package:panti_asuhan/ui/widgets/my_button.dart'; +import 'package:panti_asuhan/ui/widgets/my_textformfield.dart'; +import 'package:stacked/stacked.dart'; +import 'package:validatorless/validatorless.dart'; + +import './tambah_dana_sosial_view_model.dart'; + +class TambahDanaSosialView extends StatelessWidget { + const TambahDanaSosialView({super.key}); + + @override + Widget build(BuildContext context) { + return ViewModelBuilder.reactive( + viewModelBuilder: () => TambahDanaSosialViewModel(), + onViewModelReady: (TambahDanaSosialViewModel model) async { + await model.init(); + }, + builder: ( + BuildContext context, + TambahDanaSosialViewModel model, + Widget? child, + ) { + return Scaffold( + appBar: AppBar( + title: const Text( + "Form Dana Sosial", + style: TextStyle( + color: Colors.white, + fontSize: 20, + ), + ), + backgroundColor: mainColor, + elevation: 0, + ), + body: Center( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15), + child: Form( + key: model.formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Keterangan", + style: regularTextStyle.copyWith(color: mainColor), + ), + MyTextFormField( + hintText: "Keterangan", + controller: model.ketController, + maxLines: 3, + validator: Validatorless.required( + 'Keterangan tidak boleh kosong'), + ), + const SizedBox(height: 20), + Text( + "Jumlah (Rp. )", + style: regularTextStyle.copyWith(color: mainColor), + ), + MyTextFormField( + hintText: "Jumlah (Rp. )", + keyboardType: TextInputType.number, + controller: model.jumlahController, + validator: Validatorless.multiple( + [ + Validatorless.required('Jumlah tidak boleh kosong'), + Validatorless.number('Jumlah harus angka'), + ], + ), + ), + const SizedBox(height: 20), + Text( + "Tanggal", + style: regularTextStyle.copyWith(color: mainColor), + ), + MyTextFormField( + hintText: 'Tanggal', + readOnly: true, + validator: Validatorless.required( + 'Tanggal lahir tidak boleh kosong'), + onTap: () { + model.changeDate(context); + }, + ), + const SizedBox(height: 20), + Text( + "Jenis Dana", + style: regularTextStyle.copyWith(color: mainColor), + ), + Container( + width: double.infinity, + height: 60, + padding: const EdgeInsets.symmetric(horizontal: 20), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(25), + border: Border.all( + color: mainColor, + ), + ), + child: DropdownButtonHideUnderline( + child: DropdownButton( + value: model.jenisDana, + onChanged: (String? newValue) { + model.jenisDana = newValue!; + }, + items: model.jenisDanaList + .map>((String value) { + return DropdownMenuItem( + value: value, + child: Text(value, + style: const TextStyle(fontSize: 16)), + ); + }).toList(), + ), + ), + ), + const SizedBox(height: 20), + MyButton( + text: "Simpan", + onPressed: () {}, + ), + ], + ), + ), + ), + ), + ); + }, + ); + } +} diff --git a/lib/ui/views/tambah_dana_sosial/tambah_dana_sosial_view_model.dart b/lib/ui/views/tambah_dana_sosial/tambah_dana_sosial_view_model.dart new file mode 100644 index 0000000..216a889 --- /dev/null +++ b/lib/ui/views/tambah_dana_sosial/tambah_dana_sosial_view_model.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'package:panti_asuhan/app/core/custom_base_view_model.dart'; + +class TambahDanaSosialViewModel extends CustomBaseViewModel { + String jenisDana = 'Pemasukan'; + List jenisDanaList = ['Pemasukan', 'Pengeluaran']; + + final formKey = GlobalKey(); + + TextEditingController ketController = TextEditingController(); + TextEditingController jumlahController = TextEditingController(); + TextEditingController tanggalController = TextEditingController(); + + Future init() async {} + + void changeDate(BuildContext context) async { + // get today's date + var datePicked = await showDatePicker( + context: context, + initialDate: DateTime.now(), + firstDate: DateTime(2000), + // last date is today's date + lastDate: DateTime.now(), + ); + + if (datePicked != null) { + String date = datePicked.toString().split(' ')[0]; + tanggalController.text = date; + } + } +} diff --git a/lib/ui/widgets/my_textformfield.dart b/lib/ui/widgets/my_textformfield.dart index c67449b..4cc22b8 100644 --- a/lib/ui/widgets/my_textformfield.dart +++ b/lib/ui/widgets/my_textformfield.dart @@ -15,6 +15,9 @@ class MyTextFormField extends StatelessWidget { this.controller, this.maxLines = 1, this.onEditingComplete, + this.readOnly = false, + this.onTap, + this.keyboardType = TextInputType.text, }) : super(key: key); final String? labelText; @@ -27,6 +30,9 @@ class MyTextFormField extends StatelessWidget { final TextEditingController? controller; final int maxLines; final VoidCallback? onEditingComplete; + final bool readOnly; + final VoidCallback? onTap; + final TextInputType keyboardType; @override Widget build(BuildContext context) { @@ -36,6 +42,9 @@ class MyTextFormField extends StatelessWidget { controller: controller, focusNode: focusNode, obscureText: obscureText ?? false, + readOnly: readOnly, + onTap: onTap, + keyboardType: keyboardType, decoration: InputDecoration( prefixIcon: prefixIcon, suffixIcon: suffixIcon, diff --git a/pubspec.lock b/pubspec.lock index cee7e28..4c0c09f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -33,6 +33,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.10.0" + auto_size_text: + dependency: transitive + description: + name: auto_size_text + sha256: "3f5261cd3fb5f2a9ab4e2fc3fba84fd9fcaac8821f20a1d4e71f557521b22599" + url: "https://pub.dev" + source: hosted + version: "3.0.0" boolean_selector: dependency: transitive description: @@ -246,6 +254,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.5" + flutter_holo_date_picker: + dependency: "direct main" + description: + name: flutter_holo_date_picker + sha256: "94cf29471ffac123043745b65c690dcf6d481e98c11bda71f78a34dd6f726247" + url: "https://pub.dev" + source: hosted + version: "1.1.0" flutter_lints: dependency: "direct dev" description: @@ -813,6 +829,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.0" + validatorless: + dependency: "direct main" + description: + name: validatorless + sha256: ddb46df636114b3322d289489164cac309767b157191ba43c7ad49b28c2b57c7 + url: "https://pub.dev" + source: hosted + version: "1.2.3" vector_graphics: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index a163956..6c3df1c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -46,6 +46,9 @@ dependencies: google_fonts: flutter_svg: stylish_bottom_bar: ^1.0.0 + # calendar_date_picker2: ^0.5.2 + flutter_holo_date_picker: ^1.1.0 + validatorless: ^1.2.3 dev_dependencies: flutter_test: