struktur organisasi harus diedit

This commit is contained in:
kicap 2023-08-19 01:36:03 +08:00
parent 914e24706b
commit 4f7a8b870c
11 changed files with 650 additions and 118 deletions

8
.env
View File

@ -1,6 +1,6 @@
url = 'https://panti-asuhan.s-keytech.com/'
api_url = 'https://panti-asuhan.s-keytech.com/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 = 'http://20.20.20.25/panti_asuhan2/'
api_url = 'http://20.20.20.25/panti_asuhan2/api/'

View File

@ -2,6 +2,7 @@ import 'package:panti_asuhan/ui/views/admin_index_tracking/dana_sosial_admin/dan
import 'package:panti_asuhan/ui/views/admin_index_tracking/data_siswa/data_siswa_view.dart';
import 'package:panti_asuhan/ui/views/admin_index_tracking/edit_siswa/edit_siswa_view.dart';
import 'package:panti_asuhan/ui/views/admin_index_tracking/profil/profil_view.dart';
import 'package:panti_asuhan/ui/views/admin_index_tracking/struktur_organisasi/edit_strukrur_organisasi_dialog/edit_strukrur_organisasi_dialog_view.dart';
import 'package:panti_asuhan/ui/views/detail_dana_sosial/detail_dana_sosial_view.dart';
import 'package:panti_asuhan/ui/views/pimpinan_index_tracking/pimpinan_index_tracking/pimpinan_index_tracking_view.dart';
import 'package:panti_asuhan/ui/views/tambah_dana_sosial/tambah_dana_sosial_view.dart';
@ -64,6 +65,7 @@ import '../ui/views/user_index_tracking/user_index_tracking_view.dart';
dialogs: [
StackedDialog(classType: AddSiswaDialogView),
StackedDialog(classType: FilterDialogView),
StackedDialog(classType: EditStrukrurOrganisasiDialogView),
],
dependencies: [
LazySingleton(classType: NavigationService),

View File

@ -9,10 +9,12 @@ 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/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,
}
void setupDialogUi() {
@ -23,6 +25,9 @@ void setupDialogUi() {
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),
};
dialogService.registerCustomDialogBuilders(builders);

View File

@ -24,69 +24,74 @@ class AdminIndexTrackingView extends StatelessWidget {
AdminIndexTrackingViewModel 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.logout();
},
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,
),
),
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,
return WillPopScope(
onWillPop: () async {
return false;
},
child: Scaffold(
appBar: AppBar(
title: Text(
model.header,
style: const TextStyle(
color: Colors.white,
fontSize: 20,
),
],
currentIndex: model.currentIndex,
hasNotch: true,
backgroundColor: mainColor,
onTap: (value) {
model.handleNavigation(value);
},
option: BubbleBarOptions(
barStyle: BubbleBarStyle.horizotnal,
bubbleFillStyle: BubbleFillStyle.fill,
opacity: 0.3),
),
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,
),
),
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

