Compare commits

...

10 Commits

Author SHA1 Message Date
kicap 0ad81dd67d added cetak laporan 2024-06-05 17:09:58 +08:00
kicap 41ecbc0065 slight changes 2023-11-13 18:33:06 +08:00
kicap 667aae745b add edit siswa page 2023-08-25 04:30:59 +08:00
kicap b1dc1851a4 modify struktur organisasi page so can be edited, add delete siswa, add 'pengeluaran' in dana sosial, added edit siswa dialog 2023-08-21 03:46:54 +08:00
kicap 4f7a8b870c struktur organisasi harus diedit 2023-08-19 01:36:03 +08:00
kicap 914e24706b added user page, pimpinan page, e signature 2023-08-03 17:58:40 +08:00
kicap 30131e5ffe first commit 2023-07-15 05:05:24 +08:00
kicap 757198cb83 tambah lihat informasi dan halaman lainnya 2023-07-14 11:31:03 +08:00
kicap e0b5213a3b added add siswa and dana sosial 2023-05-18 18:58:21 +08:00
kicap 949474b8ef added other 2023-05-18 16:04:03 +08:00
62 changed files with 8760 additions and 298 deletions

10
.env
View File

@ -1,2 +1,8 @@
url = 'http://20.20.20.25/perumahan/'
api_url = 'http://20.20.20.25/perumahan/api/'
# url = 'https://panti-asuhan.s-keytech.com/'
# api_url = 'https://panti-asuhan.s-keytech.com/api/'
# url = 'http://172.29.85.181/panti_asuhan2/'
# api_url = 'http://172.29.85.181/panti_asuhan2/api/'
# url = 'http://20.20.20.25/panti_asuhan2/'
# api_url = 'http://20.20.20.25/panti_asuhan2/api/'
url = 'https://localhost.kicap-karan.com/panti_asuhan/'
api_url = 'https://localhost.kicap-karan.com/panti_asuhan/api/'

1
.gitignore vendored
View File

@ -31,6 +31,7 @@ migrate_working_dir/
.pub-cache/
.pub/
/build/
.env
# Symbolication related
app.*.symbols

View File

@ -1,25 +1,13 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.panti_asuhan">
<application
android:label="panti_asuhan"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.panti_asuhan">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application android:label="panti_asuhan" android:name="${applicationName}" android:icon="@mipmap/ic_launcher">
<activity android:name=".MainActivity" android:exported="true" android:launchMode="singleTop" android:theme="@style/LaunchTheme" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<meta-data android:name="io.flutter.embedding.android.NormalTheme" android:resource="@style/NormalTheme" />
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
@ -27,8 +15,6 @@
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
<meta-data android:name="flutterEmbedding" android:value="2" />
</application>
</manifest>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 B

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 442 B

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 B

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 205 KiB

BIN
assets/qrcode.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 KiB

View File

@ -1,15 +1,29 @@
import 'package:panti_asuhan/ui/views/admin_index_tracking/admin_index/admin_index_view.dart';
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:stacked_services/stacked_services.dart';
import 'package:stacked/stacked_annotations.dart';
import '../services/http_services.dart';
import '../services/my_easyloading.dart';
import '../services/other_function.dart';
import '../ui/views/admin_index_tracking/add_siswa_dialog/add_siswa_dialog/add_siswa_dialog_view.dart';
import '../ui/views/admin_index_tracking/dana_sosial_khusus/add_donatur_dialog/add_donatur_dialog_view.dart';
import '../ui/views/admin_index_tracking/dana_sosial_khusus/dana_sosial_khusus_view.dart';
import '../ui/views/admin_index_tracking/admin_index_tracking_view.dart';
import '../ui/views/admin_index_tracking/dana_sosial_admin/dana_sosial_admin_view.dart';
import '../ui/views/admin_index_tracking/data_siswa/data_siswa_view.dart';
import '../ui/views/admin_index_tracking/edit_siswa/edit_dialog_siswa/edit_dialog_siswa_view.dart';
import '../ui/views/admin_index_tracking/edit_siswa/edit_siswa_view.dart';
import '../ui/views/admin_index_tracking/filter_dialog/filter_dialog_view.dart';
import '../ui/views/admin_index_tracking/profil/profil_view.dart';
import '../ui/views/admin_index_tracking/sejarah/sejarah_view.dart';
import '../ui/views/admin_index_tracking/struktur_organisasi/edit_strukrur_organisasi_dialog/edit_strukrur_organisasi_dialog_view.dart';
import '../ui/views/admin_index_tracking/struktur_organisasi/struktur_organisasi_view.dart';
import '../ui/views/admin_index_tracking/visi_misi/visi_misi_view.dart';
import '../ui/views/detail_dana_sosial/detail_dana_sosial_view.dart';
import '../ui/views/login_screen/login_screen_view.dart';
import '../ui/views/pimpinan_index_tracking/pimpinan_index_tracking/pimpinan_index_tracking_view.dart';
import '../ui/views/splash_screen/splash_screen_view.dart';
import '../ui/views/tambah_dana_sosial/tambah_dana_sosial_view.dart';
import '../ui/views/user_index_tracking/user_index_tracking_view.dart';
@StackedApp(
routes: [
@ -18,12 +32,51 @@ import '../ui/views/splash_screen/splash_screen_view.dart';
MaterialRoute(
page: AdminIndexTrackingView,
children: [
MaterialRoute(page: AdminIndexView, initial: true),
// MaterialRoute(page: AdminIndexView, initial: true),
MaterialRoute(page: DanaSosialAdminView),
MaterialRoute(page: DanaSosialKhususView),
MaterialRoute(page: DataSiswaView),
MaterialRoute(page: ProfilView),
MaterialRoute(page: VisiMisiView),
MaterialRoute(page: SejarahView),
MaterialRoute(
page: StrukturOrganisasiView,
),
],
),
MaterialRoute(page: TambahDanaSosialView),
MaterialRoute(page: EditSiswaView),
MaterialRoute(page: DetailDanaSosialView),
MaterialRoute(
page: PimpinanIndexTrackingView,
children: [
// MaterialRoute(page: AdminIndexView, initial: true),
MaterialRoute(page: DanaSosialAdminView),
MaterialRoute(page: DanaSosialKhususView),
MaterialRoute(page: DataSiswaView),
MaterialRoute(page: ProfilView),
],
),
MaterialRoute(
page: UserIndexTrackingView,
children: [
// MaterialRoute(page: AdminIndexView, initial: true),
MaterialRoute(page: DanaSosialAdminView),
MaterialRoute(page: DanaSosialKhususView),
MaterialRoute(page: DataSiswaView),
MaterialRoute(page: ProfilView),
MaterialRoute(page: VisiMisiView),
MaterialRoute(page: SejarahView),
MaterialRoute(page: StrukturOrganisasiView),
],
),
],
dialogs: [
StackedDialog(classType: AddSiswaDialogView),
StackedDialog(classType: FilterDialogView),
StackedDialog(classType: EditStrukrurOrganisasiDialogView),
StackedDialog(classType: EditDialogSiswaView),
StackedDialog(classType: AddDonaturDialogView),
],
dependencies: [
LazySingleton(classType: NavigationService),
@ -34,6 +87,7 @@ import '../ui/views/splash_screen/splash_screen_view.dart';
LazySingleton(classType: MyEasyLoading),
LazySingleton(classType: MyHttpServices),
LazySingleton(classType: OtherFunction),
],
logger: StackedLogger(),
)

42
lib/app/app.dialogs.dart Normal file
View File

@ -0,0 +1,42 @@
// 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';
import '../ui/views/admin_index_tracking/dana_sosial_khusus/add_donatur_dialog/add_donatur_dialog_view.dart';
import '../ui/views/admin_index_tracking/edit_siswa/edit_dialog_siswa/edit_dialog_siswa_view.dart';
import '../ui/views/admin_index_tracking/filter_dialog/filter_dialog_view.dart';
import '../ui/views/admin_index_tracking/struktur_organisasi/edit_strukrur_organisasi_dialog/edit_strukrur_organisasi_dialog_view.dart';
enum DialogType {
addSiswaDialogView,
filterDialogView,
editStrukrurOrganisasiDialogView,
editDialogSiswaView,
addDonaturDialogView,
}
void setupDialogUi() {
final dialogService = locator<DialogService>();
final Map<DialogType, DialogBuilder> builders = {
DialogType.addSiswaDialogView: (context, request, completer) =>
AddSiswaDialogView(request: request, completer: completer),
DialogType.filterDialogView: (context, request, completer) =>
FilterDialogView(request: request, completer: completer),
DialogType.editStrukrurOrganisasiDialogView:
(context, request, completer) => EditStrukrurOrganisasiDialogView(
request: request, completer: completer),
DialogType.editDialogSiswaView: (context, request, completer) =>
EditDialogSiswaView(request: request, completer: completer),
DialogType.addDonaturDialogView: (context, request, completer) =>
AddDonaturDialogView(request: request, completer: completer),
};
dialogService.registerCustomDialogBuilders(builders);
}

View File

@ -14,6 +14,7 @@ import 'package:stacked_shared/stacked_shared.dart';
import '../services/http_services.dart';
import '../services/my_easyloading.dart';
import '../services/other_function.dart';
final locator = StackedLocator.instance;
@ -32,4 +33,5 @@ Future<void> setupLocator({
locator.registerLazySingleton(() => BottomSheetService());
locator.registerLazySingleton(() => MyEasyLoading());
locator.registerLazySingleton(() => MyHttpServices());
locator.registerLazySingleton(() => OtherFunction());
}

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,8 @@
import 'package:shared_preferences/shared_preferences.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
import '../../services/other_function.dart';
import '../app.locator.dart';
class CustomBaseViewModel extends BaseViewModel {
@ -8,6 +10,8 @@ class CustomBaseViewModel extends BaseViewModel {
final navigationService = locator<NavigationService>();
final bottomSheetService = locator<BottomSheetService>();
final snackbarService = locator<SnackbarService>();
final Future<SharedPreferences> prefs = SharedPreferences.getInstance();
final otherFunction = locator<OtherFunction>();
void back() {
navigationService.back();

View File

@ -1,14 +1,18 @@
import 'dart:io';
import 'package:flutter/material.dart';
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';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
HttpOverrides.global = MyHttpOverrides();
await dotenv.load(fileName: ".env");
await setupAllLocator();
runApp(const MyApp());
@ -21,7 +25,7 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Perumahan Mutiara Alga',
title: 'Panti Asuhan Aisyiyah Abadi',
theme: appTheme,
debugShowCheckedModeBanner: false,
navigatorKey: StackedService.navigatorKey,
@ -33,7 +37,16 @@ class MyApp extends StatelessWidget {
Future<void> 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;
}
}

View File

@ -0,0 +1,66 @@
class DanaSosialModel {
String? idDanaSosial;
String? bentuk;
String? nama;
String? jumlah;
String? tanggal;
String? keterangan;
String? jenisDonasi;
String? status;
String? jenisBarang;
String? satuan;
String? jumlahBarang;
String? createdAt;
String? updatedAt;
DanaSosialModel(
{this.idDanaSosial,
this.bentuk,
this.nama,
this.jumlah,
this.tanggal,
this.keterangan,
this.jenisDonasi,
this.status,
this.jenisBarang,
this.satuan,
this.jumlahBarang,
this.createdAt,
this.updatedAt});
DanaSosialModel.fromJson(Map<String, dynamic> json) {
idDanaSosial = json['id_dana_sosial'];
bentuk = json['bentuk'];
nama = json['nama'] ?? '';
jumlah = json['jumlah'];
tanggal = json['tanggal'];
keterangan = json['ket'] ?? '';
jenisDonasi = json['jenis'];
status =
json['status'] == '0' ? 'Belum Dikonfirmasi' : 'Sudah Dikonfirmasi';
jenisBarang = json['jenis_barang'] ?? '';
satuan = json['satuan'] ?? '';
jumlahBarang = json['jumlah_barang'] ?? '';
createdAt = json['created_at'];
updatedAt = json['updated_at'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id_dana_sosial'] = idDanaSosial;
data['bentuk'] = bentuk;
data['nama'] = nama;
data['jumlah'] = jumlah;
data['tanggal'] = tanggal;
data['ket'] = keterangan;
data['jenis'] = jenisDonasi;
data['status'] = status;
data['jenis_barang'] = jenisBarang;
data['satuan'] = satuan;
data['jumlah_barang'] = jumlahBarang;
data['created_at'] = createdAt;
data['updated_at'] = updatedAt;
return data;
}
}

View File

@ -0,0 +1,80 @@
class SiswaModel {
String? idSiswa;
String? nama;
String? tanggalLahir;
String? jenisKelamin;
String? alamat;
String? kemampuan;
String? imgUrl;
String? createdAt;
String? updatedAt;
String? tempatLahir;
String? noTelpon;
String? agama;
String? kewarganegaraan;
String? pendidikanSd;
String? pendidikanSmp;
String? pendidikanSma;
String? hobi;
SiswaModel(
{this.idSiswa,
this.nama,
this.tanggalLahir,
this.jenisKelamin,
this.alamat,
this.kemampuan,
this.imgUrl,
this.createdAt,
this.updatedAt,
this.tempatLahir,
this.noTelpon,
this.agama,
this.kewarganegaraan,
this.pendidikanSd,
this.pendidikanSmp,
this.pendidikanSma,
this.hobi});
SiswaModel.fromJson(Map<String, dynamic> json) {
idSiswa = json['id_siswa'];
nama = json['nama'];
tanggalLahir = json['tanggal_lahir'];
jenisKelamin = json['jenis_kelamin'];
alamat = json['alamat'];
kemampuan = json['kemampuan'];
imgUrl = json['img_url'];
createdAt = json['created_at'];
updatedAt = json['updated_at'];
tempatLahir = json['tempat_lahir'];
noTelpon = json['no_telpon'];
agama = json['agama'];
kewarganegaraan = json['kewarganegaraan'];
pendidikanSd = json['pendidikan_sd'];
pendidikanSmp = json['pendidikan_smp'];
pendidikanSma = json['pendidikan_sma'];
hobi = json['hobi'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id_siswa'] = idSiswa;
data['nama'] = nama;
data['tanggal_lahir'] = tanggalLahir;
data['jenis_kelamin'] = jenisKelamin;
data['alamat'] = alamat;
data['kemampuan'] = kemampuan;
data['img_url'] = imgUrl;
data['created_at'] = createdAt;
data['updated_at'] = updatedAt;
data['tempat_lahir'] = tempatLahir;
data['no_telpon'] = noTelpon;
data['agama'] = agama;
data['kewarganegaraan'] = kewarganegaraan;
data['pendidikan_sd'] = pendidikanSd;
data['pendidikan_smp'] = pendidikanSmp;
data['pendidikan_sma'] = pendidikanSma;
data['hobi'] = hobi;
return data;
}
}

View File

@ -1,11 +1,14 @@
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),
receiveTimeout: const Duration(seconds: 60),
connectTimeout: const Duration(seconds: 120),
receiveTimeout: const Duration(seconds: 120),
);
late Dio _dio;
@ -17,7 +20,9 @@ class MyHttpServices {
Future<Response> get(String path) async {
try {
return await _dio.get(path);
} on DioError {
} on DioError catch (e) {
log.e(e.message);
log.e(e.response);
rethrow;
}
}
@ -25,8 +30,32 @@ class MyHttpServices {
Future<Response> postWithFormData(String path, FormData formData) async {
try {
return await _dio.post(path, data: formData);
} on DioError {
} on DioError catch (e) {
log.e(e.message);
log.e(e.response!.statusCode);
log.e(e.response!.statusMessage);
rethrow;
}
}
// // delete
// Future<Response> delete(String path, FormData data) async {
// try {
// // log.i('path: $path');
// return await _dio.delete(
// path,
// data: data,
// // encoding: Encoding.getByName('utf-8'),
// options: Options(
// headers: {
// 'Content-Type': 'application/x-www-form-urlencoded',
// },
// ),
// );
// } on DioError catch (e) {
// log.e(e.message);
// log.e(e.response);
// rethrow;
// }
// }
}

View File

@ -0,0 +1,106 @@
import 'package:intl/intl.dart';
class OtherFunction {
int umur(String tanggalLahir) {
// change tanggalLahir to DateTime
DateTime date = DateTime.parse(tanggalLahir);
// get current date
DateTime now = DateTime.now();
// get difference in year
int year = now.year - date.year;
return year;
}
String commaFormat(int number) {
final formatter = NumberFormat('#,###');
return formatter.format(number);
}
String changeMonth(String month) {
switch (month) {
case 'Januari':
return '01';
case 'Februari':
return '02';
case 'Maret':
return '03';
case 'April':
return '04';
case 'Mei':
return '05';
case 'Juni':
return '06';
case 'Juli':
return '07';
case 'Agustus':
return '08';
case 'September':
return '09';
case 'Oktober':
return '10';
case 'November':
return '11';
case 'Desember':
return '12';
default:
return '';
}
}
String changeMonthYear(String s) {
// get the last 2 digits
String month = s.substring(s.length - 2);
// get the first 4 digits
String year = s.substring(0, 4);
// return the month and year
switch (month) {
case '01':
return 'Januari $year';
case '02':
return 'Februari $year';
case '03':
return 'Maret $year';
case '04':
return 'April $year';
case '05':
return 'Mei $year';
case '06':
return 'Juni $year';
case '07':
return 'Juli $year';
case '08':
return 'Agustus $year';
case '09':
return 'September $year';
case '10':
return 'Oktober $year';
case '11':
return 'November $year';
case '12':
return 'Desember $year';
default:
return '';
}
}
String getDayOfWeek(String date) {
DateTime dateTime = DateTime.parse(date);
List<String> daysOfWeek = [
'Senin',
'Selasa',
'Rabu',
'Kamis',
'Jumat',
'Sabtu',
'Minggu'
];
return daysOfWeek[dateTime.weekday - 1];
}
String capitalizeEachWord(String s) {
return s
.split(' ')
.map((word) => word[0].toUpperCase() + word.substring(1))
.join(' ');
}
}

View File

@ -0,0 +1,286 @@
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 './add_siswa_dialog_view_model.dart';
class DataSiswa {
final String? nama;
DataSiswa({required this.nama, r});
}
class AddSiswaDialogView extends StatelessWidget {
final DialogRequest<DataSiswa> request;
final Function(DialogResponse) completer;
const AddSiswaDialogView({
Key? key,
required DialogRequest request,
required this.completer,
}) : request = request as DialogRequest<DataSiswa>,
super(key: key);
@override
Widget build(BuildContext context) {
return ViewModelBuilder<AddSiswaDialogViewModel>.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 Anak Panti',
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: '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),
MyTextFormField(
labelText: 'Tempat Lahir',
controller: model.tempatLahirController,
validator: Validatorless.required(
'Tempat lahir tidak boleh kosong'),
),
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<String>(
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<String>(
value: value,
child: Text(
value,
style: regularTextStyle.copyWith(
fontSize: 16,
),
),
);
}).toList(),
),
),
),
const SizedBox(height: 10),
MyTextFormField(
labelText: 'No. Telepon',
controller: model.noTelponController,
keyboardType: TextInputType.number,
validator: Validatorless.multiple(
[
Validatorless.required(
'No. telepon tidak boleh kosong'),
Validatorless.number(
'No. telepon harus berupa angka'),
],
),
),
const SizedBox(height: 10),
MyTextFormField(
labelText: 'Agama',
controller: model.agamaController,
keyboardType: TextInputType.emailAddress,
validator:
Validatorless.required('Agama tidak boleh kosong'),
),
const SizedBox(height: 10),
MyTextFormField(
labelText: 'Kewarganegaraan',
controller: model.kewarganegaraanController,
keyboardType: TextInputType.emailAddress,
validator: Validatorless.required(
'Kewarganegaraan tidak boleh kosong'),
),
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: 'Pendidikan SD',
controller: model.pendidikanSDController,
validator: Validatorless.required(
'Pendidikan SD tidak boleh kosong'),
),
const SizedBox(height: 10),
MyTextFormField(
labelText: 'Pendidikan SMP',
controller: model.pendidikanSMPController,
validator: Validatorless.required(
'Pendidikan SMP tidak boleh kosong'),
),
const SizedBox(height: 10),
MyTextFormField(
labelText: 'Pendidikan SMA',
controller: model.pendidikanSMAController,
validator: Validatorless.required(
'Pendidikan SMA tidak boleh kosong'),
),
const SizedBox(height: 10),
MyTextFormField(
labelText: 'Kemampuan',
controller: model.kemampuanController,
maxLines: 4,
validator: Validatorless.required(
'Kemampuan tidak boleh kosong'),
),
const SizedBox(height: 10),
MyTextFormField(
labelText: "Hobi",
controller: model.hobiController,
maxLines: 4,
validator:
Validatorless.required('Hobi 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,
),
),
),
],
),
],
),
),
)),
);
},
);
}
}

View File

@ -0,0 +1,127 @@
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 '../../../../../app/app.locator.dart';
import '../../../../../app/app.logger.dart';
import '../../../../../app/core/custom_base_view_model.dart';
import '../../../../../services/http_services.dart';
import '../../../../../services/my_easyloading.dart';
class AddSiswaDialogViewModel extends CustomBaseViewModel {
final log = getLogger('AddSiswaDialogViewModel');
final _httpService = locator<MyHttpServices>();
final easyLoading = locator<MyEasyLoading>();
List<String> 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<FormState>();
TextEditingController namaController = TextEditingController();
TextEditingController tanggalLahirController = TextEditingController();
TextEditingController tempatLahirController = TextEditingController();
TextEditingController alamatController = TextEditingController();
TextEditingController noTelponController = TextEditingController();
TextEditingController agamaController = TextEditingController();
TextEditingController kewarganegaraanController = TextEditingController();
TextEditingController pendidikanSDController = TextEditingController();
TextEditingController pendidikanSMPController = TextEditingController();
TextEditingController pendidikanSMAController = TextEditingController();
TextEditingController kemampuanController = TextEditingController();
TextEditingController hobiController = TextEditingController();
Future<void> 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<bool> postData() async {
if (imageBytes == null) {
easyLoading.showError('Foto belum dipilih');
return false;
}
easyLoading.customLoading('Menambahkan data...');
try {
var formData = FormData.fromMap({
'nama': namaController.text,
'jenis_kelamin': jenisKelamin,
'tanggal_lahir': tanggalLahirController.text,
'tempat_lahir': tempatLahirController.text,
'alamat': alamatController.text,
'no_telpon': noTelponController.text,
'agama': agamaController.text,
'kewarganegaraan': kewarganegaraanController.text,
'pendidikan_sd': pendidikanSDController.text,
'pendidikan_smp': pendidikanSMPController.text,
'pendidikan_sma': pendidikanSMAController.text,
'kemampuan': kemampuanController.text,
'hobi': hobiController.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,
// ),
// );
}
}

View File

@ -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,
)),
);
},
);

View File

@ -24,60 +24,75 @@ class AdminIndexTrackingView extends StatelessWidget {
AdminIndexTrackingViewModel model,
Widget? child,
) {
return Scaffold(
appBar: AppBar(
title: Text(
model.header,
style: const TextStyle(
color: Colors.white,
fontSize: 20,
return WillPopScope(
onWillPop: () async {
return false;
},
child: Scaffold(
appBar: AppBar(
title: Text(
model.header,
style: const TextStyle(
color: Colors.white,
fontSize: 20,
),
),
backgroundColor: mainColor,
elevation: 0,
automaticallyImplyLeading: false,
actions: [
IconButton(
onPressed: () {
model.logout(context);
},
icon: const Icon(Icons.logout, color: Colors.white),
),
],
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
child: ExtendedNavigator(
navigatorKey: StackedService.nestedNavigationKey(3),
router: AdminIndexTrackingViewRouter(),
initialRoute: AdminIndexTrackingViewRoutes.danaSosialAdminView,
),
),
backgroundColor: mainColor,
elevation: 0,
automaticallyImplyLeading: false,
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
child: ExtendedNavigator(
navigatorKey: StackedService.nestedNavigationKey(3),
router: AdminIndexTrackingViewRouter(),
),
),
bottomNavigationBar: StylishBottomBar(
items: [
for (var item in model.bottomNavBarList)
BottomBarItem(
icon: Icon(item['icon'],
color: model.currentIndex ==
model.bottomNavBarList.indexOf(item)
? sixthGrey
: backgroundColor),
title: Text(
item['name'],
style: regularTextStyle.copyWith(
color: model.currentIndex ==
model.bottomNavBarList.indexOf(item)
? sixthGrey
: Colors.grey,
bottomNavigationBar: StylishBottomBar(
items: [
for (var item in model.bottomNavBarList)
BottomBarItem(
icon: Icon(item['icon'],
color: model.currentIndex ==
model.bottomNavBarList.indexOf(item)
? sixthGrey
: backgroundColor),
title: Text(
model.otherFunction.capitalizeEachWord(item['name']),
style: regularTextStyle.copyWith(
color: model.currentIndex ==
model.bottomNavBarList.indexOf(item)
? sixthGrey
: Colors.grey,
overflow: TextOverflow.ellipsis,
),
),
backgroundColor: model.currentIndex ==
model.bottomNavBarList.indexOf(item)
? Colors.white
: Colors.grey,
),
backgroundColor:
model.currentIndex == model.bottomNavBarList.indexOf(item)
? Colors.white
: Colors.grey,
),
],
currentIndex: model.currentIndex,
hasNotch: true,
backgroundColor: mainColor,
onTap: (value) {
model.handleNavigation(value);
},
option: BubbleBarOptions(
barStyle: BubbleBarStyle.horizotnal,
bubbleFillStyle: BubbleFillStyle.fill,
opacity: 0.3),
],
currentIndex: model.currentIndex,
hasNotch: true,
backgroundColor: mainColor,
onTap: (value) {
model.handleNavigation(value);
},
option: BubbleBarOptions(
barStyle: BubbleBarStyle.horizotnal,
bubbleFillStyle: BubbleFillStyle.fill,
opacity: 0.3),
),
),
);
},

View File

@ -1,27 +1,40 @@
import 'package:flutter/material.dart';
import 'package:panti_asuhan/app/app.router.dart';
import 'package:panti_asuhan/services/other_function.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
import '../../../app/app.locator.dart';
import '../../../app/app.logger.dart';
import '../../../app/app.router.dart';
class AdminIndexTrackingViewModel extends IndexTrackingViewModel {
final log = getLogger('AdminIndexTrackingViewModel');
final _navigationService = locator<NavigationService>();
final _dialogService = locator<DialogService>();
final Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
final otherFunction = locator<OtherFunction>();
final _bottomNavBarList = [
{
'name': 'Siswa',
'name': 'List',
'icon': Icons.people_alt_outlined,
'header': 'List Siswa'
'header': 'List Anak Panti'
},
{'name': 'Dana Sosial', 'icon': Icons.money, 'header': 'Dana Sosial'},
{'name': 'Dana', 'icon': Icons.money, 'header': 'Dana Sosial'},
// {'name': 'Dana', 'icon': Icons.money, 'header': 'Dana Sosial Khusus'},
{
'name': 'Profil',
'icon': Icons.list_alt_rounded,
'icon': Icons.person_4_outlined,
'header': 'Profil Panti Asuhan'
}
},
{'name': 'V & M', 'icon': Icons.list_alt_rounded, 'header': 'Visi & Misi'},
{'name': 'Sejarah', 'icon': Icons.list_outlined, 'header': 'Sejarah'},
{
'name': 'S O',
'icon': Icons.people_alt_outlined,
'header': 'Struktur Organisasi'
},
];
List<Map<String, dynamic>> get bottomNavBarList => _bottomNavBarList;
@ -29,13 +42,23 @@ class AdminIndexTrackingViewModel extends IndexTrackingViewModel {
final List<String> _views = [
AdminIndexTrackingViewRoutes.dataSiswaView,
AdminIndexTrackingViewRoutes.danaSosialAdminView,
// AdminIndexTrackingViewRoutes.danaSosialKhususView,
AdminIndexTrackingViewRoutes.profilView,
AdminIndexTrackingViewRoutes.visiMisiView,
AdminIndexTrackingViewRoutes.sejarahView,
AdminIndexTrackingViewRoutes.strukturOrganisasiView
];
String header = 'Dana Sosial';
Future<void> 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) {
@ -51,4 +74,31 @@ class AdminIndexTrackingViewModel extends IndexTrackingViewModel {
id: 3,
);
}
logout(BuildContext context) {
_dialogService
.showConfirmationDialog(
title: 'Logout',
description: 'Apakah anda yakin ingin logout?',
cancelTitle: 'Ya',
confirmationTitle: 'Tidak',
// barrierDismissible: true,
)
.then((value) {
if (!value!.confirmed) {
log.d('logout');
_prefs.then((SharedPreferences prefs) {
prefs.setBool('isLogin', false);
prefs.remove('role');
_navigationService.clearStackAndShow(Routes.loginScreenView);
});
} else {
// _navigationService.back();
// close dialog
Navigator.pop(context);
// Navigator.of(StackedService.navigatorKey! as BuildContext).pop();
// log.d('cancel logout');
}
});
}
}

View File

@ -1,8 +1,9 @@
import 'package:flutter/material.dart';
import 'package:panti_asuhan/app/themes/app_colors.dart';
import 'package:stacked/stacked.dart';
import '../../../../app/themes/app_colors.dart';
import '../../../../app/themes/app_text.dart';
import '../../../../services/other_function.dart';
import './dana_sosial_admin_view_model.dart';
class DanaSosialAdminView extends StatelessWidget {
@ -21,14 +22,114 @@ class DanaSosialAdminView extends StatelessWidget {
Widget? child,
) {
return Scaffold(
body: Column(
body: SingleChildScrollView(
child: Column(
children: [
const SizedBox(height: 10),
Text(
"Laporan Harian",
style:
boldTextStyle.copyWith(fontSize: 15, color: Colors.black),
),
Container(
padding:
const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
width: double.infinity,
padding: const EdgeInsets.all(10),
height: MediaQuery.of(context).size.height * 0.45,
decoration: BoxDecoration(
color: mainColor,
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: (model.danaSosialModelList.isEmpty)
? Center(
child: Text(
'Tidak ada data',
style: boldTextStyle.copyWith(
fontSize: 20,
),
),
)
: const TheDataNewly(),
),
const SizedBox(
height: 30,
),
Text(
"Laporan Bulanan",
style:
boldTextStyle.copyWith(fontSize: 15, color: Colors.black),
),
Container(
padding: const EdgeInsets.all(15),
width: double.infinity,
height: MediaQuery.of(context).size.height * 0.25,
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: model.monthIncomeOutcome.isEmpty
? const Center(
child: Text(
'Tidak ada data',
))
: const HasilIncomeOutcome(),
),
const SizedBox(
height: 30,
),
Text(
"Laporan Tahunan",
style:
boldTextStyle.copyWith(fontSize: 15, color: Colors.black),
),
Container(
padding: const EdgeInsets.all(15),
width: double.infinity,
height: MediaQuery.of(context).size.height * 0.25,
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: model.yearIncomeOutcome.isNotEmpty
? const TahunanWidget()
: const Center(
child: Text(
'Tidak ada data',
),
),
),
const SizedBox(
height: 30,
),
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
@ -41,84 +142,758 @@ class DanaSosialAdminView extends StatelessWidget {
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Dana Sosial Bulan Ini',
style: boldTextStyle.copyWith(
color: Colors.white,
fontSize: 20,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'Total Pemasukan :',
),
const Expanded(child: SizedBox()),
Text(
'Rp. ${OtherFunction().commaFormat(model.totalIncome)}',
)
],
),
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Total Dana Sosial',
style: regularTextStyle.copyWith(
color: Colors.white,
fontSize: 15,
),
const Text(
'Total Pengeluaran :',
),
const Expanded(child: SizedBox()),
Text(
'Rp. 1.000.000',
style: regularTextStyle.copyWith(
color: Colors.white,
fontSize: 15,
),
),
'Rp. ${OtherFunction().commaFormat(model.totalOutcome)}',
)
],
),
],
),
),
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'),
),
),
);
},
),
),
const SizedBox(
height: 30,
),
],
),
floatingActionButton: const FloatingActionButton(
onPressed: null,
child: Icon(Icons.add),
));
),
floatingActionButton: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
if (model.role == 'admin')
FloatingActionButton(
mini: true,
heroTag: 'btn11',
onPressed: () {
model.goToTambahDanaSosial();
},
child: const Icon(Icons.add),
),
const SizedBox(width: 5),
FloatingActionButton(
mini: true,
heroTag: 'btn22',
onPressed: () {
model.filterDialog(context);
},
child: const Icon(Icons.filter_list),
),
],
),
floatingActionButtonLocation: FloatingActionButtonLocation.miniEndTop,
);
},
);
}
}
class TahunanWidget extends ViewModelWidget<DanaSosialAdminViewModel> {
const TahunanWidget({
super.key,
});
@override
Widget build(BuildContext context, DanaSosialAdminViewModel viewModel) {
return SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
for (int i = 0; i < viewModel.yearIncomeOutcome.length; i++)
Column(
mainAxisSize: MainAxisSize.min,
children: [
Align(
alignment: Alignment.centerLeft,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"Tahun ${viewModel.yearIncomeOutcome[i]['tahun']}",
style: boldTextStyle.copyWith(
decoration: TextDecoration.underline,
fontSize: 17,
),
),
const SizedBox(
width: 10,
),
if (viewModel.role == 'admin' ||
viewModel.role == 'pimpinan')
Container(
alignment: Alignment.center,
width: 30,
height: 30,
decoration: BoxDecoration(
color: mainColor,
borderRadius: BorderRadius.circular(50),
),
child: IconButton(
onPressed: () async {
// sini untuk laporan bulanan
await viewModel.goToLaporanTahunan(
viewModel.yearIncomeOutcome[i]['tahun']);
},
icon: const Icon(
Icons.list_alt_outlined,
color: Colors.white,
size: 15,
),
),
)
],
),
),
const SizedBox(
height: 5,
),
Table(
border: TableBorder.all(
color: Colors.grey,
),
children: [
const TableRow(children: [
TableCell(
child: Center(
child: Text(
'Pemasukan',
style: boldTextStyle,
))),
TableCell(
child: Center(
child: Text(
'Pengeluaran',
style: boldTextStyle,
)))
]),
TableRow(children: [
TableCell(
child: Center(
child: Text(
'Rp. ${OtherFunction().commaFormat(viewModel.yearIncomeOutcome[i]['pemasukan'])}',
style: italicTextStyle,
))),
TableCell(
child: Center(
child: Text(
'Rp. ${OtherFunction().commaFormat(viewModel.yearIncomeOutcome[i]['pengeluaran'])}',
style: italicTextStyle,
)))
]),
],
),
const SizedBox(
height: 5,
),
const Divider(
color: Colors.grey,
thickness: 1,
),
const SizedBox(
height: 5,
),
],
)
],
),
);
}
}
class HasilIncomeOutcome extends ViewModelWidget<DanaSosialAdminViewModel> {
const HasilIncomeOutcome({
super.key,
});
@override
Widget build(BuildContext context, DanaSosialAdminViewModel viewModel) {
return SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
for (int i = 0; i < viewModel.monthIncomeOutcome.length; i++)
Column(
mainAxisSize: MainAxisSize.min,
children: [
Align(
alignment: Alignment.centerLeft,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
viewModel.otherFunction.changeMonthYear(
viewModel.monthIncomeOutcome[i]['month']),
style: boldTextStyle.copyWith(
decoration: TextDecoration.underline,
fontSize: 17,
),
),
const SizedBox(
width: 10,
),
if (viewModel.role == 'admin' ||
viewModel.role == 'pimpinan')
Container(
alignment: Alignment.center,
width: 30,
height: 30,
decoration: BoxDecoration(
color: mainColor,
borderRadius: BorderRadius.circular(50),
),
child: IconButton(
onPressed: () async {
// sini untuk laporan bulanan
await viewModel.goToLaporanBulanan(
viewModel.monthIncomeOutcome[i]['month']);
},
icon: const Icon(
Icons.list_alt_outlined,
color: Colors.white,
size: 15,
),
),
)
],
),
),
const SizedBox(
height: 5,
),
Table(
border: TableBorder.all(
color: Colors.grey,
),
children: [
const TableRow(children: [
TableCell(
child: Center(
child: Text(
'Pemasukan',
style: boldTextStyle,
))),
TableCell(
child: Center(
child: Text(
'Pengeluaran',
style: boldTextStyle,
)))
]),
TableRow(children: [
TableCell(
child: Center(
child: Text(
'Rp. ${OtherFunction().commaFormat(viewModel.monthIncomeOutcome[i]['income'])}',
style: italicTextStyle,
))),
TableCell(
child: Center(
child: Text(
'Rp. ${OtherFunction().commaFormat(viewModel.monthIncomeOutcome[i]['outcome'])}',
style: italicTextStyle,
)))
]),
],
),
const SizedBox(
height: 5,
),
const Divider(
color: Colors.grey,
thickness: 1,
),
const SizedBox(
height: 5,
),
],
)
],
),
);
}
}
class TheData extends ViewModelWidget<DanaSosialAdminViewModel> {
const TheData({
super.key,
});
@override
Widget build(BuildContext context, DanaSosialAdminViewModel viewModel) {
return ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
itemCount: viewModel.danaSosialModelList.length,
itemBuilder: (context, index) {
// viewModel.log.i(viewModel.danaSosialModelList[index].tanggal);
String jumlahDonasi = viewModel
.danaSosialModelList[index].jenisDonasi !=
'Barang'
? OtherFunction().commaFormat(
int.parse(viewModel.danaSosialModelList[index].jumlah ?? '0'))
: '0';
return Card(
child: ListTile(
title: Text(viewModel.danaSosialModelList[index].tanggal ?? '',
style: boldTextStyle.copyWith(fontSize: 13, color: mainColor)),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
viewModel.danaSosialModelList[index].bentuk == 'Pemasukan'
? 'Pemasukan'
: 'Pengeluaran',
style: boldTextStyle.copyWith(
fontSize: 14,
color: viewModel.danaSosialModelList[index].bentuk ==
'Pemasukan'
? Colors.green
: Colors.red,
),
),
Text(
viewModel.danaSosialModelList[index].nama ?? '-',
style: regularTextStyle.copyWith(
fontSize: 13,
color: viewModel.danaSosialModelList[index].bentuk ==
'Pemasukan'
? Colors.green
: Colors.red,
),
),
Text(
// viewModel.danaSosialModelList[index].jenisDonasi == 'Uang'
// ? (viewModel.isLogin == true ? 'Rp. $jumlahDonasi' : '-')
// : 'Donasi Barang',
viewModel.danaSosialModelList[index].bentuk == 'Pemasukan'
? (viewModel.danaSosialModelList[index].jenisDonasi ==
'Uang'
? (viewModel.isLogin == true
? 'Rp. $jumlahDonasi'
: '-')
: 'Donasi Barang')
: (viewModel.danaSosialModelList[index].jenisDonasi ==
'Uang'
? (viewModel.isLogin == true
? 'Rp. $jumlahDonasi'
: '-')
: 'Pengeluaran Barang'),
style: regularTextStyle.copyWith(
fontSize: 13,
color: viewModel.danaSosialModelList[index].bentuk ==
'Pemasukan'
? Colors.green
: Colors.red,
),
),
Text(
viewModel.danaSosialModelList[index].status ?? '',
style: regularTextStyle.copyWith(
fontSize: 13,
color: viewModel.danaSosialModelList[index].status ==
'Belum Dikonfirmasi'
? Colors.red
: Colors.green,
),
),
],
),
trailing: viewModel.isLogin == null
? null
: (viewModel.isLogin == true
? Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: mainColor,
borderRadius: BorderRadius.circular(50),
),
child: IconButton(
onPressed: () {
viewModel.goToEditDanaSosial(int.parse(viewModel
.danaSosialModelList[index].idDanaSosial!));
},
icon: const Icon(
Icons.edit,
color: Colors.white,
),
),
),
const SizedBox(width: 10),
Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(50),
),
child: IconButton(
onPressed: () {
viewModel.deleteData(int.parse(viewModel
.danaSosialModelList[index].idDanaSosial!));
},
icon: const Icon(
Icons.delete,
color: Colors.white,
),
),
),
],
)
: null),
),
);
},
);
}
}
class TheDataNewly extends ViewModelWidget<DanaSosialAdminViewModel> {
const TheDataNewly({
super.key,
});
@override
Widget build(BuildContext context, DanaSosialAdminViewModel viewModel) {
return ListView.builder(
itemCount: viewModel.filteredByDateData.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.only(bottom: 30),
child: Container(
padding: const EdgeInsets.all(5),
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(10)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${viewModel.otherFunction.changeMonthYear(viewModel.filteredByDateData[index]['month'])} ',
style: boldTextStyle.copyWith(
decoration: TextDecoration.underline,
fontSize: 17,
),
),
const SizedBox(height: 10),
for (int i = 0;
i < viewModel.filteredByDateData[index]['data'].length;
i++)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'${viewModel.filteredByDateData[index]['data'][i]['date']} : ${viewModel.otherFunction.getDayOfWeek(viewModel.filteredByDateData[index]['data'][i]['date'])}',
style: italicTextStyle.copyWith(
fontWeight: FontWeight.bold, fontSize: 15),
),
const SizedBox(
width: 10,
),
if (viewModel.role == 'admin' ||
viewModel.role == 'pimpinan')
Container(
alignment: Alignment.center,
width: 35,
height: 35,
decoration: BoxDecoration(
color: mainColor,
borderRadius: BorderRadius.circular(35),
),
child: IconButton(
onPressed: () {
viewModel.getLaporanHarian(
viewModel.filteredByDateData[index]
['data'][i]['date'],
viewModel.otherFunction.getDayOfWeek(
viewModel.filteredByDateData[index]
['data'][i]['date']));
},
icon: const Icon(
Icons.list_alt_outlined,
color: Colors.white,
size: 20,
),
),
)
],
),
const SizedBox(
height: 10,
),
Table(
border: TableBorder.all(
color: Colors.grey,
),
columnWidths: viewModel.role == 'admin'
? const {
0: FlexColumnWidth(
1), // 1/3 of the available width
1: FlexColumnWidth(
1.5), // 2/3 of the available width
2: FlexColumnWidth(
2.5), // 2/3 of the available width
3: FlexColumnWidth(
3), // 2/3 of the available width
4: FlexColumnWidth(
2), // 2/3 of the available width
}
: const {
0: FlexColumnWidth(
1), // 1/3 of the available width
1: FlexColumnWidth(
2), // 2/3 of the available width
2: FlexColumnWidth(
2), // 2/3 of the available width
3: FlexColumnWidth(
3), // 2/3 of the available width
},
children: [
TableRow(
children: [
const TableCell(
child: Center(
child: Text(
'No',
style: boldTextStyle,
),
),
),
const TableCell(
child: Center(
child: Text(
'Jenis',
style: boldTextStyle,
))),
const TableCell(
child: Center(
child: Text(
'Donatur',
style: boldTextStyle,
))),
const TableCell(
child: Center(
child: Text(
'Jumlah /\nKeterangan',
style: boldTextStyle,
),
),
),
if (viewModel.role == 'admin')
const TableCell(
child: Center(
child: Text(
'Aksi',
style: boldTextStyle,
),
),
)
],
),
for (int j = 0;
j <
viewModel
.filteredByDateData[index]['data'][i]
['data_dana']
.length;
j++)
TableRow(
children: [
TableCell(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 4,
horizontal: 4,
),
child: Center(
child: Text(
'${j + 1}',
style: regularTextStyle,
),
),
),
),
TableCell(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 4,
horizontal: 4,
),
child: Center(
// circle icon
child: viewModel
.filteredByDateData[index]
['data'][i]['data_dana']
[j]
.bentuk ==
'Pemasukan'
? const JenisIconContainer(
color: Colors.green,
icon: Icons.arrow_upward,
)
: const JenisIconContainer(
color: Colors.red,
icon: Icons.arrow_downward,
)),
),
),
TableCell(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 4,
horizontal: 4,
),
child: Center(
child: Text(
'${viewModel.filteredByDateData[index]['data'][i]['data_dana'][j].nama == '' ? '-' : viewModel.filteredByDateData[index]['data'][i]['data_dana'][j].nama}',
style: regularTextStyle,
)),
)),
TableCell(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 4,
horizontal: 4,
),
child: Center(
child: Text(
viewModel
.filteredByDateData[index]
['data'][i]['data_dana']
[j]
.jenisDonasi ==
'Uang'
? 'Rp. ${viewModel.otherFunction.commaFormat(int.parse(viewModel.filteredByDateData[index]['data'][i]['data_dana'][j].jumlah))}'
: viewModel
.filteredByDateData[index]
['data'][i]['data_dana'][j]
.keterangan,
style: regularTextStyle,
),
),
),
),
if (viewModel.role == 'admin')
TableCell(
child: Center(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 4,
horizontal: 4,
),
child: Wrap(
spacing: 5,
runSpacing: 10,
children: [
GestureDetector(
onTap: () {
viewModel.goToEditDanaSosial(
int.parse(viewModel
.filteredByDateData[
index]['data'][i]
['data_dana'][j]
.idDanaSosial));
},
child: JenisIconContainer(
color: Colors.blue[600]!,
icon: Icons.edit,
),
),
GestureDetector(
onTap: () {
viewModel.deleteData(
int.parse(viewModel
.filteredByDateData[index]
['data'][i]
['data_dana'][j]
.idDanaSosial!),
);
},
child: const JenisIconContainer(
color: Colors.red,
icon: Icons.delete,
),
),
],
),
),
),
)
],
),
],
),
const SizedBox(height: 10),
// create a horizontal line
const Divider(
color: Colors.grey,
thickness: 1.0,
),
const SizedBox(height: 10),
],
),
],
),
),
);
},
);
}
}
class JenisIconContainer extends StatelessWidget {
const JenisIconContainer({
super.key,
required this.color,
required this.icon,
});
final Color color;
final IconData icon;
@override
Widget build(BuildContext context) {
return Container(
width: 20,
height: 20,
alignment: Alignment.center,
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
),
child: Icon(
icon,
color: Colors.white,
size: 15,
),
);
}
}

View File

@ -1,8 +1,471 @@
import 'package:panti_asuhan/app/core/custom_base_view_model.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_file_downloader/flutter_file_downloader.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../../../app/app.dialogs.dart';
import '../../../../app/app.locator.dart';
import '../../../../app/app.logger.dart';
import '../../../../app/app.router.dart';
import '../../../../app/core/custom_base_view_model.dart';
import '../../../../model/dana_sosial_model.dart';
import '../../../../services/http_services.dart';
import '../../../../services/my_easyloading.dart';
import '../../../../services/other_function.dart';
class DanaSosialAdminViewModel extends CustomBaseViewModel {
final log = getLogger('DanaSosialAdminViewModel');
Future<void> init() async {}
final _httpService = locator<MyHttpServices>();
final easyLoading = locator<MyEasyLoading>();
// final otherF
String url = dotenv.env['url']!;
int bulan = DateTime.now().month;
List<DanaSosialModel> danaSosialModelList = [];
List<Map<String, dynamic>> filteredByDateData = []; // newly added
List<Map<String, dynamic>> monthIncomeOutcome = []; // newly added
List<Map<String, dynamic>> yearIncomeOutcome = []; // newly added
int totalIncome = 0; // newly added
int totalOutcome = 0; // newly added
String? role;
bool? isLogin;
int jumlahDonasi = 0;
int jumlahPengeluaran = 0;
Future<void> init() async {
await getData();
await getJumlahDonasi();
prefs.then((SharedPreferences prefs) {
role = prefs.getString('role');
isLogin = prefs.getBool('isLogin');
});
// log.i(bulan);
}
getJumlahDonasi() async {
setBusy(true);
easyLoading.showLoading();
// get the month
var bulan = DateTime.now().month;
// log.i(bulan);
// change bulan to string and add 0 if it is less than 10
String bulanString = bulan.toString().length == 1 ? '0$bulan' : '$bulan';
// log.i(bulanString);
try {
var response = await _httpService.get('pemasukan?bulan=$bulanString');
// log.i(response.data['jumlah']);
// var theJumlahDonasi = response.data['jumlah'];
jumlahDonasi = response.data['jumlah'];
jumlahPengeluaran = response.data['jumlah_pengeluaran'];
} catch (e) {
log.e(e);
} finally {
setBusy(false);
easyLoading.dismissLoading();
}
}
getData() async {
setBusy(true);
easyLoading.showLoading();
try {
var response = await _httpService.get('dana_sosial');
// log.i(response.data);
danaSosialModelList = [];
var datanya = response.data['data'];
// log.i(datanya.length);
if (datanya.length > 0) {
for (var item in datanya) {
danaSosialModelList.add(DanaSosialModel.fromJson(item));
}
}
changeByDate(danaSosialModelList);
getTahunan();
notifyListeners();
// log.i(danaSosialModelList.length);
} catch (e) {
log.e(e);
} finally {
setBusy(false);
easyLoading.dismissLoading();
}
}
getTahunan() async {
try {
var response = await _httpService.get('data_tahunan');
var datanya = response.data['data'];
log.i(datanya.length);
for (int i = 0; i < datanya.length; i++) {
log.i(datanya[i]);
yearIncomeOutcome.add(datanya[i]);
}
notifyListeners();
} catch (e) {
log.e(e);
}
}
//newly added
changeByDate(List<DanaSosialModel> data) {
filteredByDateData = [];
for (var item in data) {
var monthKey =
item.tanggal!.substring(0, 7); // Extracting the year and month
var dateKey = item.tanggal; // The full date
var monthData = filteredByDateData.firstWhere(
(element) => element['month'] == monthKey,
orElse: () => {
'month': monthKey,
'data': [],
});
var dateData =
monthData['data'].firstWhere((element) => element['date'] == dateKey,
orElse: () => {
'date': dateKey,
'data_dana': [],
});
dateData['data_dana'].add(item);
if (!monthData['data'].contains(dateData)) {
monthData['data'].add(dateData);
}
if (!filteredByDateData.contains(monthData)) {
filteredByDateData.add(monthData);
}
}
filteredByDateData.sort((a, b) => a['month'].compareTo(b['month']));
// log.i(filteredByDateData);
// group the data by month
monthIncomeOutcome = [];
for (var item in filteredByDateData) {
var income = 0;
var outcome = 0;
for (var dateData in item['data']) {
for (var danaSosialModel in dateData['data_dana']) {
if (danaSosialModel.bentuk == 'Pemasukan' &&
danaSosialModel.jenisDonasi == 'Uang') {
income += int.parse(danaSosialModel.jumlah ?? '0');
} else if (danaSosialModel.bentuk == 'Pengeluaran' &&
danaSosialModel.jenisDonasi == 'Uang') {
outcome += int.parse(danaSosialModel.jumlah ?? '0');
}
}
}
monthIncomeOutcome.add({
'month': item['month'],
'income': income,
'outcome': outcome,
});
}
// log.i(monthIncomeOutcome);
totalIncome = 0;
totalOutcome = 0;
for (var item in monthIncomeOutcome) {
totalIncome += int.parse(item['income'].toString());
totalOutcome += int.parse(item['outcome'].toString());
}
log.i(totalIncome);
log.i(totalOutcome);
}
goToTambahDanaSosial() {
navigationService.navigateTo(Routes.tambahDanaSosialView);
}
getFilter(String sql) async {
setBusy(true);
easyLoading.showLoading();
FormData formData = FormData.fromMap({
'sql': sql,
});
try {
var response = await _httpService.postWithFormData(
'filter_dana',
formData,
);
// log.i(response.data);
danaSosialModelList = [];
var datanya = response.data['data'];
jumlahDonasi = response.data['jumlah_donasi'];
jumlahPengeluaran = response.data['jumlah_pengeluaran'];
// log.i(datanya.length);
if (datanya.length > 0) {
for (var item in datanya) {
danaSosialModelList.add(DanaSosialModel.fromJson(item));
}
}
setBusy(false);
changeByDate(danaSosialModelList);
notifyListeners();
// log.i(danaSosialModelList);
} catch (e) {
log.e(e);
setBusy(false);
} finally {
easyLoading.dismissLoading();
}
}
filterDialog(BuildContext context) async {
// create a dialog
final res = await dialogService.showCustomDialog(
variant: DialogType.filterDialogView,
);
if (res!.confirmed) {
String jenisDonasi = res.data['jenisDonasi'] == 'Semua'
? ''
: "jenis = '${res.data['jenisDonasi']}' and ";
String bulan = res.data['bulan'] == 'Semua'
? ''
: "tanggal like '%-${OtherFunction().changeMonth(res.data['bulan'])}-%' and ";
String tahun = res.data['tahun'] == 'Semua'
? ''
: 'tanggal like "%${res.data['tahun']}-%" and ';
String status = res.data['status'] == 'Semua'
? ''
: (res.data['status'] == 'Belum Dikonfirmasi')
? 'status = 0'
: 'status = 1';
if (jenisDonasi == 'Semua' &&
bulan == 'Semua' &&
tahun == 'Semua' &&
status == 'Semua') {
getData();
return;
}
String sql =
'Select * from tb_dana_sosial where $jenisDonasi$bulan$tahun$status';
// check the last 3 character if it is 'or ' then remove it
// if (sql.substring(sql.length - 3) == 'or ') {
// sql = sql.substring(0, sql.length - 3);
// }
if (sql.substring(sql.length - 4) == 'and ') {
sql = sql.substring(0, sql.length - 4);
}
// log.i(sql);
getFilter(sql);
}
}
goToEditDanaSosial(int id) async {
navigationService.navigateTo(
Routes.detailDanaSosialView,
arguments: DetailDanaSosialViewArguments(
id: id,
),
);
}
deleteData(int parse) async {
await dialogService
.showDialog(
title: 'Hapus Data',
description: 'Apakah anda yakin ingin menghapus data ini?',
buttonTitle: 'Hapus',
cancelTitle: 'Batal',
buttonTitleColor: Colors.red,
cancelTitleColor: Colors.green,
)
.then(
(value) async {
if (value!.confirmed) {
easyLoading.showLoading();
setBusy(true);
try {
await _httpService.postWithFormData(
'hapus_dana_sosial',
FormData.fromMap({
'id_dana_sosial': parse,
}));
// log.i(response.data);
easyLoading.dismissLoading();
easyLoading.showSuccess('Data berhasil dihapus');
getData();
getJumlahDonasi();
} on DioError catch (e) {
// easyLoading.dismissLoading();
log.e(e);
easyLoading.showError('Terjadi kesalahan');
} finally {
setBusy(false);
easyLoading.dismissLoading();
}
} else {
// log.i('cancel');
return;
}
},
);
}
goToLaporanBulanan(data) async {
// seeperate by "-", the first index is year, the second index is month
String year = data.substring(0, data.indexOf('-'));
String month = data.substring(data.indexOf('-') + 1);
try {
setBusy(true);
easyLoading.customLoading("Mengunduh Laporan Bulanan...");
await _httpService.get('laporan_bulanan?tahun=$year&bulan=$month');
// if (response.data) {
String urlPdf = '${url}assets/pdf/laporan_bulanan_$month,$year.pdf';
log.i(urlPdf);
FileDownloader.downloadFile(
url: urlPdf,
// name: "THE FILE NAME AFTER DOWNLOADING", //(optional)
onProgress: (fileName, progress) {
// change progress to 0-1
double progressPercent = progress / 100;
easyLoading.showProgress(
progressPercent,
"Downloading: $progress%",
);
},
onDownloadCompleted: (String path) {
easyLoading.dismissLoading();
snackbarService.showSnackbar(
message: "Laporan Bulanan Berhasil Tersimpan di $path",
duration: const Duration(seconds: 3),
);
},
onDownloadError: (String error) {
// log.i('DOWNLOAD ERROR: $error');
snackbarService.showSnackbar(
message: "Laporan Bulanan Gagal Tersimpan: $error",
duration: const Duration(seconds: 3),
);
});
// }
} catch (e) {
log.e(e);
} finally {
setBusy(false);
easyLoading.dismissLoading();
}
}
getLaporanHarian(String filteredByDateData, String dayOfWeek) async {
String date = filteredByDateData;
String day = dayOfWeek;
try {
setBusy(true);
easyLoading.customLoading("Mengunduh Laporan Harian...");
await _httpService.get('laporan_harian?tanggal=$date&hari=$day');
String urlPdf = '${url}assets/pdf/laporan_harian_$day,$date.pdf';
log.i(urlPdf);
FileDownloader.downloadFile(
url: urlPdf,
// name: "THE FILE NAME AFTER DOWNLOADING", //(optional)
onProgress: (fileName, progress) {
// change progress to 0-1
double progressPercent = progress / 100;
easyLoading.showProgress(
progressPercent,
"Downloading: $progress%",
);
},
onDownloadCompleted: (String path) {
easyLoading.dismissLoading();
snackbarService.showSnackbar(
message: "Laporan Harian Berhasil Tersimpan di $path",
duration: const Duration(seconds: 3),
);
},
onDownloadError: (String error) {
// log.i('DOWNLOAD ERROR: $error');
snackbarService.showSnackbar(
message: "Laporan Harian Gagal Tersimpan: $error",
duration: const Duration(seconds: 3),
);
});
} catch (e) {
log.e(e);
} finally {
setBusy(false);
easyLoading.dismissLoading();
}
}
goToLaporanTahunan(String yearIncomeOutcome) async {
// log.i(yearIncomeOutcome);
try {
setBusy(true);
easyLoading.customLoading("Mengunduh Laporan Tahun...");
await _httpService.get('laporan_tahunan?tahun=$yearIncomeOutcome');
// if (response.data) {
String urlPdf = '${url}assets/pdf/laporan_tahunan_$yearIncomeOutcome.pdf';
log.i(urlPdf);
FileDownloader.downloadFile(
url: urlPdf,
// name: "THE FILE NAME AFTER DOWNLOADING", //(optional)
onProgress: (fileName, progress) {
// change progress to 0-1
double progressPercent = progress / 100;
easyLoading.showProgress(
progressPercent,
"Downloading: $progress%",
);
},
onDownloadCompleted: (String path) {
easyLoading.dismissLoading();
snackbarService.showSnackbar(
message: "Laporan Tahunan Berhasil Tersimpan di $path",
duration: const Duration(seconds: 3),
);
},
onDownloadError: (String error) {
// log.i('DOWNLOAD ERROR: $error');
snackbarService.showSnackbar(
message: "Laporan Tahunan Gagal Tersimpan: $error",
duration: const Duration(seconds: 3),
);
});
// }
} catch (e) {
log.e(e);
} finally {
setBusy(false);
easyLoading.dismissLoading();
}
}
}

View File

@ -0,0 +1,80 @@
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
import 'package:validatorless/validatorless.dart';
import '../../../../widgets/my_button.dart';
import '../../../../widgets/my_textformfield.dart';
import './add_donatur_dialog_view_model.dart';
class AddDonaturDialogView extends StatelessWidget {
final DialogRequest? request;
final Function(DialogResponse)? completer;
const AddDonaturDialogView({
Key? key,
this.request,
this.completer,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return ViewModelBuilder<AddDonaturDialogViewModel>.reactive(
viewModelBuilder: () => AddDonaturDialogViewModel(),
onViewModelReady: (AddDonaturDialogViewModel model) async {
await model.init();
},
builder: (
BuildContext context,
AddDonaturDialogViewModel model,
Widget? child,
) {
return Dialog(
child: Container(
padding: const EdgeInsets.all(20),
child: Form(
key: model.formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'Tambah Donatur',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 20),
MyTextFormField(
controller: model.namaController,
hintText: 'Masukkan Nama Donatur',
labelText: 'Nama Donatur',
validator: Validatorless.required(
'Nama Donatur tidak boleh kosong'),
),
const SizedBox(height: 20),
SizedBox(
width: 200,
child: MyButton(
text: 'Tambah',
onPressed: () async {
if (model.formKey.currentState!.validate()) {
// hide keyboard
FocusScope.of(context).unfocus();
bool res = await model.addDonatur();
if (res) {
completer!(DialogResponse(confirmed: true));
}
}
},
),
),
],
),
),
),
);
},
);
}
}

View File

@ -0,0 +1,39 @@
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import '../../../../../app/app.locator.dart';
import '../../../../../app/app.logger.dart';
import '../../../../../app/core/custom_base_view_model.dart';
import '../../../../../services/http_services.dart';
import '../../../../../services/my_easyloading.dart';
class AddDonaturDialogViewModel extends CustomBaseViewModel {
final log = getLogger('AddDonaturDialogViewModel');
final _httpService = locator<MyHttpServices>();
final easyLoading = locator<MyEasyLoading>();
final formKey = GlobalKey<FormState>();
TextEditingController namaController = TextEditingController();
Future<void> init() async {}
Future<bool> addDonatur() async {
setBusy(true);
easyLoading.showLoading();
try {
await _httpService.postWithFormData(
'donatur',
FormData.fromMap({
'nama_donatur': namaController.text,
}));
return true;
} catch (e) {
log.e(e);
return false;
} finally {
setBusy(false);
easyLoading.dismissLoading();
}
}
}

View File

@ -0,0 +1,737 @@
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import '../../../../app/themes/app_colors.dart';
import '../../../../app/themes/app_text.dart';
import '../../../../services/other_function.dart';
import '../dana_sosial_admin/dana_sosial_admin_view.dart';
import './dana_sosial_khusus_view_model.dart';
class DanaSosialKhususView extends StatelessWidget {
const DanaSosialKhususView({super.key});
@override
Widget build(BuildContext context) {
return ViewModelBuilder<DanaSosialKhususViewModel>.reactive(
viewModelBuilder: () => DanaSosialKhususViewModel(),
onViewModelReady: (DanaSosialKhususViewModel model) async {
await model.init();
},
builder: (
BuildContext context,
DanaSosialKhususViewModel model,
Widget? child,
) {
return Scaffold(
body: Column(
children: [
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Expanded(
// child: 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. ${OtherFunction().commaFormat(model.jumlahDonasi)}',
// style: regularTextStyle.copyWith(
// color: Colors.white,
// fontSize: 15,
// ),
// ),
// ],
// ),
// const SizedBox(height: 10),
// Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Text(
// 'Pengeluaran Dana Sosial',
// style: regularTextStyle.copyWith(
// color: Colors.white,
// fontSize: 15,
// ),
// ),
// Text(
// 'Rp. ${OtherFunction().commaFormat(model.jumlahPengeluaran)}',
// style: regularTextStyle.copyWith(
// color: Colors.white,
// fontSize: 15,
// ),
// ),
// ],
// ),
// ],
// ),
// ),
// ),
// const SizedBox(width: 10),
// Container(
// width: 50,
// height: 50,
// decoration: const BoxDecoration(
// shape: BoxShape.circle,
// color: mainColor,
// ),
// child: IconButton(
// icon: const Icon(
// Icons.filter_list,
// color: Colors.white,
// ),
// onPressed: () {
// model.filterDialog(context);
// },
// ),
// ),
// ],
// ),
const SizedBox(height: 10),
Expanded(
flex: 4,
child: Container(
padding: const EdgeInsets.all(10),
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: (model.danaSosialModelList.isEmpty)
? Center(
child: Text(
'Tidak ada data',
style: boldTextStyle.copyWith(
fontSize: 20,
),
),
)
: const TheDataNewly(),
),
),
const SizedBox(
height: 20,
),
Expanded(
flex: 2,
child: Container(
padding: const EdgeInsets.all(15),
width: double.infinity,
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: model.monthIncomeOutcome.isEmpty
? const Center(
child: Text(
'Tidak ada data',
))
: const HasilIncomeOutcome(),
),
),
const SizedBox(
height: 20,
),
Container(
padding: const EdgeInsets.all(20),
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: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'Total Pemasukan :',
),
const Expanded(child: SizedBox()),
Text(
'Rp. ${OtherFunction().commaFormat(model.totalIncome)}',
)
],
),
),
],
),
floatingActionButton: model.role == 'admin'
? Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
if (model.role == 'admin')
FloatingActionButton(
heroTag: 'btn1',
onPressed: () {
model.goToTambahDanaSosial();
},
child: const Icon(Icons.add),
),
if (model.role == 'admin') const SizedBox(width: 10),
if (model.role == 'admin')
FloatingActionButton(
heroTag: 'btn2',
onPressed: () async {
bool res = await model.addDonatur();
model.log.i(res);
},
child: const Icon(Icons.person_add_alt),
),
const SizedBox(width: 10),
FloatingActionButton(
heroTag: 'btn3',
onPressed: () {
model.filterDialog(context);
},
child: const Icon(Icons.filter_list),
),
],
)
: null,
floatingActionButtonLocation: FloatingActionButtonLocation.miniEndTop,
);
},
);
}
}
class TheData extends ViewModelWidget<DanaSosialKhususViewModel> {
const TheData({
super.key,
});
@override
Widget build(BuildContext context, DanaSosialKhususViewModel viewModel) {
return ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 10),
itemCount: viewModel.danaSosialModelList.length,
itemBuilder: (context, index) {
String jumlahDonasi = viewModel
.danaSosialModelList[index].jenisDonasi !=
'Barang'
? OtherFunction().commaFormat(
int.parse(viewModel.danaSosialModelList[index].jumlah ?? '0'))
: '0';
return Card(
child: ListTile(
title: Text(viewModel.danaSosialModelList[index].tanggal ?? '',
style: boldTextStyle.copyWith(fontSize: 13, color: mainColor)),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
viewModel.danaSosialModelList[index].bentuk == 'Pemasukan'
? 'Pemasukan'
: 'Pengeluaran',
style: boldTextStyle.copyWith(
fontSize: 14,
color: viewModel.danaSosialModelList[index].bentuk ==
'Pemasukan'
? Colors.green
: Colors.red,
),
),
Text(
viewModel.danaSosialModelList[index].nama ?? '-',
style: regularTextStyle.copyWith(
fontSize: 13,
color: viewModel.danaSosialModelList[index].bentuk ==
'Pemasukan'
? Colors.green
: Colors.red,
),
),
Text(
// viewModel.danaSosialModelList[index].jenisDonasi == 'Uang'
// ? (viewModel.isLogin == true ? 'Rp. $jumlahDonasi' : '-')
// : 'Donasi Barang',
viewModel.danaSosialModelList[index].bentuk == 'Pemasukan'
? (viewModel.danaSosialModelList[index].jenisDonasi ==
'Uang'
? (viewModel.isLogin == true
? 'Rp. $jumlahDonasi'
: '-')
: 'Donasi Barang')
: (viewModel.danaSosialModelList[index].jenisDonasi ==
'Uang'
? (viewModel.isLogin == true
? 'Rp. $jumlahDonasi'
: '-')
: 'Pengeluaran Barang'),
style: regularTextStyle.copyWith(
fontSize: 13,
color: viewModel.danaSosialModelList[index].bentuk ==
'Pemasukan'
? Colors.green
: Colors.red,
),
),
Text(
viewModel.danaSosialModelList[index].status ?? '',
style: regularTextStyle.copyWith(
fontSize: 13,
color: viewModel.danaSosialModelList[index].status ==
'Belum Dikonfirmasi'
? Colors.red
: Colors.green,
),
),
],
),
trailing: viewModel.isLogin == null
? null
: (viewModel.isLogin == true
? Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: mainColor,
borderRadius: BorderRadius.circular(50),
),
child: IconButton(
onPressed: () {
viewModel.goToEditDanaSosial(int.parse(viewModel
.danaSosialModelList[index].idDanaSosial!));
},
icon: const Icon(
Icons.edit,
color: Colors.white,
),
),
),
const SizedBox(width: 10),
Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(50),
),
child: IconButton(
onPressed: () {
viewModel.deleteData(int.parse(viewModel
.danaSosialModelList[index].idDanaSosial!));
},
icon: const Icon(
Icons.delete,
color: Colors.white,
),
),
),
],
)
: null),
),
);
},
);
}
}
class TheDataNewly extends ViewModelWidget<DanaSosialKhususViewModel> {
const TheDataNewly({
super.key,
});
@override
Widget build(BuildContext context, DanaSosialKhususViewModel viewModel) {
return ListView.builder(
itemCount: viewModel.filteredByDateData.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.only(bottom: 30),
child: Container(
padding: const EdgeInsets.all(5),
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(10)),
boxShadow: [
BoxShadow(
color: mainGrey.withOpacity(0.2),
spreadRadius: 5,
blurRadius: 7,
offset: const Offset(0, 3), // changes position of shadow
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${viewModel.otherFunction.changeMonthYear(viewModel.filteredByDateData[index]['month'])} ',
style: boldTextStyle.copyWith(
decoration: TextDecoration.underline,
fontSize: 17,
),
),
const SizedBox(height: 10),
for (int i = 0;
i < viewModel.filteredByDateData[index]['data'].length;
i++)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${viewModel.filteredByDateData[index]['data'][i]['date']} : ${viewModel.otherFunction.getDayOfWeek(viewModel.filteredByDateData[index]['data'][i]['date'])}',
style: italicTextStyle.copyWith(
fontWeight: FontWeight.bold, fontSize: 15),
),
const SizedBox(
height: 10,
),
Table(
border: TableBorder.all(
color: Colors.grey,
),
columnWidths: viewModel.role == 'admin'
? const {
0: FlexColumnWidth(
1), // 1/3 of the available width
1: FlexColumnWidth(
1.5), // 2/3 of the available width
2: FlexColumnWidth(
2.5), // 2/3 of the available width
3: FlexColumnWidth(
3), // 2/3 of the available width
4: FlexColumnWidth(
2), // 2/3 of the available width
}
: const {
0: FlexColumnWidth(
1), // 1/3 of the available width
1: FlexColumnWidth(
2), // 2/3 of the available width
2: FlexColumnWidth(
2), // 2/3 of the available width
3: FlexColumnWidth(
3), // 2/3 of the available width
},
children: [
TableRow(
children: [
const TableCell(
child: Center(
child: Text(
'No',
style: boldTextStyle,
),
),
),
const TableCell(
child: Center(
child: Text(
'Jenis',
style: boldTextStyle,
))),
const TableCell(
child: Center(
child: Text(
'Donatur',
style: boldTextStyle,
))),
const TableCell(
child: Center(
child: Text(
'Jumlah /\nKeterangan',
style: boldTextStyle,
),
),
),
if (viewModel.role == 'admin')
const TableCell(
child: Center(
child: Text(
'Aksi',
style: boldTextStyle,
),
),
)
],
),
for (int j = 0;
j <
viewModel
.filteredByDateData[index]['data'][i]
['data_dana']
.length;
j++)
TableRow(
children: [
TableCell(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 4,
horizontal: 4,
),
child: Center(
child: Text(
'${j + 1}',
style: regularTextStyle,
),
),
),
),
TableCell(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 4,
horizontal: 4,
),
child: Center(
// circle icon
child: viewModel
.filteredByDateData[index]
['data'][i]['data_dana']
[j]
.bentuk ==
'Pemasukan'
? const JenisIconContainer(
color: Colors.green,
icon: Icons.arrow_upward,
)
: const JenisIconContainer(
color: Colors.red,
icon: Icons.arrow_downward,
)),
),
),
TableCell(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 4,
horizontal: 4,
),
child: Center(
child: Text(
'${viewModel.filteredByDateData[index]['data'][i]['data_dana'][j].nama == '' ? '-' : viewModel.filteredByDateData[index]['data'][i]['data_dana'][j].nama}',
style: regularTextStyle,
)),
)),
TableCell(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 4,
horizontal: 4,
),
child: Center(
child: Text(
viewModel
.filteredByDateData[index]
['data'][i]['data_dana']
[j]
.jenisDonasi ==
'Uang'
? 'Rp. ${viewModel.otherFunction.commaFormat(int.parse(viewModel.filteredByDateData[index]['data'][i]['data_dana'][j].jumlah))}'
: viewModel
.filteredByDateData[index]
['data'][i]['data_dana'][j]
.keterangan,
style: regularTextStyle,
),
),
),
),
if (viewModel.role == 'admin')
TableCell(
child: Center(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 4,
horizontal: 4,
),
child: Wrap(
runSpacing: 10,
spacing: 5,
children: [
GestureDetector(
onTap: () {
viewModel.goToEditDanaSosial(
int.parse(viewModel
.filteredByDateData[
index]['data'][i]
['data_dana'][j]
.idDanaSosial));
},
child: JenisIconContainer(
color: Colors.blue[600]!,
icon: Icons.edit,
),
),
GestureDetector(
onTap: () {
viewModel.deleteData(
int.parse(viewModel
.filteredByDateData[index]
['data'][i]
['data_dana'][j]
.idDanaSosial!),
);
},
child: const JenisIconContainer(
color: Colors.red,
icon: Icons.delete,
),
),
],
),
),
),
)
],
),
],
),
const SizedBox(height: 10),
// create a horizontal line
const Divider(
color: Colors.grey,
thickness: 1.0,
),
const SizedBox(height: 10),
],
),
],
),
),
);
},
);
}
}
class HasilIncomeOutcome extends ViewModelWidget<DanaSosialKhususViewModel> {
const HasilIncomeOutcome({
super.key,
});
@override
Widget build(BuildContext context, DanaSosialKhususViewModel viewModel) {
return SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
for (int i = 0; i < viewModel.monthIncomeOutcome.length; i++)
Column(
children: [
Text(
viewModel.otherFunction.changeMonthYear(
viewModel.monthIncomeOutcome[i]['month']),
style: boldTextStyle.copyWith(
decoration: TextDecoration.underline,
fontSize: 17,
),
),
const SizedBox(
height: 5,
),
Table(
border: TableBorder.all(
color: Colors.grey,
),
children: [
const TableRow(children: [
TableCell(
child: Center(
child: Text(
'Pemasukan',
style: boldTextStyle,
))),
TableCell(
child: Center(
child: Text(
'Pengeluaran',
style: boldTextStyle,
)))
]),
TableRow(children: [
TableCell(
child: Center(
child: Text(
'Rp. ${OtherFunction().commaFormat(viewModel.monthIncomeOutcome[i]['income'])}',
style: italicTextStyle,
))),
TableCell(
child: Center(
child: Text(
'Rp. ${OtherFunction().commaFormat(viewModel.monthIncomeOutcome[i]['outcome'])}',
style: italicTextStyle,
)))
]),
],
),
const SizedBox(
height: 5,
),
const Divider(
color: Colors.grey,
thickness: 1,
),
const SizedBox(
height: 5,
),
],
)
],
),
);
}
}

View File

@ -0,0 +1,327 @@
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../../../app/app.dialogs.dart';
import '../../../../app/app.locator.dart';
import '../../../../app/app.logger.dart';
import '../../../../app/app.router.dart';
import '../../../../app/core/custom_base_view_model.dart';
import '../../../../model/dana_sosial_model.dart';
import '../../../../services/http_services.dart';
import '../../../../services/my_easyloading.dart';
import '../../../../services/other_function.dart';
class DanaSosialKhususViewModel extends CustomBaseViewModel {
final log = getLogger('DanaSosialKhususViewModel');
final _httpService = locator<MyHttpServices>();
final easyLoading = locator<MyEasyLoading>();
int bulan = DateTime.now().month;
List<DanaSosialModel> danaSosialModelList = [];
List<Map<String, dynamic>> filteredByDateData = []; // newly added
List<Map<String, dynamic>> monthIncomeOutcome = []; // newly added
int totalIncome = 0; // newly added
int totalOutcome = 0; // newly added
String? role;
bool? isLogin;
int jumlahDonasi = 0;
int jumlahPengeluaran = 0;
Future<void> init() async {
await getData();
await getJumlahDonasi();
prefs.then((SharedPreferences prefs) {
role = prefs.getString('role');
isLogin = prefs.getBool('isLogin');
});
// log.i(bulan);
}
getJumlahDonasi() async {
setBusy(true);
easyLoading.showLoading();
// get the month
var bulan = DateTime.now().month;
log.i(bulan);
// change bulan to string and add 0 if it is less than 10
String bulanString = bulan.toString().length == 1 ? '0$bulan' : '$bulan';
log.i(bulanString);
try {
var response =
await _httpService.get('pemasukan_khusus?bulan=$bulanString');
log.i(response.data['jumlah']);
// var theJumlahDonasi = response.data['jumlah'];
jumlahDonasi = response.data['jumlah'];
jumlahPengeluaran = response.data['jumlah_pengeluaran'];
} catch (e) {
log.e(e);
} finally {
setBusy(false);
easyLoading.dismissLoading();
}
}
getData() async {
setBusy(true);
easyLoading.showLoading();
try {
var response = await _httpService.get('dana_sosial_khusus');
// log.i(response.data);
danaSosialModelList = [];
var datanya = response.data['data'];
// log.i(datanya.length);
if (datanya.length > 0) {
for (var item in datanya) {
danaSosialModelList.add(DanaSosialModel.fromJson(item));
}
}
setBusy(false);
notifyListeners();
// log.i(danaSosialModelList.length);
changeByDate(danaSosialModelList);
} catch (e) {
log.e(e);
setBusy(false);
} finally {
easyLoading.dismissLoading();
}
}
//newly added
changeByDate(List<DanaSosialModel> data) {
filteredByDateData = [];
for (var item in data) {
var monthKey =
item.tanggal!.substring(0, 7); // Extracting the year and month
var dateKey = item.tanggal; // The full date
var monthData = filteredByDateData.firstWhere(
(element) => element['month'] == monthKey,
orElse: () => {
'month': monthKey,
'data': [],
});
var dateData =
monthData['data'].firstWhere((element) => element['date'] == dateKey,
orElse: () => {
'date': dateKey,
'data_dana': [],
});
dateData['data_dana'].add(item);
if (!monthData['data'].contains(dateData)) {
monthData['data'].add(dateData);
}
if (!filteredByDateData.contains(monthData)) {
filteredByDateData.add(monthData);
}
}
filteredByDateData.sort((a, b) => a['month'].compareTo(b['month']));
// log.i(filteredByDateData);
// group the data by month
monthIncomeOutcome = [];
for (var item in filteredByDateData) {
var income = 0;
var outcome = 0;
for (var dateData in item['data']) {
for (var danaSosialModel in dateData['data_dana']) {
if (danaSosialModel.bentuk == 'Pemasukan' &&
danaSosialModel.jenisDonasi == 'Uang') {
income += int.parse(danaSosialModel.jumlah ?? '0');
} else if (danaSosialModel.bentuk == 'Pengeluaran' &&
danaSosialModel.jenisDonasi == 'Uang') {
outcome += int.parse(danaSosialModel.jumlah ?? '0');
}
}
}
monthIncomeOutcome.add({
'month': item['month'],
'income': income,
'outcome': outcome,
});
}
// log.i(monthIncomeOutcome);
totalIncome = 0;
totalOutcome = 0;
for (var item in monthIncomeOutcome) {
totalIncome += int.parse(item['income'].toString());
totalOutcome += int.parse(item['outcome'].toString());
}
log.i(totalIncome);
log.i(totalOutcome);
}
goToTambahDanaSosial() {
navigationService.navigateTo(
Routes.tambahDanaSosialView,
arguments: const TambahDanaSosialViewArguments(isKhusus: true),
);
}
getFilter(String sql) async {
setBusy(true);
easyLoading.showLoading();
FormData formData = FormData.fromMap({
'sql': sql,
});
try {
var response = await _httpService.postWithFormData(
'filter_dana_khusus',
formData,
);
// log.i(response.data);
danaSosialModelList = [];
var datanya = response.data['data'];
jumlahDonasi = response.data['jumlah_donasi'];
jumlahPengeluaran = response.data['jumlah_pengeluaran'];
// log.i(datanya.length);
if (datanya.length > 0) {
for (var item in datanya) {
danaSosialModelList.add(DanaSosialModel.fromJson(item));
}
}
setBusy(false);
notifyListeners();
log.i(danaSosialModelList);
} catch (e) {
log.e(e);
setBusy(false);
} finally {
easyLoading.dismissLoading();
}
}
filterDialog(BuildContext context) async {
// create a dialog
final res = await dialogService.showCustomDialog(
variant: DialogType.filterDialogView,
);
if (res!.confirmed) {
String jenisDonasi = res.data['jenisDonasi'] == 'Semua'
? ''
: "jenis = '${res.data['jenisDonasi']}' and ";
String bulan = res.data['bulan'] == 'Semua'
? ''
: "tanggal like '%-${OtherFunction().changeMonth(res.data['bulan'])}-%' and ";
String tahun = res.data['tahun'] == 'Semua'
? ''
: 'tanggal like "%${res.data['tahun']}-%" and ';
String status = res.data['status'] == 'Semua'
? ''
: (res.data['status'] == 'Belum Dikonfirmasi')
? 'status = 0'
: 'status = 1';
if (jenisDonasi == 'Semua' &&
bulan == 'Semua' &&
tahun == 'Semua' &&
status == 'Semua') {
getData();
return;
}
String sql =
'Select * from tb_dana_sosial_khusus where $jenisDonasi$bulan$tahun$status';
// check the last 3 character if it is 'or ' then remove it
// if (sql.substring(sql.length - 3) == 'or ') {
// sql = sql.substring(0, sql.length - 3);
// }
if (sql.substring(sql.length - 4) == 'and ') {
sql = sql.substring(0, sql.length - 4);
}
log.i(sql);
getFilter(sql);
}
}
goToEditDanaSosial(int id) async {
navigationService.navigateTo(
Routes.detailDanaSosialView,
arguments: DetailDanaSosialViewArguments(
id: id,
isKhusus: true,
),
);
}
deleteData(int parse) async {
await dialogService
.showDialog(
title: 'Hapus Data',
description: 'Apakah anda yakin ingin menghapus data ini?',
buttonTitle: 'Hapus',
cancelTitle: 'Batal',
buttonTitleColor: Colors.red,
cancelTitleColor: Colors.green,
)
.then(
(value) async {
if (value!.confirmed) {
easyLoading.showLoading();
setBusy(true);
try {
var response = await _httpService.postWithFormData(
'hapus_dana_sosial_khusus',
FormData.fromMap({
'id_dana_sosial': parse,
}));
log.i(response.data);
easyLoading.dismissLoading();
easyLoading.showSuccess('Data berhasil dihapus');
getData();
getJumlahDonasi();
} on DioError catch (e) {
// easyLoading.dismissLoading();
log.e(e);
easyLoading.showError('Terjadi kesalahan');
} finally {
setBusy(false);
easyLoading.dismissLoading();
}
} else {
log.i('cancel');
return;
}
},
);
}
Future<bool> addDonatur() async {
var res = await dialogService.showCustomDialog(
variant: DialogType.addDonaturDialogView,
);
if (res!.confirmed) {
snackbarService.showSnackbar(
message: 'Berhasil menambahkan donatur',
duration: const Duration(seconds: 2),
);
return true;
}
return false;
}
}

View File

@ -1,6 +1,9 @@
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import '../../../../app/themes/app_colors.dart';
import '../../../../app/themes/app_text.dart';
import '../../../../services/other_function.dart';
import './data_siswa_view_model.dart';
class DataSiswaView extends StatelessWidget {
@ -18,12 +21,135 @@ 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 Anak Panti',
style: regularTextStyle.copyWith(
color: Colors.white,
fontSize: 15,
),
),
Text(
'${model.jumlahSiswa} 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: (model.siswaModelList.isEmpty)
? Center(
child: Text(
'Tidak ada data',
style: boldTextStyle.copyWith(
fontSize: 20,
),
),
)
: // ListView.builder(
ListView.builder(
padding: const EdgeInsets.symmetric(
horizontal: 15, vertical: 10),
itemCount: model.siswaModelList.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
leading: Container(
alignment: Alignment.center,
width: 25,
height: 25,
decoration: BoxDecoration(
color: mainColor,
borderRadius: BorderRadius.circular(50),
),
child: Text(
'${index + 1}',
style: regularTextStyle.copyWith(
color: Colors.white),
),
),
title: Text(
model.otherFunction.capitalizeEachWord(
model.siswaModelList[index].nama!),
style: boldTextStyle.copyWith(
fontSize: 13, color: mainColor)),
subtitle: Text(
'Umur : ${OtherFunction().umur(model.siswaModelList[index].tanggalLahir ?? '')}'),
// circle avatar
trailing: GestureDetector(
onTap: () {
model.log.i(
'Edit${model.siswaModelList[index].idSiswa!}');
model.goToEditSiswa(int.parse(model
.siswaModelList[index].idSiswa!));
},
child: Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: mainColor,
borderRadius: BorderRadius.circular(50),
),
child: const Icon(
Icons.person,
color: Colors.white,
),
),
)),
);
},
),
),
),
],
),
floatingActionButton: model.role == 'admin'
? FloatingActionButton(
onPressed: () {
model.addSiswa();
},
child: const Icon(Icons.add),
)
: null,
);
},
);

View File

@ -1,5 +1,88 @@
import 'package:panti_asuhan/app/core/custom_base_view_model.dart';
import 'package:panti_asuhan/ui/views/admin_index_tracking/edit_siswa/edit_siswa_view.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../../../app/app.dialogs.dart';
import '../../../../app/app.locator.dart';
import '../../../../app/app.logger.dart';
import '../../../../app/core/custom_base_view_model.dart';
import '../../../../model/siswa_model.dart';
import '../../../../services/http_services.dart';
import '../../../../services/my_easyloading.dart';
import '../add_siswa_dialog/add_siswa_dialog/add_siswa_dialog_view.dart';
class DataSiswaViewModel extends CustomBaseViewModel {
Future<void> init() async {}
final log = getLogger('DataSiswaViewModel');
final _httpService = locator<MyHttpServices>();
final easyLoading = locator<MyEasyLoading>();
List<SiswaModel> siswaModelList = [];
int jumlahSiswa = 0;
String? role;
Future<void> init() async {
await getData();
await getJumlahSiswa();
prefs.then((SharedPreferences prefs) {
role = prefs.getString('role');
});
}
getJumlahSiswa() async {
setBusy(true);
easyLoading.showLoading();
try {
var response = await _httpService.get('jumlah_siswa');
log.i(response.data['data']);
jumlahSiswa = int.parse(response.data['data']['jumlah']);
} catch (e) {
log.e(e);
} finally {
setBusy(false);
easyLoading.dismissLoading();
}
}
getData() async {
setBusy(true);
easyLoading.showLoading();
try {
var response = await _httpService.get('siswa');
log.i(response.data);
siswaModelList = [];
var datanya = response.data['data'];
// log.i(datanya.length);
if (datanya.length > 0) {
for (var item in datanya) {
siswaModelList.add(SiswaModel.fromJson(item));
}
}
setBusy(false);
notifyListeners();
log.i(siswaModelList);
} catch (e) {
log.e(e);
} finally {
setBusy(false);
easyLoading.dismissLoading();
}
}
void addSiswa() async {
final res = await dialogService.showCustomDialog(
variant: DialogType.addSiswaDialogView,
data: DataSiswa(nama: null),
);
if (res?.confirmed != true) return;
siswaModelList = [];
await getData();
}
goToEditSiswa(int idSiswa) async {
navigationService.navigateToView(EditSiswaView(idSiswa: idSiswa));
}
}

View File

@ -0,0 +1,315 @@
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_textformfield.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
import 'package:validatorless/validatorless.dart';
import './edit_dialog_siswa_view_model.dart';
class EditDialogSiswaView extends StatelessWidget {
final DialogRequest request;
final Function(DialogResponse) completer;
const EditDialogSiswaView({
Key? key,
required this.request,
required this.completer,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return ViewModelBuilder<EditDialogSiswaViewModel>.reactive(
viewModelBuilder: () => EditDialogSiswaViewModel(),
onViewModelReady: (EditDialogSiswaViewModel model) async {
await model.init(request.data);
},
builder: (
BuildContext context,
EditDialogSiswaViewModel model,
Widget? child,
) {
return Dialog(
child: Container(
padding: const EdgeInsets.all(20),
decoration: const BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
),
),
child: SingleChildScrollView(
child: Form(
key: model.formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
const Center(
child: Text(
'Edit Data Anak Panti',
style: boldTextStyle,
),
),
const SizedBox(height: 10),
Text(
' Nama',
style: regularTextStyle.copyWith(
fontSize: 12,
color: mainColor,
),
),
MyTextFormField(
hintText: 'Masukkan Nama',
controller: model.namaController,
validator:
Validatorless.required('Nama tidak boleh kosong'),
),
const SizedBox(height: 10),
Text(
' Tanggal Lahir',
style: regularTextStyle.copyWith(
fontSize: 12,
color: mainColor,
),
),
MyTextFormField(
controller: model.tanggalLahirController,
readOnly: true,
validator: Validatorless.required(
'Tanggal lahir tidak boleh kosong'),
onTap: () {
model.changeDate(context);
},
),
const SizedBox(height: 10),
Text(
' Tempat Lahir',
style: regularTextStyle.copyWith(
fontSize: 12,
color: mainColor,
),
),
MyTextFormField(
hintText: 'Masukkan Tempat Lahir',
controller: model.tempatLahirController,
validator: Validatorless.required(
'Tempat lahir tidak boleh kosong'),
),
const SizedBox(height: 10),
Text(
' Jenis Kelamin',
style: regularTextStyle.copyWith(
fontSize: 12,
color: mainColor,
),
),
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<String>(
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<String>(
value: value,
child: Text(
value,
style: regularTextStyle.copyWith(
fontSize: 16,
),
),
);
}).toList(),
),
),
),
const SizedBox(height: 10),
Text(
' No. Telepon',
style: regularTextStyle.copyWith(
fontSize: 12,
color: mainColor,
),
),
MyTextFormField(
hintText: 'Masukkan No. Telepon',
controller: model.noTelponController,
validator: Validatorless.multiple(
[
Validatorless.required(
'No. telepon tidak boleh kosong'),
Validatorless.number(
'No. telepon harus berupa angka'),
],
),
),
const SizedBox(height: 10),
Text(
' Agama',
style: regularTextStyle.copyWith(
fontSize: 12,
color: mainColor,
),
),
MyTextFormField(
controller: model.agamaController,
validator:
Validatorless.required('Agama tidak boleh kosong'),
),
const SizedBox(height: 10),
Text(
' Kewarganegaraan',
style: regularTextStyle.copyWith(
fontSize: 12,
color: mainColor,
),
),
MyTextFormField(
controller: model.kewarganegaraanController,
validator: Validatorless.required(
'Kewarganegaraan tidak boleh kosong'),
),
const SizedBox(height: 10),
Text(
' Alamat',
style: regularTextStyle.copyWith(
fontSize: 12,
color: mainColor,
),
),
MyTextFormField(
controller: model.alamatController,
maxLines: 2,
validator:
Validatorless.required('Alamat tidak boleh kosong'),
),
const SizedBox(height: 10),
Text(
' Pendidikan SD',
style: regularTextStyle.copyWith(
fontSize: 12,
color: mainColor,
),
),
MyTextFormField(
controller: model.pendidikanSDController,
validator: Validatorless.required(
'Pendidikan SD tidak boleh kosong'),
),
const SizedBox(height: 10),
Text(
' Pendidikan SMP',
style: regularTextStyle.copyWith(
fontSize: 12,
color: mainColor,
),
),
MyTextFormField(
controller: model.pendidikanSMPController,
validator: Validatorless.required(
'Pendidikan SMP tidak boleh kosong'),
),
const SizedBox(height: 10),
Text(
' Pendidikan SMA',
style: regularTextStyle.copyWith(
fontSize: 12,
color: mainColor,
),
),
MyTextFormField(
controller: model.pendidikanSMAController,
validator: Validatorless.required(
'Pendidikan SMA tidak boleh kosong'),
),
const SizedBox(height: 10),
Text(
' Kemampuan',
style: regularTextStyle.copyWith(
fontSize: 12,
color: mainColor,
),
),
MyTextFormField(
controller: model.kemampuanController,
maxLines: 4,
validator: Validatorless.required(
'Kemampuan tidak boleh kosong'),
),
const SizedBox(height: 10),
Text(
' Hobi',
style: regularTextStyle.copyWith(
fontSize: 12,
color: mainColor,
),
),
MyTextFormField(
controller: model.hobiController,
maxLines: 4,
validator:
Validatorless.required('Hobi 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.updateSiswa();
if (res) {
// ignore: use_build_context_synchronously
Navigator.pop(context);
completer(DialogResponse(confirmed: true));
}
// completer(DialogResponse(confirmed: true));
}
},
child: const Text(
'Update',
style: TextStyle(
color: blueColor,
),
),
),
],
),
],
),
),
),
),
);
},
);
}
}

View File

@ -0,0 +1,108 @@
import 'package:dio/dio.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_holo_date_picker/flutter_holo_date_picker.dart';
import '../../../../../app/app.locator.dart';
import '../../../../../app/app.logger.dart';
import '../../../../../app/core/custom_base_view_model.dart';
import '../../../../../model/siswa_model.dart';
import '../../../../../services/http_services.dart';
import '../../../../../services/my_easyloading.dart';
class EditDialogSiswaViewModel extends CustomBaseViewModel {
final log = getLogger('EditDialogSiswaViewModel');
final easyLoading = locator<MyEasyLoading>();
final _httpService = locator<MyHttpServices>();
SiswaModel? siswaModel;
String jenisKelamin = 'Laki-laki';
List<String> jenisKelaminList = ['Laki-laki', 'Perempuan'];
TextEditingController namaController = TextEditingController();
TextEditingController tanggalLahirController = TextEditingController();
TextEditingController tempatLahirController = TextEditingController();
TextEditingController alamatController = TextEditingController();
TextEditingController noTelponController = TextEditingController();
TextEditingController agamaController = TextEditingController();
TextEditingController kewarganegaraanController = TextEditingController();
TextEditingController pendidikanSDController = TextEditingController();
TextEditingController pendidikanSMPController = TextEditingController();
TextEditingController pendidikanSMAController = TextEditingController();
TextEditingController kemampuanController = TextEditingController();
TextEditingController hobiController = TextEditingController();
final formKey = GlobalKey<FormState>();
Future<void> init(data) async {
log.i('data: $data');
setBusy(true);
siswaModel = data;
notifyListeners();
setBusy(false);
namaController.text = siswaModel!.nama!;
tanggalLahirController.text = siswaModel!.tanggalLahir!;
tempatLahirController.text = siswaModel!.tempatLahir!;
alamatController.text = siswaModel!.alamat!;
noTelponController.text = siswaModel!.noTelpon!;
agamaController.text = siswaModel!.agama!;
kewarganegaraanController.text = siswaModel!.kewarganegaraan!;
pendidikanSDController.text = siswaModel!.pendidikanSd!;
pendidikanSMPController.text = siswaModel!.pendidikanSmp!;
pendidikanSMAController.text = siswaModel!.pendidikanSma!;
kemampuanController.text = siswaModel!.kemampuan!;
hobiController.text = siswaModel!.hobi!;
jenisKelamin = siswaModel!.jenisKelamin!;
notifyListeners();
}
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;
}
}
Future<bool> updateSiswa() async {
setBusy(true);
easyLoading.customLoading('Updating data...');
try {
var formData = FormData.fromMap({
'id': siswaModel!.idSiswa,
'nama': namaController.text,
'tanggal_lahir': tanggalLahirController.text,
'tempat_lahir': tempatLahirController.text,
'jenis_kelamin': jenisKelamin,
'alamat': alamatController.text,
'no_telpon': noTelponController.text,
'agama': agamaController.text,
'kewarganegaraan': kewarganegaraanController.text,
'pendidikan_sd': pendidikanSDController.text,
'pendidikan_smp': pendidikanSMPController.text,
'pendidikan_sma': pendidikanSMAController.text,
'kemampuan': kemampuanController.text,
'hobi': hobiController.text,
});
await _httpService.postWithFormData('siswa_edit', formData);
snackbarService.showSnackbar(message: 'Data berhasil diupdate');
return true;
} catch (e) {
log.e(e);
return false;
} finally {
easyLoading.dismissLoading();
setBusy(false);
}
}
}

View File

@ -0,0 +1,311 @@
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:stacked/stacked.dart';
import 'package:validatorless/validatorless.dart';
import '../../../../app/themes/app_colors.dart';
import '../../../widgets/my_textformfield.dart';
import './edit_siswa_view_model.dart';
class EditSiswaView extends StatelessWidget {
final int idSiswa;
const EditSiswaView({required this.idSiswa, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return ViewModelBuilder<EditSiswaViewModel>.reactive(
viewModelBuilder: () => EditSiswaViewModel(),
onViewModelReady: (EditSiswaViewModel model) async {
await model.init(
idSiswa,
);
},
builder: (
BuildContext context,
EditSiswaViewModel model,
Widget? child,
) {
return Scaffold(
appBar: AppBar(
title: const Text(
"Informasi Data Anak Panti",
style: TextStyle(
color: Colors.white,
fontSize: 20,
),
),
backgroundColor: mainColor,
elevation: 0,
// back button color to white
iconTheme: const IconThemeData(color: Colors.white),
),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: SingleChildScrollView(
child: Stack(
children: [
Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Center(
child: CircleAvatar(
radius: 50,
backgroundColor: fontParagraphColor,
backgroundImage: model.siswaModel != null
? NetworkImage(
'${dotenv.env['url']}${model.siswaModel!.imgUrl}',
)
: null,
child: model.siswaModel == null
? const Icon(
Icons.person,
size: 50,
color: Colors.white,
)
: null,
),
),
const SizedBox(height: 10),
MyTextFormField(
labelText: 'Nama',
controller: model.namaController,
validator:
Validatorless.required('Nama tidak boleh kosong'),
enabled: false,
),
const SizedBox(height: 10),
MyTextFormField(
labelText: 'Tanggal Lahir',
controller: model.tanggalLahirController,
readOnly: true,
validator: Validatorless.required(
'Tanggal lahir tidak boleh kosong'),
enabled: false,
onTap: () {
// model.changeDate(context);
},
),
const SizedBox(height: 10),
MyTextFormField(
labelText: 'Tempat Lahir',
controller: model.tempatLahirController,
enabled: false,
validator: Validatorless.required(
'Tempat lahir tidak boleh kosong'),
),
const SizedBox(height: 10),
// create dropdown button
MyTextFormField(
labelText: 'Jenis Kelamin',
controller: model.jkController,
enabled: false,
validator: Validatorless.required(
'Pendidikan SMP tidak boleh kosong'),
),
const SizedBox(height: 10),
MyTextFormField(
labelText: 'No. Telepon',
controller: model.noTelponController,
enabled: false,
keyboardType: TextInputType.number,
validator: Validatorless.multiple(
[
Validatorless.required(
'No. telepon tidak boleh kosong'),
Validatorless.number(
'No. telepon harus berupa angka'),
],
),
),
const SizedBox(height: 10),
MyTextFormField(
labelText: 'Agama',
controller: model.agamaController,
enabled: false,
keyboardType: TextInputType.emailAddress,
validator:
Validatorless.required('Agama tidak boleh kosong'),
),
const SizedBox(height: 10),
MyTextFormField(
labelText: 'Kewarganegaraan',
controller: model.kewarganegaraanController,
enabled: false,
keyboardType: TextInputType.emailAddress,
validator: Validatorless.required(
'Kewarganegaraan tidak boleh kosong'),
),
const SizedBox(height: 10),
MyTextFormField(
labelText: 'Alamat',
controller: model.alamatController,
enabled: false,
maxLines: 2,
validator:
Validatorless.required('Alamat tidak boleh kosong'),
),
const SizedBox(height: 10),
MyTextFormField(
labelText: 'Pendidikan SD',
controller: model.pendidikanSDController,
enabled: false,
validator: Validatorless.required(
'Pendidikan SD tidak boleh kosong'),
),
const SizedBox(height: 10),
MyTextFormField(
labelText: 'Pendidikan SMP',
controller: model.pendidikanSMPController,
enabled: false,
validator: Validatorless.required(
'Pendidikan SMP tidak boleh kosong'),
),
const SizedBox(height: 10),
MyTextFormField(
labelText: 'Pendidikan SMA',
controller: model.pendidikanSMAController,
enabled: false,
validator: Validatorless.required(
'Pendidikan SMA tidak boleh kosong'),
),
const SizedBox(height: 10),
MyTextFormField(
labelText: 'Kemampuan',
controller: model.kemampuanController,
enabled: false,
maxLines: 4,
validator: Validatorless.required(
'Kemampuan tidak boleh kosong'),
),
const SizedBox(height: 10),
MyTextFormField(
labelText: "Hobi",
controller: model.hobiController,
enabled: false,
maxLines: 4,
validator:
Validatorless.required('Hobi tidak boleh kosong'),
),
// Row(
// mainAxisAlignment: MainAxisAlignment.end,
// children: [
// TextButton(
// onPressed: () {},
// 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,
// ),
// ),
// ),
// ],
// ),
],
),
model.role == 'admin'
? Positioned(
top: 0,
right: 45,
// create a edit rounded button
child: Container(
alignment: Alignment.center,
height: 30,
width: 30,
decoration: BoxDecoration(
color: blueColor,
borderRadius: BorderRadius.circular(20),
),
child: IconButton(
onPressed: () {
// model.changeEdit();
model.editData();
},
icon: const Icon(
Icons.edit,
color: Colors.white,
size: 13,
),
),
),
)
: const SizedBox(),
model.role == 'admin'
? Positioned(
top: 0,
right: 0,
// create a close rounded button
child: Container(
alignment: Alignment.center,
height: 30,
width: 30,
decoration: BoxDecoration(
color: dangerColor,
borderRadius: BorderRadius.circular(20),
),
child: IconButton(
onPressed: () {
// model.changeEdit();
model.dialogService
.showDialog(
title: 'Hapus Data',
description:
'Apakah anda yakin ingin menghapus data ini?',
buttonTitle: 'Hapus',
cancelTitle: 'Batal',
buttonTitleColor: dangerColor,
cancelTitleColor: mainColor,
)
.then((value) {
if (value!.confirmed) {
model.deleteData();
// close dialog
Navigator.pop(context);
// model.navigationService.clearTillFirstAndShow(
// Routes.splashScreenView);
}
});
// model.deleteData();
},
icon: const Icon(
Icons.delete_forever,
color: Colors.white,
size: 15,
),
),
),
)
: const SizedBox(),
],
),
),
),
);
},
);
}
}

View File

@ -0,0 +1,118 @@
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../../../app/app.dialogs.dart';
import '../../../../app/app.locator.dart';
import '../../../../app/app.logger.dart';
import '../../../../app/core/custom_base_view_model.dart';
import '../../../../model/siswa_model.dart';
import '../../../../services/http_services.dart';
import '../../../../services/my_easyloading.dart';
class EditSiswaViewModel extends CustomBaseViewModel {
final log = getLogger('EditSiswaViewModel');
final _httpService = locator<MyHttpServices>();
final easyLoading = locator<MyEasyLoading>();
String? role;
SiswaModel? siswaModel;
List<String> jenisKelaminList = ['Laki-laki', 'Perempuan'];
String jenisKelamin = 'Laki-laki';
TextEditingController namaController = TextEditingController();
TextEditingController tanggalLahirController = TextEditingController();
TextEditingController tempatLahirController = TextEditingController();
TextEditingController alamatController = TextEditingController();
TextEditingController noTelponController = TextEditingController();
TextEditingController agamaController = TextEditingController();
TextEditingController kewarganegaraanController = TextEditingController();
TextEditingController pendidikanSDController = TextEditingController();
TextEditingController pendidikanSMPController = TextEditingController();
TextEditingController pendidikanSMAController = TextEditingController();
TextEditingController kemampuanController = TextEditingController();
TextEditingController hobiController = TextEditingController();
TextEditingController jkController = TextEditingController();
Future<void> init(int idSiswa) async {
log.i('idSiswa: $idSiswa');
getData(idSiswa);
prefs.then((SharedPreferences prefs) {
role = prefs.getString('role');
});
}
getData(int idSiswa) async {
// log.i('idSiswa: $idSiswa');
setBusy(true);
easyLoading.showLoading();
try {
var response = await _httpService.get('siswa_detail?id=$idSiswa');
var datanya = response.data['data'];
siswaModel = SiswaModel.fromJson(datanya);
notifyListeners();
log.i(siswaModel!.imgUrl);
namaController.text = siswaModel!.nama!;
tanggalLahirController.text = siswaModel!.tanggalLahir!;
tempatLahirController.text = siswaModel!.tempatLahir!;
alamatController.text = siswaModel!.alamat!;
noTelponController.text = siswaModel!.noTelpon!;
agamaController.text = siswaModel!.agama!;
kewarganegaraanController.text = siswaModel!.kewarganegaraan!;
pendidikanSDController.text = siswaModel!.pendidikanSd!;
pendidikanSMPController.text = siswaModel!.pendidikanSmp!;
pendidikanSMAController.text = siswaModel!.pendidikanSma!;
kemampuanController.text = siswaModel!.kemampuan!;
hobiController.text = siswaModel!.hobi!;
jkController.text = siswaModel!.jenisKelamin!;
} catch (e) {
log.e(e);
} finally {
setBusy(false);
easyLoading.dismissLoading();
}
}
void deleteData() async {
setBusy(true);
easyLoading.showLoading();
try {
var response = await _httpService.postWithFormData(
'siswa_delete',
FormData.fromMap(
{
'id': siswaModel!.idSiswa,
},
),
);
log.i(response.data);
snackbarService.showSnackbar(
message: 'Data berhasil dihapus',
title: 'Berhasil',
duration: const Duration(seconds: 2),
);
// navigationService.back();
} catch (e) {
snackbarService.showSnackbar(
message: 'Data gagal dihapus',
title: 'Gagal',
duration: const Duration(seconds: 2),
);
log.e(e);
} finally {
setBusy(false);
easyLoading.dismissLoading();
}
}
editData() async {
var res = await dialogService.showCustomDialog(
variant: DialogType.editDialogSiswaView,
data: siswaModel,
);
if (res?.confirmed != true) {
init(int.parse(siswaModel!.idSiswa!));
}
}
}

View File

@ -0,0 +1,284 @@
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
import '../../../../app/themes/app_colors.dart';
import '../../../../app/themes/app_text.dart';
import './filter_dialog_view_model.dart';
class FilterDialogView extends StatelessWidget {
final DialogRequest? request;
final Function(DialogResponse)? completer;
const FilterDialogView({
Key? key,
this.request,
this.completer,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return ViewModelBuilder<FilterDialogViewModel>.reactive(
viewModelBuilder: () => FilterDialogViewModel(),
onViewModelReady: (FilterDialogViewModel model) async {
await model.init();
},
builder: (
BuildContext context,
FilterDialogViewModel 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: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
"FILTER LAPORAN",
style: boldTextStyle.copyWith(
fontSize: 16,
),
),
const SizedBox(height: 10),
Align(
alignment: Alignment.centerLeft,
child: Text(
"Jenis Dana Sosial",
style: regularTextStyle.copyWith(color: mainColor),
textAlign: TextAlign.left,
),
),
Container(
width: double.infinity,
height: 60,
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25),
border: Border.all(
color: mainColor,
),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: model.jenisDonasi,
onChanged: (String? newValue) {
// model.setSelectedjenisDonasi(newValue!);
model.log.i(newValue);
model.jenisDonasi = newValue!;
model.notifyListeners();
},
items: model.jenisDonasiList.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: regularTextStyle.copyWith(
fontSize: 16,
),
),
);
}).toList(),
),
),
),
const SizedBox(height: 10),
Align(
alignment: Alignment.centerLeft,
child: Text(
"Filter Bulan",
style: regularTextStyle.copyWith(color: mainColor),
textAlign: TextAlign.left,
),
),
Container(
width: double.infinity,
height: 60,
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25),
border: Border.all(
color: mainColor,
),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: model.bulan,
onChanged: (String? newValue) {
// model.setSelectedbulan(newValue!);
model.log.i(newValue);
model.bulan = newValue!;
model.notifyListeners();
},
items: model.bulanList.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: regularTextStyle.copyWith(
fontSize: 16,
),
),
);
}).toList(),
),
),
),
const SizedBox(height: 10),
Align(
alignment: Alignment.centerLeft,
child: Text(
"Filter Tahun",
style: regularTextStyle.copyWith(color: mainColor),
textAlign: TextAlign.left,
),
),
Container(
width: double.infinity,
height: 60,
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25),
border: Border.all(
color: mainColor,
),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: model.tahun,
onChanged: (String? newValue) {
// model.setSelectedtahun(newValue!);
model.log.i(newValue);
model.tahun = newValue!;
model.notifyListeners();
},
items: model.tahunList.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: regularTextStyle.copyWith(
fontSize: 16,
),
),
);
}).toList(),
),
),
),
const SizedBox(height: 10),
Align(
alignment: Alignment.centerLeft,
child: Text(
"Filter Status",
style: regularTextStyle.copyWith(color: mainColor),
textAlign: TextAlign.left,
),
),
Container(
width: double.infinity,
height: 60,
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25),
border: Border.all(
color: mainColor,
),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: model.status,
onChanged: (String? newValue) {
// model.setSelectedstatus(newValue!);
model.log.i(newValue);
model.status = newValue!;
model.notifyListeners();
},
items: model.statusList.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: regularTextStyle.copyWith(
fontSize: 16,
),
),
);
}).toList(),
),
),
),
const SizedBox(height: 15),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Expanded(
child: TextButton(
onPressed: () {
// model.filter();
// model.filter();
completer!(
DialogResponse(
confirmed: true,
data: {
'jenisDonasi': model.jenisDonasi,
'bulan': model.bulan,
'tahun': model.tahun,
'status': model.status,
},
),
);
},
style: TextButton.styleFrom(
backgroundColor: mainColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25),
),
),
child: Text(
"Filter",
style: boldTextStyle.copyWith(
fontSize: 16,
color: Colors.white,
),
),
),
),
const SizedBox(width: 10),
Expanded(
child: TextButton(
onPressed: () {
completer!(DialogResponse(confirmed: false));
},
style: TextButton.styleFrom(
backgroundColor: mainGrey,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25),
),
),
child: Text(
"Close",
style: boldTextStyle.copyWith(
fontSize: 16,
color: Colors.white,
),
),
),
),
],
)
],
),
),
);
},
);
}
}

View File

@ -0,0 +1,43 @@
import '../../../../app/app.logger.dart';
import '../../../../app/core/custom_base_view_model.dart';
class FilterDialogViewModel extends CustomBaseViewModel {
final log = getLogger('FilterDialogViewModel');
String jenisDonasi = 'Semua';
List<String> jenisDonasiList = ['Semua', 'Uang', 'Barang'];
String bulan = 'Semua';
List<String> bulanList = [
'Semua',
'Januari',
'Februari',
'Maret',
'April',
'Mei',
'Juni',
'Juli',
'Agustus',
'September',
'Oktober',
'November',
'Desember'
];
String tahun = 'Semua';
List<String> tahunList = [
'Semua',
'2024',
'2023',
'2022',
];
String status = 'Semua';
List<String> statusList = [
'Semua',
'Belum Dikonfirmasi',
'Sudah Dikonfirmasi',
];
Future<void> init() async {}
}

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import '../../../../app/themes/app_text.dart';
import './profil_view_model.dart';
class ProfilView extends StatelessWidget {
@ -18,10 +19,57 @@ class ProfilView extends StatelessWidget {
ProfilViewModel model,
Widget? child,
) {
return const Scaffold(
body: Center(
child: Text(
'ProfilView',
return Scaffold(
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
child: Column(
children: [
const Center(
child: Image(
image: AssetImage("assets/logo.png"),
width: 200,
height: 200,
),
),
const SizedBox(height: 10),
Text(
"PANTI ASUHAN ABADI AISYIYAH",
style: boldTextStyle.copyWith(
fontSize: 20,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 10),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
Icon(Icons.phone),
SizedBox(width: 15),
Expanded(
child: Text(
"085 298 962 023",
textAlign: TextAlign.justify,
style: regularTextStyle,
),
),
],
),
const SizedBox(height: 10),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
Icon(Icons.location_on),
SizedBox(width: 15),
Expanded(
child: Text(
"Jln Panti Asuhan No. 3 Ujung Lare, Kec. Soreang, Kota Parepare, Sulawesi Selatan 91133",
textAlign: TextAlign.justify,
style: regularTextStyle,
),
),
],
),
],
),
),
);

View File

@ -0,0 +1,48 @@
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import './sejarah_view_model.dart';
class SejarahView extends StatelessWidget {
const SejarahView({super.key});
@override
Widget build(BuildContext context) {
return ViewModelBuilder<SejarahViewModel>.nonReactive(
viewModelBuilder: () => SejarahViewModel(),
onViewModelReady: (SejarahViewModel model) async {
await model.init();
},
builder: (
BuildContext context,
SejarahViewModel model,
Widget? child,
) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(20),
child: SingleChildScrollView(
child: Column(
children: [
const Center(
child: Image(
image: AssetImage("assets/logo.png"),
width: 150,
height: 150,
),
),
const SizedBox(height: 20),
Text(
'${model.isi1}\n\n${model.isi2}',
style: const TextStyle(fontSize: 18),
textAlign: TextAlign.justify,
),
],
),
),
),
);
},
);
}
}

View File

@ -0,0 +1,11 @@
import 'package:panti_asuhan/app/core/custom_base_view_model.dart';
class SejarahViewModel extends CustomBaseViewModel {
String isi1 =
"Panti Asuhan Abadi Aisiyah Kota Parepare dibangun secara bertahap yang peletakan batu pertamanya pada tahun 1960, dan mulai ditempati pada tahun 1962, sehingga tahun berdirinya Panti Asuhan Abadi Aisyiyah Kota Parepare pada Tahun 1963";
String isi2 =
"Pada perkembangannya dibangun bangunan yang ada pada sebelah kiri bangunan p[ertama, kemudia dibangun lagi Mushollah, dan pembangunannya sepenuhnya bantuan prisden Soeharto pada waktu itu. Pada perkembangan berikutnya karena jalan masukke Panti Asuhan Masih berupa pematang sawah, maka oleh Hj. Syamsiah Jabbar sebagai pengelolah pertama meminta Pemerintah untuk meninjau jalan tersebut dan pada tahun 1964-1965 oleh Pemerintah dibangun jalanan beraspal";
Future<void> init() async {}
}

View File

@ -0,0 +1,253 @@
import 'package:flutter/material.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:stacked_services/stacked_services.dart';
import './edit_strukrur_organisasi_dialog_view_model.dart';
class EditStrukrurOrganisasiDialogView extends StatelessWidget {
final DialogRequest? request;
final Function(DialogResponse)? completer;
const EditStrukrurOrganisasiDialogView({
Key? key,
this.request,
this.completer,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return ViewModelBuilder<EditStrukrurOrganisasiDialogViewModel>.reactive(
viewModelBuilder: () => EditStrukrurOrganisasiDialogViewModel(),
onViewModelReady: (EditStrukrurOrganisasiDialogViewModel model) async {
await model.init(
request!.data,
);
},
builder: (
BuildContext context,
EditStrukrurOrganisasiDialogViewModel model,
Widget? child,
) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
child: Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
),
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Stack(
children: [
SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: model.child,
),
),
// top right rounded add button
request?.data['tambahan'] == false
? const SizedBox()
: Positioned(
top: 0,
right: 0,
child: GestureDetector(
onTap: () {
model.addWidget();
// model.check();
},
child: Container(
padding: const EdgeInsets.all(5),
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.circular(20),
),
child: const Icon(
Icons.add,
color: Colors.white,
),
),
),
),
],
),
const SizedBox(
height: 10,
),
SizedBox(
width: 150,
child: MyButton(
text: 'Simpan',
onPressed: () {
// model.check();
model.uploadJabatan(completer!);
},
),
),
],
),
),
),
);
},
);
}
}
class TheWidget extends ViewModelWidget<EditStrukrurOrganisasiDialogViewModel> {
final TextEditingController controller;
final int index;
@override
// ignore: overridden_fields
final Key key;
const TheWidget({
required this.key,
required this.controller,
required this.index,
}) : super(key: key);
@override
Widget build(
BuildContext context, EditStrukrurOrganisasiDialogViewModel viewModel) {
return Stack(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
// ignore: prefer_const_literals_to_create_immutables
children: [
Center(
child: FotoWidget(
index: index,
)),
const SizedBox(height: 10),
const Text(
' Nama',
style: regularTextStyle,
),
MyTextFormField(
controller: controller,
),
const SizedBox(height: 10),
// straight line divider
const Divider(
color: Colors.grey,
height: 20,
thickness: 1,
indent: 0,
endIndent: 0,
),
const SizedBox(height: 10),
],
),
// top right rounded add button
index == 1
? const SizedBox()
: Positioned(
top: 0,
right: 0,
child: GestureDetector(
onTap: () {
viewModel.removeWidget(index, key);
},
child: Container(
padding: const EdgeInsets.all(5),
decoration: BoxDecoration(
color: Colors.red,
borderRadius: BorderRadius.circular(20),
),
child: const Icon(
Icons.remove,
color: Colors.white,
),
),
),
),
],
);
}
}
class FotoWidget
extends ViewModelWidget<EditStrukrurOrganisasiDialogViewModel> {
final int index;
const FotoWidget({
Key? key,
required this.index,
}) : super(key: key);
@override
Widget build(
BuildContext context, EditStrukrurOrganisasiDialogViewModel viewModel) {
return Container(
width: 100,
height: 100,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(150),
// color: Colors.grey,
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 2,
blurRadius: 5,
),
],
),
child: Stack(
children: [
Center(
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: viewModel.imageBytes['image$index'] == null
? const Icon(
Icons.person,
size: 90,
)
: ClipRRect(
borderRadius: BorderRadius.circular(50),
child: Image.memory(
viewModel.imageBytes['image$index']!,
width: 100,
height: 100,
fit: BoxFit.cover,
),
),
),
),
Positioned(
bottom: 0,
right: 0,
child: GestureDetector(
onTap: () {
viewModel.addImage(index);
},
child: Container(
width: 30,
height: 30,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(150),
color: Colors.grey,
),
child: const Icon(
Icons.edit,
size: 20,
),
),
),
),
],
),
);
}
}

View File

@ -0,0 +1,178 @@
import 'dart:typed_data';
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 'package:stacked_services/stacked_services.dart';
import '../../../../../app/app.locator.dart';
import '../../../../../app/app.logger.dart';
import '../../../../../app/core/custom_base_view_model.dart';
import '../../../../../services/http_services.dart';
import '../../../../../services/my_easyloading.dart';
import 'edit_strukrur_organisasi_dialog_view.dart';
class EditStrukrurOrganisasiDialogViewModel extends CustomBaseViewModel {
final log = getLogger('EditStrukrurOrganisasiDialogViewModel');
final easyloading = locator<MyEasyLoading>();
final _httpService = locator<MyHttpServices>();
List<Widget> child = [];
Map<String, TextEditingController> controllers = {};
int controllerIndex = 1;
Map<String, Key> keys = {};
// image picker
Map<String, String> imagePaths = {};
ImagePicker picker = ImagePicker();
Map<String, XFile> imageFiles = {};
Map<String, Uint8List> imageBytes = {};
late String name;
Future<void> init(data) async {
name = data['jabatan'];
log.i(data);
// create controller
createController('$name$controllerIndex');
keys['$name$controllerIndex'] = UniqueKey();
// add theWidget to child
child.add(TheWidget(
controller: controllers['$name$controllerIndex']!,
index: controllerIndex,
key: keys['$name$controllerIndex']!,
));
}
void createController(String key) {
controllers[key] = TextEditingController();
}
void addWidget() {
controllerIndex++;
createController('$name$controllerIndex');
keys['$name$controllerIndex'] = UniqueKey();
child.add(TheWidget(
controller: controllers['$name$controllerIndex']!,
index: controllerIndex,
key: keys['$name$controllerIndex']!,
));
notifyListeners();
}
void disposeSingleController(String key) {
controllers[key]!.dispose();
controllers.remove(key);
}
check() {
log.i(controllers);
}
/////////// utk gambar //////////////
void addImage(int index) async {
try {
final XFile? image = await picker.pickImage(source: ImageSource.gallery);
if (image != null) {
imageFiles['image$index'] = image;
imagePaths['image$index'] = image.path;
imageBytes['image$index'] = await image.readAsBytes();
log.i('image path: ${imagePaths['image$index']}');
notifyListeners();
}
} catch (e) {
log.e(e);
}
}
uploadJabatan(Function(DialogResponse p1) completer) async {
// check if the controller is empty
for (var i = 1; i <= controllers.length; i++) {
String key = controllers.keys.elementAt(i - 1);
if (controllers[key]!.text.isEmpty) {
snackbarService.showSnackbar(
message: 'Nama tidak boleh kosong',
title: 'Error',
duration: const Duration(seconds: 2),
);
return;
}
}
// check if the image is empty
for (var i = 1; i <= controllers.length; i++) {
// String key = controllers.keys.elementAt(i - 1);
if (!imagePaths.containsKey('image$i')) {
snackbarService.showSnackbar(
message: 'Gambar tidak boleh kosong',
title: 'Error',
duration: const Duration(seconds: 2),
);
return;
}
}
setBusy(true);
easyloading.customLoading('Edit Detail Jabatan $name');
Map<String, dynamic> array = {
'jabatan': name,
'jumlah': controllers.length.toString(),
};
// log.i(imagePaths.values);
// rearrange the data
for (var i = 1; i <= controllers.length; i++) {
String key = controllers.keys.elementAt(i - 1);
array['nama$i'] = controllers[key]!.text;
// array['nama$i'] = controllers['$name$i']!.text;
array['image$i'] = await MultipartFile.fromFile(
imagePaths.values.elementAt(i - 1),
filename: imageFiles.values.elementAt(i - 1).name,
contentType: MediaType.parse('image/jpeg'),
);
}
FormData formData = FormData.fromMap(array);
// log.i(array);
try {
// // // log.i(formData.fields);
await _httpService.postWithFormData('edit_jabatan', formData);
// log.i(response.data);
completer(
DialogResponse(confirmed: true),
);
} catch (e) {
log.e(e);
completer(
DialogResponse(confirmed: false),
);
} finally {
easyloading.dismissLoading();
setBusy(false);
}
}
void removeWidget(int index, Key containerKey) {
log.i('remove widget $index');
// remove the widget that has key
child.removeWhere((element) => element.key == containerKey);
// remove the controller
disposeSingleController('$name$index');
// // remove the image
imageFiles.remove('image$index');
imagePaths.remove('image$index');
imageBytes.remove('image$index');
log.i(controllers);
log.i(imagePaths);
notifyListeners();
}
}

View File

@ -0,0 +1,335 @@
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:stacked/stacked.dart';
import './struktur_organisasi_view_model.dart';
class StrukturOrganisasiView extends StatelessWidget {
const StrukturOrganisasiView({super.key});
@override
Widget build(BuildContext context) {
return ViewModelBuilder<StrukturOrganisasiViewModel>.reactive(
viewModelBuilder: () => StrukturOrganisasiViewModel(),
onViewModelReady: (StrukturOrganisasiViewModel model) async {
await model.init();
},
builder: (
BuildContext context,
StrukturOrganisasiViewModel model,
Widget? child,
) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(20),
child: SingleChildScrollView(
child: Column(
children: [
const Center(
child: Image(
image: AssetImage("assets/logo.png"),
width: 150,
height: 150,
),
),
const SizedBox(
height: 20,
),
FirstWidget(
title: "Ketua",
data: model.dataKetua,
),
const SizedBox(
height: 20,
),
FirstWidget(
title: 'Sekretaris',
data: model.dataSekretaris,
),
const SizedBox(
height: 20,
),
FirstWidget(
title: 'Bendahara',
data: model.dataBendahara,
),
const SizedBox(
height: 20,
),
FirstWidget(
title: 'Wakil Bendahara',
data: model.dataWakilBendahara,
),
const SizedBox(
height: 20,
),
SecondWidget(
title: 'Seksi Pengasuh',
data: model.dataPengasuh,
length: model.dataPengasuhLength,
),
const SizedBox(
height: 20,
),
SecondWidget(
title: 'Seksi Ibadah',
data: model.dataIbadah,
length: model.dataIbadahLength,
),
const SizedBox(
height: 20,
),
SecondWidget(
title: 'Seksi Pendidikan',
data: model.dataPendidikan,
length: model.dataPendidikanLength,
),
const SizedBox(
height: 20,
),
SecondWidget(
title: 'Seksi Kesehatan',
data: model.dataKesehatan,
length: model.dataKesehatanLength,
),
const SizedBox(
height: 20,
),
SecondWidget(
title: 'Seksi Kebersihan',
data: model.dataKebersihan,
length: model.dataKebersihanLength,
),
const SizedBox(
height: 20,
),
SecondWidget(
title: 'Seksi Keterampilan',
data: model.dataKeterampilan,
length: model.dataKeterampilanLength,
),
const SizedBox(
height: 20,
),
SecondWidget(
title: 'Sarana dan Prasarana',
data: model.dataSaranaDanPrasarana,
length: model.dataSaranaDanPrasaranaLength,
),
const SizedBox(
height: 20,
),
SecondWidget(
title: 'Seksi Konsumsi',
data: model.dataKonsumsi,
length: model.dataKonsumsiLength,
),
const SizedBox(
height: 20,
),
SecondWidget(
title: 'Seksi Keamanan',
data: model.dataKeamanan,
length: model.dataKeamananLength,
),
const SizedBox(
height: 20,
),
SecondWidget(
title: 'Seksi Gedung',
data: model.dataGedung,
length: model.dataGedungLength,
),
const SizedBox(
height: 20,
),
SecondWidget(
title: 'Seksi Wisma',
data: model.dataWisma,
length: model.dataWismaLength,
),
const SizedBox(
height: 20,
),
SecondWidget(
title: 'Kelompok Putra',
data: model.dataKelompokPutra,
length: model.dataKelompokPutraLength,
),
const SizedBox(
height: 20,
),
SecondWidget(
title: 'Kelompok Putri',
data: model.dataKelompokPutri,
length: model.dataKelompokPutriLength,
),
const SizedBox(
height: 20,
),
],
),
),
),
);
},
);
}
}
class SecondWidget extends ViewModelWidget<StrukturOrganisasiViewModel> {
final String title;
final Map<String, dynamic> data;
final int length;
const SecondWidget({
super.key,
required this.title,
required this.data,
required this.length,
});
@override
Widget build(BuildContext context, StrukturOrganisasiViewModel viewModel) {
return Card(
elevation: 4,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
title,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(
width: 10,
),
viewModel.role == 'admin'
? IconButton(
onPressed: () {
viewModel.editData(title, true);
},
icon: const Icon(Icons.edit),
)
: const SizedBox(),
],
),
for (var i = 0; i < length; i++)
Column(
children: [
Center(
child: data.isEmpty
? null
: ClipRRect(
borderRadius: BorderRadius.circular(50),
child: Image.network(
'${dotenv.env['url']}${data['img_url$i']}',
width: 100,
height: 100,
fit: BoxFit.cover,
),
),
),
const SizedBox(
height: 2,
),
Text(
data['nama$i'] ?? '...',
style: const TextStyle(
fontSize: 20,
),
),
const SizedBox(
height: 5,
),
// create a line
const Divider(
color: Colors.black,
height: 20,
thickness: 1,
indent: 20,
endIndent: 20,
),
const SizedBox(height: 5),
],
),
],
),
);
}
}
class FirstWidget extends ViewModelWidget<StrukturOrganisasiViewModel> {
final String title;
final Map<String, dynamic> data;
const FirstWidget({
super.key,
required this.title,
required this.data,
});
@override
Widget build(BuildContext context, StrukturOrganisasiViewModel viewModel) {
return Card(
elevation: 4,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
title,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(
width: 10,
),
viewModel.role == 'admin'
? IconButton(
onPressed: () {
viewModel.editData(title, false);
},
icon: const Icon(Icons.edit),
)
: const SizedBox(),
],
),
Center(
child: data.isEmpty
? null
: ClipRRect(
borderRadius: BorderRadius.circular(50),
child: Image.network(
'${dotenv.env['url']}${data['img_url']}',
width: 100,
height: 100,
fit: BoxFit.cover,
),
),
),
const SizedBox(
height: 2,
),
Text(
data['nama'] ?? '...',
style: const TextStyle(
fontSize: 20,
),
),
],
),
);
}
}

View File

@ -0,0 +1,178 @@
import 'package:shared_preferences/shared_preferences.dart';
import '../../../../app/app.dialogs.dart';
import '../../../../app/app.locator.dart';
import '../../../../app/app.logger.dart';
import '../../../../app/core/custom_base_view_model.dart';
import '../../../../services/http_services.dart';
import '../../../../services/my_easyloading.dart';
class StrukturOrganisasiViewModel extends CustomBaseViewModel {
final log = getLogger('StrukturOrganisasiViewModel');
final _httpService = locator<MyHttpServices>();
final easyLoading = locator<MyEasyLoading>();
String? role;
Map<String, dynamic> dataKetua = {};
Map<String, dynamic> dataSekretaris = {};
Map<String, dynamic> dataBendahara = {};
Map<String, dynamic> dataWakilBendahara = {};
Map<String, dynamic> dataPengasuh = {};
int dataPengasuhLength = 0;
Map<String, dynamic> dataIbadah = {};
int dataIbadahLength = 0;
Map<String, dynamic> dataPendidikan = {};
int dataPendidikanLength = 0;
Map<String, dynamic> dataKebersihan = {};
int dataKebersihanLength = 0;
Map<String, dynamic> dataKesehatan = {};
int dataKesehatanLength = 0;
Map<String, dynamic> dataKeterampilan = {};
int dataKeterampilanLength = 0;
Map<String, dynamic> dataSaranaDanPrasarana = {};
int dataSaranaDanPrasaranaLength = 0;
Map<String, dynamic> dataKonsumsi = {};
int dataKonsumsiLength = 0;
Map<String, dynamic> dataKeamanan = {};
int dataKeamananLength = 0;
Map<String, dynamic> dataGedung = {};
int dataGedungLength = 0;
Map<String, dynamic> dataWisma = {};
int dataWismaLength = 0;
Map<String, dynamic> dataKelompokPutra = {};
int dataKelompokPutraLength = 0;
Map<String, dynamic> dataKelompokPutri = {};
int dataKelompokPutriLength = 0;
Future<void> init() async {
prefs.then((SharedPreferences prefs) {
role = prefs.getString('role');
});
getData('Ketua', false, null);
getData('Sekretaris', false, null);
getData('Bendahara', false, null);
getData('Wakil Bendahara', false, null);
getData('Seksi Pengasuh', true, dataPengasuhLength);
getData('Seksi Ibadah', true, dataIbadahLength);
getData('Seksi Pendidikan', true, dataPendidikanLength);
getData('Seksi Kebersihan', true, dataKebersihanLength);
getData('Seksi Kesehatan', true, dataKesehatanLength);
getData('Seksi Keterampilan', true, dataKeterampilanLength);
getData('Sarana dan Prasarana', true, dataSaranaDanPrasaranaLength);
getData('Seksi Konsumsi', true, dataKonsumsiLength);
getData('Seksi Keamanan', true, dataKeamananLength);
getData('Seksi Gedung', true, dataGedungLength);
getData('Seksi Wisma', true, dataWismaLength);
getData('Kelompok Putra', true, dataKelompokPutraLength);
getData('Kelompok Putri', true, dataKelompokPutriLength);
}
getData(String jabatan, bool stat, int? length) async {
easyLoading.customLoading('Loading Data');
setBusy(true);
try {
var response = await _httpService.get('jabatan?jabatan=$jabatan');
// log.i(response.data);
if (response.data['data'].length == 0) return;
Map<String, dynamic> data = {};
if (!stat) {
var datanya = response.data['data'][0];
data['nama'] = datanya['nama'];
data['img_url'] = datanya['img_url'];
// log.i(data);
// return;
} else {
var datanya = response.data['data'];
length = datanya.length;
for (var i = 0; i < datanya.length; i++) {
data['nama$i'] = datanya[i]['nama'];
data['img_url$i'] = datanya[i]['img_url'];
}
}
if (jabatan == 'Ketua') {
dataKetua = data;
} else if (jabatan == 'Sekretaris') {
dataSekretaris = data;
} else if (jabatan == 'Bendahara') {
dataBendahara = data;
} else if (jabatan == 'Wakil Bendahara') {
dataWakilBendahara = data;
} else if (jabatan == 'Seksi Pengasuh') {
dataPengasuh = data;
dataPengasuhLength = length!;
} else if (jabatan == 'Seksi Ibadah') {
dataIbadah = data;
dataIbadahLength = length!;
} else if (jabatan == 'Seksi Pendidikan') {
dataPendidikan = data;
dataPendidikanLength = length!;
} else if (jabatan == 'Seksi Kebersihan') {
dataKebersihan = data;
dataKebersihanLength = length!;
} else if (jabatan == 'Seksi Kesehatan') {
dataKesehatan = data;
dataKesehatanLength = length!;
} else if (jabatan == 'Seksi Keterampilan') {
dataKeterampilan = data;
dataKeterampilanLength = length!;
} else if (jabatan == 'Sarana dan Prasarana') {
dataSaranaDanPrasarana = data;
dataSaranaDanPrasaranaLength = length!;
} else if (jabatan == 'Seksi Konsumsi') {
dataKonsumsi = data;
dataKonsumsiLength = length!;
} else if (jabatan == 'Seksi Keamanan') {
dataKeamanan = data;
dataKeamananLength = length!;
} else if (jabatan == 'Seksi Gedung') {
dataGedung = data;
dataGedungLength = length!;
} else if (jabatan == 'Seksi Wisma') {
dataWisma = data;
dataWismaLength = length!;
} else if (jabatan == 'Kelompok Putra') {
dataKelompokPutra = data;
dataKelompokPutraLength = length!;
} else if (jabatan == 'Kelompok Putri') {
dataKelompokPutri = data;
dataKelompokPutriLength = length!;
}
} catch (e) {
log.e(e);
} finally {
notifyListeners();
setBusy(false);
easyLoading.dismissLoading();
}
}
void editData(String jabatan, bool bool) {
var res = dialogService.showCustomDialog(
variant: DialogType.editStrukrurOrganisasiDialogView,
data: {
"jabatan": jabatan,
'tambahan': bool,
},
);
res.whenComplete(() async => {
// await Future.delayed(Duration(seconds: 1)),
init(),
});
}
}

View File

@ -0,0 +1,67 @@
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import './visi_misi_view_model.dart';
class VisiMisiView extends StatelessWidget {
const VisiMisiView({super.key});
@override
Widget build(BuildContext context) {
return ViewModelBuilder<VisiMisiViewModel>.nonReactive(
viewModelBuilder: () => VisiMisiViewModel(),
onViewModelReady: (VisiMisiViewModel model) async {
await model.init();
},
builder: (
BuildContext context,
VisiMisiViewModel model,
Widget? child,
) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(20),
child: SingleChildScrollView(
child: Column(
children: [
const Center(
child: Image(
image: AssetImage("assets/logo.png"),
width: 150,
height: 150,
),
),
const Center(
child: Text(
'Visi',
style: TextStyle(fontSize: 20),
),
),
const SizedBox(height: 10),
Text(
model.visi,
style: const TextStyle(fontSize: 18),
textAlign: TextAlign.justify,
),
const SizedBox(height: 30),
const Center(
child: Text(
'Misi',
style: TextStyle(fontSize: 20),
),
),
const SizedBox(height: 10),
Text(
'${model.misi1}\n\n${model.misi2}\n\n${model.misi3}\n\n${model.misi4}\n\n${model.misi5}',
style: const TextStyle(fontSize: 18),
textAlign: TextAlign.justify,
),
],
),
),
),
);
},
);
}
}

View File

@ -0,0 +1,18 @@
import 'package:panti_asuhan/app/core/custom_base_view_model.dart';
class VisiMisiViewModel extends CustomBaseViewModel {
String visi =
"Terwujudnya anak asuhan yang berakhlak mulia sesuai dengan tujuan Pensyarikatan ";
String misi1 =
"1. Mengembangkan proses pengkaderan menuju masyarakat Islam yang sebenar-benarnya";
String misi2 =
"2. Memberikan pembinaan Berbasis Keluarga Islami sehingga melahirkan anak asuhan yang berkarakter";
String misi3 =
"3. Menegmbangkan Pendidikan Inklusif secara formal dan informal.";
String misi4 =
"4. Meningkatkan proses belajar sehingga menjadi anak asuhan yang berkualitas dan berprilaku Islami";
String misi5 =
"5. Menjadikan LKSA ABADI Aisyyah sebagai tempat beramal bagu seluruh lapisan masyarakat";
Future<void> init() async {}
}

View File

@ -0,0 +1,422 @@
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import '../../../app/themes/app_colors.dart';
import '../../../app/themes/app_text.dart';
import '../../widgets/my_textformfield.dart';
import './detail_dana_sosial_view_model.dart';
class DetailDanaSosialView extends StatelessWidget {
final int id;
final bool isKhusus;
const DetailDanaSosialView(
{Key? key, required this.id, this.isKhusus = false})
: super(key: key);
@override
Widget build(BuildContext context) {
return ViewModelBuilder<DetailDanaSosialViewModel>.reactive(
viewModelBuilder: () => DetailDanaSosialViewModel(),
onViewModelReady: (DetailDanaSosialViewModel model) async {
await model.init(id, isKhusus);
},
builder: (
BuildContext context,
DetailDanaSosialViewModel model,
Widget? child,
) {
return Scaffold(
appBar: AppBar(
title: const Text('Detail Dana Sosial',
style: TextStyle(color: Colors.white)),
backgroundColor: mainColor,
iconTheme: const IconThemeData(color: Colors.white),
),
body: Padding(
padding: const EdgeInsets.all(15),
child: model.danaSosialModel == null
? const Center(
child: CircularProgressIndicator(),
)
: (model.danaSosialModel!.bentuk == 'Pemasukan'
? const SafeArea(child: PemasukanWidget())
: const PengeluaranWidget()),
),
);
},
);
}
}
class PengeluaranWidget extends ViewModelWidget<DetailDanaSosialViewModel> {
const PengeluaranWidget({
super.key,
});
@override
Widget build(BuildContext context, DetailDanaSosialViewModel viewModel) {
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
"Bentuk",
style: regularTextStyle.copyWith(color: mainColor),
),
),
MyTextFormField(
controller: viewModel.bentukController,
maxLines: 1,
readOnly: true,
),
const SizedBox(
height: 15,
),
Align(
alignment: Alignment.centerLeft,
child: Text(
"Tanggal",
style: regularTextStyle.copyWith(color: mainColor),
),
),
MyTextFormField(
controller: viewModel.tanggalController,
maxLines: 1,
readOnly: true,
),
const SizedBox(
height: 15,
),
Align(
alignment: Alignment.centerLeft,
child: Text(
"Jenis",
style: regularTextStyle.copyWith(color: mainColor),
),
),
MyTextFormField(
controller: viewModel.jenisController,
maxLines: 1,
readOnly: true,
),
Visibility(
visible: viewModel.jenisBool,
child: const SizedBox(
height: 15,
),
),
Visibility(
visible: viewModel.jenisBool,
child: Align(
alignment: Alignment.centerLeft,
child: Text(
"Jumlah",
style: regularTextStyle.copyWith(color: mainColor),
),
),
),
Visibility(
visible: viewModel.jenisBool,
child: MyTextFormField(
controller: viewModel.jumlahController,
maxLines: 1,
readOnly: true,
),
),
Visibility(
visible: !viewModel.jenisBool,
child: const SizedBox(
height: 15,
),
),
Visibility(
visible: !viewModel.jenisBool,
child: Align(
alignment: Alignment.centerLeft,
child: Text(
"Keterangan",
style: regularTextStyle.copyWith(color: mainColor),
),
),
),
Visibility(
visible: !viewModel.jenisBool,
child: MyTextFormField(
controller: viewModel.keteranganController,
maxLines: 2,
readOnly: true,
),
),
const SizedBox(
height: 15,
),
Expanded(
child: Align(
alignment: Alignment.centerRight,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"Tanda Tangan",
style: regularTextStyle.copyWith(color: mainColor),
),
SizedBox(
height: 150,
width: 150,
child: viewModel.danaSosialModel == null
? Container(
decoration: BoxDecoration(
border: Border.all(color: mainColor),
),
)
: (viewModel.danaSosialModel!.status! ==
'Belum Dikonfirmasi'
? const TtdWidget()
: Image.asset(
'assets/qrcode.png',
fit: BoxFit.cover,
)),
),
const SizedBox(
height: 10,
),
Text(
"Dr. Andi Fitriani D, S.Ag, M.Pd",
style: regularTextStyle.copyWith(
color: mainColor,
fontWeight: FontWeight.bold,
),
),
// create a horizontal line
],
),
),
),
],
);
}
}
class PemasukanWidget extends ViewModelWidget<DetailDanaSosialViewModel> {
const PemasukanWidget({
super.key,
});
@override
Widget build(BuildContext context, DetailDanaSosialViewModel viewModel) {
return SingleChildScrollView(
child: SizedBox(
height: MediaQuery.of(context).size.height,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
"Nama Donator",
style: regularTextStyle.copyWith(color: mainColor),
),
),
MyTextFormField(
controller: viewModel.namaController,
maxLines: 1,
readOnly: true,
),
const SizedBox(
height: 15,
),
Align(
alignment: Alignment.centerLeft,
child: Text(
"Tanggal",
style: regularTextStyle.copyWith(color: mainColor),
),
),
MyTextFormField(
controller: viewModel.tanggalController,
maxLines: 1,
readOnly: true,
),
const SizedBox(
height: 15,
),
Align(
alignment: Alignment.centerLeft,
child: Text(
"Jenis",
style: regularTextStyle.copyWith(color: mainColor),
),
),
MyTextFormField(
controller: viewModel.jenisController,
maxLines: 1,
readOnly: true,
),
Visibility(
visible: viewModel.jenisBool,
child: const SizedBox(
height: 15,
),
),
Visibility(
visible: viewModel.jenisBool,
child: Align(
alignment: Alignment.centerLeft,
child: Text(
"Jumlah",
style: regularTextStyle.copyWith(color: mainColor),
),
),
),
Visibility(
visible: viewModel.jenisBool,
child: MyTextFormField(
controller: viewModel.jumlahController,
maxLines: 1,
readOnly: true,
),
),
Visibility(
visible: !viewModel.jenisBool,
child: const SizedBox(
height: 15,
),
),
if (!viewModel.jenisBool)
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Jenis Barang",
style: regularTextStyle.copyWith(color: mainColor),
),
MyTextFormField(
controller: viewModel.jenisBarangController,
maxLines: 1,
readOnly: true,
),
const SizedBox(
height: 15,
),
Text(
"Jumlah Barang",
style: regularTextStyle.copyWith(color: mainColor),
),
MyTextFormField(
controller: viewModel.jumlahBarangController,
maxLines: 1,
readOnly: true,
),
const SizedBox(
height: 15,
),
],
),
Visibility(
visible: !viewModel.jenisBool,
child: Align(
alignment: Alignment.centerLeft,
child: Text(
"Keterangan",
style: regularTextStyle.copyWith(color: mainColor),
),
),
),
Visibility(
visible: !viewModel.jenisBool,
child: MyTextFormField(
controller: viewModel.keteranganController,
maxLines: 2,
readOnly: true,
),
),
const SizedBox(
height: 15,
),
Expanded(
child: Align(
alignment: Alignment.centerRight,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
"Tanda Tangan",
style: regularTextStyle.copyWith(color: mainColor),
),
SizedBox(
height: 150,
width: 150,
child: viewModel.danaSosialModel == null
? Container(
decoration: BoxDecoration(
border: Border.all(color: mainColor),
),
)
: (viewModel.danaSosialModel!.status! ==
'Belum Dikonfirmasi'
? const TtdWidget()
: Image.asset(
'assets/qrcode.png',
fit: BoxFit.cover,
)),
),
const SizedBox(
height: 10,
),
Text(
"DRA. HJ. CIA",
style: regularTextStyle.copyWith(
color: mainColor,
fontWeight: FontWeight.bold,
),
),
// create a horizontal line
],
),
),
),
],
),
),
);
}
}
class TtdWidget extends ViewModelWidget<DetailDanaSosialViewModel> {
const TtdWidget({
super.key,
});
@override
Widget build(BuildContext context, DetailDanaSosialViewModel viewModel) {
return Container(
decoration: BoxDecoration(
border: Border.all(color: mainColor),
),
child: viewModel.role == 'pimpinan' || viewModel.role == 'admin'
? InkWell(
onTap: () {
viewModel.handleTtd();
},
child: Center(
child: Text(
"Tanda Tangan",
style: regularTextStyle.copyWith(
color: mainColor,
fontStyle: FontStyle.italic,
),
),
),
)
: null,
);
}
}

View File

@ -0,0 +1,127 @@
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../../app/app.locator.dart';
import '../../../app/app.logger.dart';
import '../../../app/core/custom_base_view_model.dart';
import '../../../model/dana_sosial_model.dart';
import '../../../services/http_services.dart';
import '../../../services/my_easyloading.dart';
import '../../../services/other_function.dart';
class DetailDanaSosialViewModel extends CustomBaseViewModel {
final log = getLogger('DetailDanaSosialViewModel');
final _httpService = locator<MyHttpServices>();
final easyLoading = locator<MyEasyLoading>();
bool isKhusus = false;
int? idDanaSosial;
DanaSosialModel? danaSosialModel;
bool jenisBool = true;
String? role;
TextEditingController namaController = TextEditingController();
TextEditingController tanggalController = TextEditingController();
TextEditingController jumlahController = TextEditingController();
TextEditingController jenisController = TextEditingController();
TextEditingController keteranganController = TextEditingController();
TextEditingController bentukController = TextEditingController();
// my revision syntax
TextEditingController jenisBarangController = TextEditingController();
TextEditingController jumlahBarangController = TextEditingController();
// TextEditingController satuanController = TextEditingController();
Future<void> init(int id, bool isKhusus) async {
log.i('init and id: $id');
this.isKhusus = isKhusus;
getData(id);
idDanaSosial = id;
prefs.then((SharedPreferences prefs) {
role = prefs.getString('role');
});
}
getData(int id) async {
setBusy(true);
easyLoading.showLoading();
try {
String url = isKhusus
? 'dana_sosial_khusus_detail?id=$id'
: 'dana_sosial_detail?id=$id';
var response = await _httpService.get(url);
log.i(response.data['data']);
danaSosialModel = DanaSosialModel.fromJson(response.data['data']);
namaController.text = danaSosialModel!.nama!;
tanggalController.text = danaSosialModel!.tanggal!;
jumlahController.text =
"Rp. ${OtherFunction().commaFormat(danaSosialModel!.jumlah != null ? int.parse(danaSosialModel!.jumlah!) : 0)}";
jenisController.text = danaSosialModel!.jenisDonasi!;
keteranganController.text = danaSosialModel!.keterangan!;
bentukController.text = danaSosialModel!.bentuk!;
// my revision syntax
jenisBarangController.text = danaSosialModel!.jenisBarang!;
jumlahBarangController.text =
'${danaSosialModel!.jumlahBarang!} ${danaSosialModel!.satuan!}';
// satuanController.text = danaSosialModel!.satuan! ;
if (danaSosialModel!.jenisDonasi == 'Uang') {
jenisBool = true;
} else {
jenisBool = false;
}
// log.i('status: ${danaSosialModel!.status}');
setBusy(false);
notifyListeners();
} catch (e) {
log.e(e);
setBusy(false);
} finally {
easyLoading.dismissLoading();
}
}
void handleTtd() {
dialogService
.showConfirmationDialog(
title: 'Konfirmasi',
description: 'Apakah anda yakin ingin mengkonfirmasi tanda tangan ini?',
cancelTitle: 'Tidak',
confirmationTitle: 'Ya',
)
.then((value) async {
if (value!.confirmed) {
setBusy(true);
easyLoading.showLoading();
try {
FormData formData = FormData.fromMap({
'id': idDanaSosial,
});
String url = isKhusus ? 'dana_sosial_khusus_ttd' : 'dana_sosial_ttd';
var response = await _httpService.postWithFormData(url, formData);
log.i(response.data);
getData(idDanaSosial!);
setBusy(false);
notifyListeners();
} catch (e) {
log.e(e);
setBusy(false);
dialogService.showDialog(
title: 'Error',
description: e.toString(),
);
} finally {
easyLoading.dismissLoading();
}
}
});
}
}

View File

@ -1,9 +1,11 @@
import 'package:flutter/material.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 '../../../app/app.router.dart';
import '../../../app/themes/app_text.dart';
import '../../widgets/my_button.dart';
import '../../widgets/my_textformfield.dart';
import './login_screen_view_model.dart';
class LoginScreenView extends StatelessWidget {
@ -24,58 +26,79 @@ 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,
child: SingleChildScrollView(
child: Form(
key: model.formKey,
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,
),
),
const SizedBox(height: 10),
Text(
"SILAHKAN LOGIN",
style: boldTextStyle.copyWith(
fontSize: 18,
),
),
const SizedBox(
height: 10,
),
MyTextFormField(
hintText: "Username",
prefixIcon: const Icon(Icons.person),
controller: model.usernameController,
validator:
Validatorless.required("Username tidak boleh kosong"),
),
const SizedBox(
height: 10,
),
MyTextFormField(
hintText: "Password",
prefixIcon: const Icon(Icons.lock),
controller: model.passwordController,
obscureText: true,
validator:
Validatorless.required("Password tidak boleh kosong"),
),
const SizedBox(
height: 10,
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.5,
child: MyButton(
text: "LOGIN",
onPressed: () {
if (model.formKey.currentState!.validate()) {
model.login();
}
},
),
),
TextButton(
onPressed: () {
model.navigationService
.navigateTo(Routes.userIndexTrackingView);
},
child: const Text(
"Kembali ke beranda",
),
)
],
),
// show the logo.png
const Center(
child: Image(
image: AssetImage("assets/logo.png"),
width: 150,
height: 150,
),
),
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();
},
),
),
],
),
),
),
);

View File

@ -1,13 +1,45 @@
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../../app/app.logger.dart';
import '../../../app/app.router.dart';
import '../../../app/core/custom_base_view_model.dart';
class LoginScreenViewModel extends CustomBaseViewModel {
final log = getLogger('LoginScreenViewModel');
final Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
Future<void> init() async {}
goToAdmin() {
log.d('goToAdmin');
navigationService.navigateTo(Routes.adminIndexTrackingView);
final formKey = GlobalKey<FormState>();
TextEditingController usernameController = TextEditingController();
TextEditingController passwordController = TextEditingController();
login() {
if (usernameController.text == 'admin' &&
passwordController.text == 'admin') {
_prefs.then((SharedPreferences prefs) {
prefs.setBool('isLogin', true);
prefs.setString('role', 'admin');
});
log.d('goToAdmin');
navigationService.navigateTo(Routes.adminIndexTrackingView);
} else if (usernameController.text == 'pimpinan' &&
passwordController.text == 'pimpinan') {
_prefs.then((SharedPreferences prefs) {
prefs.setBool('isLogin', true);
prefs.setString('role', 'pimpinan');
});
log.d('goToPimpinan');
navigationService.navigateTo(Routes.pimpinanIndexTrackingView);
} else {
dialogService.showDialog(
title: 'Gagal',
description: 'Username atau password salah',
);
_prefs.then((SharedPreferences prefs) {
prefs.setBool('isLogin', false);
prefs.remove('role');
});
}
}
}

View File

@ -0,0 +1,99 @@
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
import 'package:stylish_bottom_bar/model/bar_items.dart';
import 'package:stylish_bottom_bar/stylish_bottom_bar.dart';
import '../../../../app/app.router.dart';
import '../../../../app/themes/app_colors.dart';
import '../../../../app/themes/app_text.dart';
import './pimpinan_index_tracking_view_model.dart';
class PimpinanIndexTrackingView extends StatelessWidget {
const PimpinanIndexTrackingView({super.key});
@override
Widget build(BuildContext context) {
return ViewModelBuilder<PimpinanIndexTrackingViewModel>.reactive(
viewModelBuilder: () => PimpinanIndexTrackingViewModel(),
onViewModelReady: (PimpinanIndexTrackingViewModel model) async {
await model.init();
},
builder: (
BuildContext context,
PimpinanIndexTrackingViewModel model,
Widget? child,
) {
return Scaffold(
appBar: AppBar(
title: Text(
model.header,
style: const TextStyle(
color: Colors.white,
fontSize: 20,
),
),
backgroundColor: mainColor,
elevation: 0,
automaticallyImplyLeading: false,
actions: [
IconButton(
onPressed: () {
// model.navigationService.navigateTo(Routes.loginScreenView);
model.logout();
},
icon: const Icon(
Icons.logout,
color: Colors.white,
),
),
],
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
child: ExtendedNavigator(
navigatorKey: StackedService.nestedNavigationKey(4),
router: PimpinanIndexTrackingViewRouter(),
initialRoute: PimpinanIndexTrackingViewRoutes.danaSosialAdminView,
),
),
bottomNavigationBar: StylishBottomBar(
items: [
for (var item in model.bottomNavBarList)
BottomBarItem(
icon: Icon(item['icon'],
color: model.currentIndex ==
model.bottomNavBarList.indexOf(item)
? sixthGrey
: backgroundColor),
title: Text(
item['name'],
style: regularTextStyle.copyWith(
color: model.currentIndex ==
model.bottomNavBarList.indexOf(item)
? sixthGrey
: Colors.grey,
),
),
backgroundColor:
model.currentIndex == model.bottomNavBarList.indexOf(item)
? Colors.white
: Colors.grey,
),
],
currentIndex: model.currentIndex,
hasNotch: true,
backgroundColor: mainColor,
onTap: (value) {
model.handleNavigation(value);
},
option: BubbleBarOptions(
barStyle: BubbleBarStyle.horizotnal,
bubbleFillStyle: BubbleFillStyle.fill,
opacity: 0.3),
),
);
},
);
}
}

View File

@ -0,0 +1,90 @@
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
import '../../../../app/app.locator.dart';
import '../../../../app/app.logger.dart';
import '../../../../app/app.router.dart';
class PimpinanIndexTrackingViewModel extends IndexTrackingViewModel {
final log = getLogger('PimpinanIndexTrackingViewModel');
final _navigationService = locator<NavigationService>();
final _dialogService = locator<DialogService>();
final Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
String header = 'Dana Sosial';
final _bottomNavBarList = [
{
'name': 'List',
'icon': Icons.people_alt_outlined,
'header': 'List Anak Panti'
},
{'name': 'Dana', 'icon': Icons.money, 'header': 'Dana Sosial'},
// {'name': 'Dana', 'icon': Icons.money, 'header': 'Dana Sosial Khusus'},
{
'name': 'Profil',
'icon': Icons.person_4_outlined,
'header': 'Profil Panti Asuhan'
},
];
List<Map<String, dynamic>> get bottomNavBarList => _bottomNavBarList;
final List<String> _views = [
PimpinanIndexTrackingViewRoutes.dataSiswaView,
PimpinanIndexTrackingViewRoutes.danaSosialAdminView,
// PimpinanIndexTrackingViewRoutes.danaSosialKhususView,
PimpinanIndexTrackingViewRoutes.profilView,
];
Future<void> init() async {
_prefs.then((SharedPreferences prefs) {
if (prefs.getString('role') == 'pimpinan') {
setIndex(1);
// // await 2 seconds to make sure the view is loaded
// Future.delayed(const Duration(milliseconds: 500));
} else {
prefs.setBool('isLogin', false);
prefs.remove('role');
_navigationService.clearStackAndShow(Routes.loginScreenView);
}
});
}
void handleNavigation(int index) {
log.d("handleNavigation: $index");
log.d("currentIndex: $currentIndex");
if (currentIndex == index) return;
setIndex(index);
header = _bottomNavBarList[index]['header'] as String;
_navigationService.navigateTo(
_views[index],
id: 4,
);
}
logout() {
_dialogService
.showConfirmationDialog(
title: 'Logout',
description: 'Apakah anda yakin ingin logout?',
cancelTitle: 'Ya',
confirmationTitle: 'Tidak',
// barrierDismissible: true,
)
.then((value) {
if (!value!.confirmed) {
_prefs.then((SharedPreferences prefs) {
prefs.setBool('isLogin', false);
prefs.remove('role');
_navigationService.clearStackAndShow(Routes.loginScreenView);
});
} else {
_navigationService.back();
}
});
}
}

View File

@ -1,3 +1,5 @@
import 'package:shared_preferences/shared_preferences.dart';
import '../../../app/app.logger.dart';
import '../../../app/app.router.dart';
import '../../../app/core/custom_base_view_model.dart';
@ -7,8 +9,29 @@ class SplashScreenViewModel extends CustomBaseViewModel {
Future<void> init() async {
// wait 2 seconds then navigate to login
await Future.delayed(const Duration(seconds: 2));
await navigationService.navigateTo(
Routes.loginScreenView,
);
prefs.then((SharedPreferences prefs) {
if (prefs.getBool('isLogin') != true) {
return navigationService.navigateTo(
Routes.userIndexTrackingView,
);
}
if (prefs.getString('role') == 'admin') {
return navigationService.navigateTo(
Routes.adminIndexTrackingView,
);
}
if (prefs.getString('role') == 'pimpinan') {
return navigationService.navigateTo(
Routes.pimpinanIndexTrackingView,
);
}
});
// await navigationService.navigateTo(
// // Routes.loginScreenView,
// Routes.userIndexTrackingView,
// );
}
}

View File

@ -0,0 +1,416 @@
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import 'package:validatorless/validatorless.dart';
import '../../../app/themes/app_colors.dart';
import '../../../app/themes/app_text.dart';
import '../../widgets/my_button.dart';
import '../../widgets/my_textformfield.dart';
import './tambah_dana_sosial_view_model.dart';
class TambahDanaSosialView extends StatelessWidget {
final bool isKhusus;
const TambahDanaSosialView({
Key? key,
this.isKhusus = false,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return ViewModelBuilder<TambahDanaSosialViewModel>.reactive(
viewModelBuilder: () => TambahDanaSosialViewModel(),
onViewModelReady: (TambahDanaSosialViewModel model) async {
await model.init(isKhusus);
},
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: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Bentuk Donasi",
style: regularTextStyle.copyWith(color: mainColor),
),
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<String>(
value: model.bentukDonasi,
onChanged: (String? newValue) {
// model.setSelectedbentukDonasi(newValue!);
model.log.i(newValue);
model.bentukDonasi = newValue!;
model.notifyListeners();
},
items: model.bentukDonasiList.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: regularTextStyle.copyWith(
fontSize: 16,
),
),
);
}).toList(),
),
),
),
const SizedBox(height: 20),
Visibility(
visible: model.bentukDonasi == 'Pemasukan',
child: Text(
"Nama Donator",
style: regularTextStyle.copyWith(color: mainColor),
),
),
Visibility(
visible: model.bentukDonasi == 'Pemasukan' &&
isKhusus == false,
child: MyTextFormField(
hintText: "Nama Donatur",
controller: model.namaController,
maxLines: 1,
// validator: Validatorless.required(
// 'Nama Donatur tidak boleh kosong'),
),
),
Visibility(
visible: model.bentukDonasi == 'Pemasukan' &&
isKhusus == true,
child: 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<String>(
value: model.donaturSelected,
onChanged: (String? newValue) {
// model.setSelectedjenisDonasi(newValue!);
model.log.i(newValue);
model.donaturSelected = newValue!;
// check where is the index and get the ['id_donatur']
var index = model.listDonatur.indexWhere(
(element) => element == newValue,
);
model.donaturSelectedIndex = int.parse(
model.listDonaturMap[index]['id_donatur'],
);
model.log.i(model.donaturSelectedIndex);
},
items: model.listDonatur.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: regularTextStyle.copyWith(
fontSize: 16,
),
),
);
}).toList(),
),
),
),
),
Visibility(
visible: model.bentukDonasi == 'Pemasukan',
child: const SizedBox(height: 20),
),
Text(
"Jenis Donasi / Pengeluaran",
style: regularTextStyle.copyWith(color: mainColor),
),
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<String>(
value: model.jenisDonasi,
onChanged: (String? newValue) {
// model.setSelectedjenisDonasi(newValue!);
model.log.i(newValue);
model.jenisDonasi = newValue!;
model.notifyListeners();
},
items: model.jenisDonasiList.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: regularTextStyle.copyWith(
fontSize: 16,
),
),
);
}).toList(),
),
),
),
// ini jika barang
if (model.jenisDonasi == 'Barang')
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 20),
Text(
"Jenis Barang",
style:
regularTextStyle.copyWith(color: mainColor),
),
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<String>(
value: model.jenisBarangSelected,
onChanged: (String? newValue) {
// model.setSelectedjenisDonasi(newValue!);
model.log.i(newValue);
model.jenisBarangSelected = newValue!;
model.changeSatuan(newValue);
model.notifyListeners();
},
items:
model.jenisBarangList.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: regularTextStyle.copyWith(
fontSize: 16,
),
),
);
}).toList(),
),
),
),
],
),
// ini jika barang dan jenis barang beras dll
if (model.jenisDonasi == 'Barang' &&
(model.jenisBarangSelected == 'Beras' ||
model.jenisBarangSelected == 'Mi Instan' ||
model.jenisBarangSelected == 'Telur' ||
model.jenisBarangSelected == 'Gula'))
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 20),
Text(
"Pilih Satuan ${model.jenisBarangSelected}",
style:
regularTextStyle.copyWith(color: mainColor),
),
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<String>(
value: model.satuanSelected,
onChanged: (String? newValue) {
// model.setSelectedjenisDonasi(newValue!);
model.log.i(newValue);
model.satuanSelected = newValue!;
model.notifyListeners();
},
items: model.satuanList.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: regularTextStyle.copyWith(
fontSize: 16,
),
),
);
}).toList(),
),
),
),
],
),
// ini jika barang dan jenis barang dll
if (model.jenisDonasi == 'Barang')
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 20),
Text(
"Jumlah ${model.satuanSelected}",
style:
regularTextStyle.copyWith(color: mainColor),
),
MyTextFormField(
hintText: "Jumlah ${model.satuanSelected}",
controller: model.jumlahBarangController,
maxLines: 1,
keyboardType: TextInputType.number,
maxLength: 3,
// validator: Validatorless.required(
// 'Nama Donatur tidak boleh kosong'),
),
],
),
Visibility(
visible: model.jenisDonasi == 'Uang',
child: const SizedBox(height: 20),
),
Visibility(
visible: model.jenisDonasi == 'Uang',
child: Text(
"Jumlah (Rp. )",
style: regularTextStyle.copyWith(color: mainColor),
),
),
Visibility(
visible: model.jenisDonasi == 'Uang',
child: MyTextFormField(
hintText: "Jumlah (Rp. ) Donasi",
keyboardType: TextInputType.number,
controller: model.jumlahController,
validator: model.jenisDonasi == 'Uang'
? Validatorless.multiple(
[
Validatorless.required(
'Jumlah tidak boleh kosong'),
Validatorless.number('Jumlah harus angka'),
],
)
: null,
),
),
const SizedBox(height: 20),
Text(
"Tanggal",
style: regularTextStyle.copyWith(color: mainColor),
),
MyTextFormField(
hintText: ' Pilih Tanggal',
readOnly: true,
controller: model.tanggalController,
validator: Validatorless.required(
'Tanggal tidak boleh kosong'),
onTap: () {
model.changeDate(context);
},
),
const SizedBox(height: 20),
Visibility(
visible: model.jenisDonasi == 'Barang',
child: Text(
"Keterangan",
style: regularTextStyle.copyWith(color: mainColor),
),
),
Visibility(
visible: model.jenisDonasi == 'Barang',
child: MyTextFormField(
hintText: 'Masukkan Keterangan',
maxLines: 3,
controller: model.keteranganController,
validator: model.jenisDonasi == 'Barang'
? Validatorless.multiple(
[
Validatorless.required(
'Keterangan tidak boleh kosong'),
],
)
: null,
),
),
const SizedBox(height: 20),
MyButton(
text: "Simpan Data",
onPressed: () {
if (isKhusus &&
model.donaturSelected == '-Tiada Donatur-') {
model.snackbarService.showSnackbar(
message: 'Donatur harus diisi terlebih dahulu',
title: 'Gagal',
duration: const Duration(seconds: 2),
);
return;
}
if (model.formKey.currentState!.validate()) {
model.log.i('Form Valid');
model.addData();
}
},
),
],
),
),
),
),
),
);
},
);
}
}

View File

@ -0,0 +1,195 @@
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import '../../../app/app.locator.dart';
import '../../../app/app.logger.dart';
import '../../../app/app.router.dart';
import '../../../app/core/custom_base_view_model.dart';
import '../../../services/http_services.dart';
import '../../../services/my_easyloading.dart';
class TambahDanaSosialViewModel extends CustomBaseViewModel {
final log = getLogger('TambahDanaSosialViewModel');
final _httpService = locator<MyHttpServices>();
final easyLoading = locator<MyEasyLoading>();
String bentukDonasi = 'Pemasukan';
List<String> bentukDonasiList = ['Pemasukan', 'Pengeluaran'];
String jenisDonasi = 'Uang';
List<String> jenisDonasiList = ['Uang', 'Barang'];
final formKey = GlobalKey<FormState>();
TextEditingController namaController = TextEditingController();
TextEditingController jumlahController = TextEditingController();
TextEditingController jumlahBarangController = TextEditingController();
TextEditingController tanggalController = TextEditingController();
TextEditingController keteranganController = TextEditingController();
// my revision syntax
List<String> jenisBarangList = [
'Beras',
'Minyak',
'Mi Instan',
'Telur',
'Gula',
'Susu',
'Pakaian'
];
String jenisBarangSelected = 'Beras';
String satuanSelected = 'Kg';
// TextEditingController jumlahBarangController = TextEditingController();
bool isJenisBarangSelected = true;
List<String> satuanLiterKg = ['Liter', 'Kg'];
List<String> satuanBungkusDos = ['Bungkus', 'Dos'];
List<String> satuanButirRak = ['Butir', 'Rak'];
List<String> satuanList = ['Liter', 'Kg'];
// end of my revision syntax
// the new revision syntax
bool isKhusus = false;
List<String> listDonatur = ['-Tiada Donatur-'];
List<Map<String, dynamic>> listDonaturMap = [];
String donaturSelected = '-Tiada Donatur-';
int donaturSelectedIndex = 0;
// end of the new revision syntax
Future<void> init(bool isKhusus) async {
this.isKhusus = isKhusus;
if (isKhusus) {
bentukDonasiList = ['Pemasukan'];
getDonatur();
}
}
getDonatur() async {
setBusy(true);
easyLoading.customLoading('Mengambil data...');
try {
var response = await _httpService.get('donatur');
Map<String, dynamic> data = response.data;
// log.i(data);
int jumlah = data['jumlah'];
if (jumlah > 0) {
listDonatur.clear();
for (var i = 0; i < data['data'].length; i++) {
listDonatur.add(data['data'][i]['nama_donatur']);
listDonaturMap.add(data['data'][i]);
}
// get the [id_donatur] of the first [donatur
donaturSelected = listDonatur[0];
donaturSelectedIndex = int.parse(listDonaturMap[0]['id_donatur']);
log.i(donaturSelectedIndex);
}
} catch (e) {
log.e(e);
} finally {
setBusy(false);
easyLoading.dismissLoading();
}
}
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;
}
}
void addData() async {
easyLoading.customLoading('Menambahkan data...');
log.i(donaturSelectedIndex);
try {
var formData = FormData.fromMap({
'bentuk': bentukDonasi,
'nama': namaController.text,
'jumlah': jumlahController.text,
'tanggal': tanggalController.text,
'ket': keteranganController.text,
'jenis': jenisDonasi,
'jenis_barang': jenisDonasi == 'Barang' ? jenisBarangSelected : null,
'satuan': jenisDonasi == 'Barang' ? satuanSelected : null,
'jumlah_barang':
jenisDonasi == 'Barang' ? jumlahBarangController.text : null,
'donatur': isKhusus ? donaturSelectedIndex : null,
});
var response =
// await _httpService.postWithFormData('dana_sosial', formData);
await _httpService.postWithFormData(
!isKhusus ? 'dana_sosial' : 'dana_sosial_khusus', formData);
log.i(response.data);
easyLoading.showSuccess(" Data berhasil ditambahkan");
navigationService.navigateTo(Routes.adminIndexTrackingView);
} catch (e) {
log.e(e);
snackbarService.showSnackbar(
message: 'Gagal menambahkan data',
title: 'Error',
duration: const Duration(seconds: 4),
);
} finally {
easyLoading.dismissLoading();
}
}
// my revision syntax
changeSatuan(String value) {
jenisBarangSelected = value;
switch (value) {
case 'Beras':
isJenisBarangSelected = true;
satuanSelected = 'Kg';
satuanList = satuanLiterKg;
break;
case 'Minyak':
isJenisBarangSelected = false;
satuanSelected = 'Liter';
break;
case 'Mi Instan':
isJenisBarangSelected = true;
satuanSelected = 'Bungkus';
satuanList = satuanBungkusDos;
break;
case 'Telur':
isJenisBarangSelected = true;
satuanSelected = 'Butir';
satuanList = satuanButirRak;
break;
case 'Gula':
isJenisBarangSelected = true;
satuanSelected = 'Kg';
satuanList = satuanLiterKg;
break;
case 'Susu':
isJenisBarangSelected = false;
satuanSelected = 'Liter';
break;
case 'Pakaian':
isJenisBarangSelected = false;
satuanSelected = 'Helai';
// satuanList = satuanButirRak;
break;
default:
satuanSelected = '';
}
// notifyListeners();
}
}

View File

@ -0,0 +1,95 @@
import 'package:flutter/material.dart';
import 'package:panti_asuhan/app/app.router.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
import 'package:stylish_bottom_bar/model/bar_items.dart';
import 'package:stylish_bottom_bar/stylish_bottom_bar.dart';
import '../../../app/themes/app_colors.dart';
import '../../../app/themes/app_text.dart';
import './user_index_tracking_view_model.dart';
class UserIndexTrackingView extends StatelessWidget {
const UserIndexTrackingView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return ViewModelBuilder<UserIndexTrackingViewModel>.reactive(
viewModelBuilder: () => UserIndexTrackingViewModel(),
onViewModelReady: (UserIndexTrackingViewModel model) async {
await model.init();
},
builder: (
BuildContext context,
UserIndexTrackingViewModel model,
Widget? child,
) {
return Scaffold(
appBar: AppBar(
title: Text(
model.header,
style: const TextStyle(
color: Colors.white,
fontSize: 20,
),
),
backgroundColor: mainColor,
elevation: 0,
automaticallyImplyLeading: false,
actions: [
IconButton(
onPressed: () {
model.login();
},
icon: const Icon(Icons.login, color: Colors.white),
),
],
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
child: ExtendedNavigator(
navigatorKey: StackedService.nestedNavigationKey(5),
router: UserIndexTrackingViewRouter(),
initialRoute: UserIndexTrackingViewRoutes.danaSosialAdminView,
),
),
bottomNavigationBar: StylishBottomBar(
items: [
for (var item in model.bottomNavBarList)
BottomBarItem(
icon: Icon(item['icon'],
color: model.currentIndex ==
model.bottomNavBarList.indexOf(item)
? sixthGrey
: backgroundColor),
title: Text(
item['name'],
style: regularTextStyle.copyWith(
color: model.currentIndex ==
model.bottomNavBarList.indexOf(item)
? sixthGrey
: Colors.grey,
),
),
backgroundColor:
model.currentIndex == model.bottomNavBarList.indexOf(item)
? Colors.white
: Colors.grey,
),
],
currentIndex: model.currentIndex,
hasNotch: true,
backgroundColor: mainColor,
onTap: (value) {
model.handleNavigation(value);
},
option: BubbleBarOptions(
barStyle: BubbleBarStyle.horizotnal,
bubbleFillStyle: BubbleFillStyle.fill,
opacity: 0.3),
),
);
},
);
}
}

View File

@ -0,0 +1,73 @@
import 'package:flutter/material.dart';
import 'package:panti_asuhan/app/app.router.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
import '../../../app/app.locator.dart';
import '../../../app/app.logger.dart';
class UserIndexTrackingViewModel extends IndexTrackingViewModel {
final log = getLogger('PimpinanIndexTrackingViewModel');
final _navigationService = locator<NavigationService>();
// final _dialogService = locator<DialogService>();
String header = 'Dana Sosial';
final _bottomNavBarList = [
{
'name': 'List',
'icon': Icons.people_alt_outlined,
'header': 'List Anak Panti'
},
{'name': 'Dana', 'icon': Icons.money, 'header': 'Dana Sosial'},
// {'name': 'Dana', 'icon': Icons.money, 'header': 'Dana Sosial Khusus'},
{
'name': 'Profil',
'icon': Icons.person_4_outlined,
'header': 'Profil Panti Asuhan'
},
{'name': 'V & M', 'icon': Icons.list_alt_rounded, 'header': 'Visi & Misi'},
{'name': 'Sejarah', 'icon': Icons.list_outlined, 'header': 'Sejarah'},
{
'name': 'S O',
'icon': Icons.people_alt_outlined,
'header': 'Struktur Organisasi'
},
];
List<Map<String, dynamic>> get bottomNavBarList => _bottomNavBarList;
final List<String> _views = [
UserIndexTrackingViewRoutes.dataSiswaView,
UserIndexTrackingViewRoutes.danaSosialAdminView,
// UserIndexTrackingViewRoutes.danaSosialKhususView,
UserIndexTrackingViewRoutes.profilView,
UserIndexTrackingViewRoutes.visiMisiView,
UserIndexTrackingViewRoutes.sejarahView,
UserIndexTrackingViewRoutes.strukturOrganisasiView
];
Future<void> init() async {
setIndex(1);
}
void handleNavigation(int index) {
log.d("handleNavigation: $index");
log.d("currentIndex: $currentIndex");
if (currentIndex == index) return;
setIndex(index);
header = _bottomNavBarList[index]['header'] as String;
_navigationService.navigateTo(
_views[index],
id: 5,
);
}
void login() {
_navigationService.navigateTo(
Routes.loginScreenView,
);
}
}

View File

@ -15,6 +15,12 @@ class MyTextFormField extends StatelessWidget {
this.controller,
this.maxLines = 1,
this.onEditingComplete,
this.readOnly = false,
this.onTap,
this.keyboardType = TextInputType.text,
this.initialValue,
this.enabled = true,
this.maxLength,
}) : super(key: key);
final String? labelText;
@ -27,15 +33,27 @@ class MyTextFormField extends StatelessWidget {
final TextEditingController? controller;
final int maxLines;
final VoidCallback? onEditingComplete;
final bool readOnly;
final VoidCallback? onTap;
final TextInputType keyboardType;
final String? initialValue;
final bool enabled;
final int? maxLength;
@override
Widget build(BuildContext context) {
return TextFormField(
maxLength: maxLength,
enabled: enabled,
initialValue: initialValue,
onEditingComplete: onEditingComplete,
maxLines: maxLines,
controller: controller,
focusNode: focusNode,
obscureText: obscureText ?? false,
readOnly: readOnly,
onTap: onTap,
keyboardType: keyboardType,
decoration: InputDecoration(
prefixIcon: prefixIcon,
suffixIcon: suffixIcon,

View File

@ -5,10 +5,10 @@
import FlutterMacOS
import Foundation
import location
import path_provider_foundation
import shared_preferences_foundation
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
LocationPlugin.register(with: registry.registrar(forPlugin: "LocationPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
}

View File

@ -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,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.5"
flutter_file_downloader:
dependency: "direct main"
description:
name: flutter_file_downloader
sha256: e36f1a23937c83b96003ecc48f3227acbd468a1ff26be87959cecd057222632a
url: "https://pub.dev"
source: hosted
version: "1.2.1"
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:
@ -361,7 +385,7 @@ packages:
source: hosted
version: "3.2.1"
http_parser:
dependency: transitive
dependency: "direct main"
description:
name: http_parser
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
@ -408,6 +432,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.6.3"
intl:
dependency: "direct main"
description:
name: intl
sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d"
url: "https://pub.dev"
source: hosted
version: "0.18.1"
io:
dependency: transitive
description:
@ -440,30 +472,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.1"
location:
dependency: "direct main"
description:
name: location
sha256: "9051959f6f2ccadd887b28b66e9cbbcc25b6838e37cf9e894c421ccc0ebf80b5"
url: "https://pub.dev"
source: hosted
version: "4.4.0"
location_platform_interface:
dependency: transitive
description:
name: location_platform_interface
sha256: "62eeaf1658e92e4459b727f55a3c328eccbac8ba043fa6d262ac5286ad48384c"
url: "https://pub.dev"
source: hosted
version: "2.3.0"
location_web:
dependency: transitive
description:
name: location_web
sha256: "6c08c408a040534c0269c4ff9fe17eebb5a36dea16512fbaf116b9c8bc21545b"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
logger:
dependency: transitive
description:
@ -664,6 +672,62 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.1.0"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
sha256: "0344316c947ffeb3a529eac929e1978fcd37c26be4e8468628bac399365a3ca1"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
sha256: fe8401ec5b6dcd739a0fe9588802069e608c3fdbfd3c3c93e546cf2f90438076
url: "https://pub.dev"
source: hosted
version: "2.2.0"
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_foundation
sha256: f39696b83e844923b642ce9dd4bd31736c17e697f6731a5adf445b1274cf3cd4
url: "https://pub.dev"
source: hosted
version: "2.3.2"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
sha256: "71d6806d1449b0a9d4e85e0c7a917771e672a3d5dc61149cc9fac871115018e1"
url: "https://pub.dev"
source: hosted
version: "2.3.0"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
sha256: "23b052f17a25b90ff2b61aad4cc962154da76fb62848a9ce088efe30d7c50ab1"
url: "https://pub.dev"
source: hosted
version: "2.3.0"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
sha256: "7347b194fb0bbeb4058e6a4e87ee70350b6b2b90f8ac5f8bd5b3a01548f6d33a"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
sha256: f95e6a43162bce43c9c3405f3eb6f39e5b5d11f65fab19196cf8225e2777624d
url: "https://pub.dev"
source: hosted
version: "2.3.0"
shelf:
dependency: transitive
description:
@ -813,6 +877,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:

View File

@ -42,10 +42,18 @@ dependencies:
path_provider: ^2.0.9
dio:
flutter_easyloading:
location: ^4.4.0
# location: ^4.4.0
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
intl:
shared_preferences:
# flutter_hooks:
http_parser:
flutter_file_downloader: ^1.2.1
dev_dependencies:
flutter_test:
@ -75,6 +83,7 @@ flutter:
assets:
- .env
- assets/logo.png
- assets/qrcode.png
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see