@ -71,7 +71,7 @@ class AdminIndexTrackingViewModel extends IndexTrackingViewModel {
);
}
logout() {
logout(BuildContext context) {
_dialogService
.showConfirmationDialog(
title: 'Logout',
@ -82,13 +82,18 @@ class AdminIndexTrackingViewModel extends IndexTrackingViewModel {
)
.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();
// _navigationService.back();
// close dialog
Navigator.pop(context);
// Navigator.of(StackedService.navigatorKey! as BuildContext).pop();
// log.d('cancel logout');
}
});
}

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();
},
),
),
],
),
),
),
);
},
);
}
}
class TheWidget extends ViewModelWidget<EditStrukrurOrganisasiDialogViewModel> {
final TextEditingController controller;
final int index;
@override
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 == 0
? 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,144 @@
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 '../../../../../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() async {
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(formData.fields);
var response =
await _httpService.postWithFormData('edit_jabatan', formData);
log.i(response.data);
setBusy(true);
easyloading.customLoading('Edit Detail Jabatan $name');
await Future.delayed(const Duration(seconds: 1));
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

@ -8,7 +8,7 @@ class StrukturOrganisasiView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ViewModelBuilder<StrukturOrganisasiViewModel>.nonReactive(
return ViewModelBuilder<StrukturOrganisasiViewModel>.reactive(
viewModelBuilder: () => StrukturOrganisasiViewModel(),
onViewModelReady: (StrukturOrganisasiViewModel model) async {
await model.init();
@ -34,60 +34,15 @@ class StrukturOrganisasiView extends StatelessWidget {
const SizedBox(
height: 20,
),
const Text(
"Ketua",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(
height: 2,
),
Text(
model.ketua,
style: const TextStyle(
fontSize: 20,
),
),
const KetuaWidget(),
const SizedBox(
height: 20,
),
const Text(
"Sekretaris",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(
height: 2,
),
Text(
model.sekretaris,
style: const TextStyle(
fontSize: 20,
),
),
const SekretarisWidget(),
const SizedBox(
height: 20,
),
const Text(
"Bendahara",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(
height: 2,
),
Text(
model.bendahara,
style: const TextStyle(
fontSize: 20,
),
),
const BendaharaWidget(),
const SizedBox(
height: 20,
),
@ -320,3 +275,147 @@ class StrukturOrganisasiView extends StatelessWidget {
);
}
}
class BendaharaWidget extends ViewModelWidget<StrukturOrganisasiViewModel> {
const BendaharaWidget({
super.key,
});
@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: [
const Text(
"Bendahara",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
IconButton(
onPressed: () {
// model.editSekretaris();
},
icon: const Icon(Icons.edit),
)
],
),
const SizedBox(
height: 2,
),
Text(
viewModel.bendahara,
style: const TextStyle(
fontSize: 20,
),
),
],
),
);
}
}
class SekretarisWidget extends ViewModelWidget<StrukturOrganisasiViewModel> {
const SekretarisWidget({
super.key,
});
@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: [
const Text(
"Sekretaris",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(
width: 10,
),
IconButton(
onPressed: () {
// model.editSekretaris();
},
icon: const Icon(Icons.edit),
)
],
),
const SizedBox(
height: 2,
),
Text(
viewModel.sekretaris,
style: const TextStyle(
fontSize: 20,
),
),
],
),
);
}
}
class KetuaWidget extends ViewModelWidget<StrukturOrganisasiViewModel> {
const KetuaWidget({
super.key,
});
@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: [
const Text(
"Ketua",
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
const SizedBox(
width: 10,
),
IconButton(
onPressed: () {
viewModel.editData('ketua', false);
},
icon: const Icon(Icons.edit),
)
],
),
const SizedBox(
height: 2,
),
Text(
viewModel.ketua,
style: const TextStyle(
fontSize: 20,
),
),
],
),
);
}
}

View File

@ -1,6 +1,11 @@
import 'package:panti_asuhan/app/core/custom_base_view_model.dart';
import '../../../../app/app.dialogs.dart';
import '../../../../app/app.logger.dart';
class StrukturOrganisasiViewModel extends CustomBaseViewModel {
final log = getLogger('StrukturOrganisasiViewModel');
String ketua = "Dr. Andi Fitriani D, S.Ag, M.Pd";
String sekretaris = "Fitriana Buyanus, S.Si ., M.Kes";
String bendahara = "Hj. Djaliah, A.Ma";
@ -51,4 +56,16 @@ class StrukturOrganisasiViewModel extends CustomBaseViewModel {
String kelompokPutra3 = "Amin Rais";
Future<void> init() async {}
void editData(String jabatan, bool bool) {
var res = dialogService.showCustomDialog(
variant: DialogType.editStrukrurOrganisasiDialogView,
data: {
"jabatan": jabatan,
'tambahan': bool,
},
);
res;
}
}

View File

@ -377,7 +377,7 @@ packages:
source: hosted
version: "3.2.1"
http_parser:
dependency: transitive
dependency: "direct main"
description:
name: http_parser
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"

View File

@ -51,6 +51,8 @@ dependencies:
validatorless: ^1.2.3
intl:
shared_preferences:
# flutter_hooks:
http_parser:
dev_dependencies:
flutter_test: