finish caleg, area and tim survei page

This commit is contained in:
kicap
2023-10-26 16:05:01 +08:00
parent 364bee3120
commit 85be29e7ce
24 changed files with 1100 additions and 228 deletions

View File

@ -38,20 +38,27 @@ class AdminFirstPageView extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const TopContainer(
TopContainer(
title: 'Jumlah Area',
value: '10 Area',
value: '${model.jumlahArea} Area',
icon: Icons.place_outlined,
background: warningColor,
),
const SizedBox(height: 10),
const TopContainer(
TopContainer(
title: 'Jumlah Caleg',
value: '10 Caleg',
value: '${model.jumlahCaleg} Caleg',
icon: Icons.co_present_outlined,
background: greenColor,
),
const SizedBox(height: 10),
TopContainer(
title: 'Tim Survei',
value: '${model.jumlahTimSurvei} Tim Survei',
icon: Icons.co_present_outlined,
background: orangeColor,
),
const SizedBox(height: 10),
const TopContainer(
title: 'Jumlah Pemilih',
value: '10 Pemilih',
@ -61,55 +68,109 @@ class AdminFirstPageView extends StatelessWidget {
const SizedBox(
height: 20,
),
RichText(
text: TextSpan(
text: 'Selamat Datang, ',
style: regularTextStyle,
children: [
const TextSpan(
text: 'Admin\n',
style: boldTextStyle,
),
const TextSpan(
text: 'Silahkan tambahkan data ',
style: regularTextStyle,
),
TextSpan(
text: 'Area ',
style: boldTextStyle.copyWith(
color: greenColor,
fontStyle: FontStyle.italic,
if (model.isBusy)
const Center(child: CircularProgressIndicator()),
if (!model.isBusy && model.status == true)
RichText(
text: TextSpan(
text: 'Selamat Datang, ',
style: regularTextStyle,
children: [
const TextSpan(
text: 'Admin\n',
style: boldTextStyle,
),
),
const TextSpan(
text: 'terlebih dahulu sebelum menambahkan data ',
style: regularTextStyle,
),
TextSpan(
text: 'Caleg',
style: boldTextStyle.copyWith(
color: greenColor,
fontStyle: FontStyle.italic,
const TextSpan(
text: 'Silahkan tambahkan data ',
style: regularTextStyle,
),
),
const TextSpan(
text:
'.\n\nData Pemilih akan diambil dari data yang dimasukkan oleh tim survei\n\n',
style: regularTextStyle,
),
const TextSpan(
text:
'Jika terjadi kesalahan pada data, silahkan hubungi ',
style: regularTextStyle,
),
],
TextSpan(
text: 'Area ',
style: boldTextStyle.copyWith(
color: greenColor,
fontStyle: FontStyle.italic,
),
),
const TextSpan(
text: 'terlebih dahulu sebelum menambahkan data ',
style: regularTextStyle,
),
TextSpan(
text: 'Caleg',
style: boldTextStyle.copyWith(
color: greenColor,
fontStyle: FontStyle.italic,
),
),
const TextSpan(
text:
'.\n\nData Pemilih akan diambil dari data yang dimasukkan oleh tim survei\n\n',
style: regularTextStyle,
),
const TextSpan(
text:
'Jika terjadi kesalahan pada data, silahkan hubungi ',
style: regularTextStyle,
),
],
),
),
if (!model.isBusy && model.status == false)
RichText(
text: TextSpan(
text: 'Selamat Datang, ',
style: regularTextStyle,
children: [
const TextSpan(
text: 'Admin\n',
style: boldTextStyle,
),
const TextSpan(
text: 'Terjadi ',
style: regularTextStyle,
),
TextSpan(
text: 'Error ',
style: boldTextStyle.copyWith(
color: redColor,
fontStyle: FontStyle.italic,
),
),
const TextSpan(
text: 'pada saat mengambil data\n',
style: regularTextStyle,
),
const TextSpan(
text: 'Silahkan coba lagi dengan menekan icon',
style: regularTextStyle,
),
TextSpan(
text: ' Pengaturan\n',
style: boldTextStyle.copyWith(
color: greenColor,
fontStyle: FontStyle.italic,
),
),
const TextSpan(
text: 'di pojok kanan bawah\n',
style: regularTextStyle,
),
],
),
),
),
],
),
),
),
),
// with setting icon
floatingActionButton: FloatingActionButton(
onPressed: () {
// model.gotoSetting();
},
child: const Icon(Icons.settings),
),
floatingActionButtonLocation: FloatingActionButtonLocation.miniEndTop,
);
},
);

View File

@ -1,7 +1,55 @@
import '../../../../app/app.logger.dart';
import '../../../../app/core/custom_base_view_model.dart';
import '../../../../model/area_model.dart';
import '../../../../model/caleg_model.dart';
import '../../../../model/my_response.model.dart';
import '../../../../model/tim_survei_model.dart';
class AdminFirstPageViewModel extends CustomBaseViewModel {
final log = getLogger('AdminFirstPageViewModel');
// variabel
int jumlahArea = 0;
int jumlahCaleg = 0;
int jumlahTimSurvei = 0;
bool status = false;
Future<void> init() async {
globalVar.backPressed = 'exitApp';
await getDataArea();
}
getDataArea() async {
log.i('getData');
setBusy(true);
globalVar.backPressed = 'cantBack';
try {
var response = await httpService.get('area');
log.i(response.data);
MyResponseModel myResponseModel = MyResponseModel.fromJson(response.data);
AreaListModel areaListModel =
AreaListModel.fromJson(myResponseModel.data);
jumlahArea = areaListModel.jumlah!;
response = await httpService.get('caleg');
log.i(response.data);
myResponseModel = MyResponseModel.fromJson(response.data);
CalegListModel calegListModel =
CalegListModel.fromJson(myResponseModel.data);
jumlahCaleg = calegListModel.jumlah!;
response = await httpService.get('survei');
myResponseModel = MyResponseModel.fromJson(response.data);
TimSurveiListModel timSurveiListModel =
TimSurveiListModel.fromJson(myResponseModel.data);
jumlahTimSurvei = timSurveiListModel.jumlah!;
status = true;
} catch (e) {
status = false;
log.e(e);
} finally {
globalVar.backPressed = 'exitApp';
setBusy(false);
}
}
}

View File

@ -92,9 +92,13 @@ class HalamanAreaView extends StatelessWidget {
child: Column(
children: [
if (model.jumlahArea == 0)
const Center(
child: Text(
'Belum ada area diinput')),
Center(
child: model.status == true
? const Text(
'Belum ada area diinput')
: const Text(
'Gagal mengambil data'),
),
if (model.jumlahArea > 0)
for (var i = 0;
i < model.jumlahArea;

View File

@ -13,6 +13,7 @@ class HalamanAreaViewModel extends CustomBaseViewModel {
// variabel
List<AreaModel> listAreaModel = [];
int jumlahArea = 0;
bool status = false;
// add area form
final formKey = GlobalKey<FormState>();
@ -38,7 +39,9 @@ class HalamanAreaViewModel extends CustomBaseViewModel {
log.i('listAreaModel: $listAreaModel');
log.i('jumlahArea: $jumlahArea');
status = true;
} catch (e) {
status = false;
log.e(e);
} finally {
globalVar.backPressed = 'exitApp';

View File

@ -1,8 +1,8 @@
import 'package:cek_suara/app/themes/app_text.dart';
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import '../../../../app/themes/app_colors.dart';
import '../../../../app/themes/app_text.dart';
import '../../../widgets/top_container.dart';
import './halaman_caleg_view_model.dart';
@ -71,40 +71,58 @@ class HalamanCalegView extends StatelessWidget {
),
if (!model.isBusy &&
model.listCalegModel.isNotEmpty)
Expanded(
child: SingleChildScrollView(
child: Column(
children: [
for (var i = 0;
i < model.jumlahCaleg;
i++)
Card(
child: ListTile(
leading: Text('${i + 1}'),
title: Text(
model
.listCalegModel[i].namaCaleg!,
style: boldTextStyle,
),
subtitle: Text(
'No. Urut: ${model.listCalegModel[i].nomorUrutCaleg!}',
style: italicTextStyle,
),
trailing: IconButton(
icon: const Icon(Icons.edit,
color: mainColor),
onPressed: () {},
),
SingleChildScrollView(
child: Column(
children: [
for (var i = 0; i < model.jumlahCaleg; i++)
Card(
child: ListTile(
leading: Text('${i + 1}'),
title: Text(
model.listCalegModel[i].namaCaleg!,
style: boldTextStyle,
),
subtitle: Text(
'No. Urut: ${model.listCalegModel[i].nomorUrutCaleg!}',
style: italicTextStyle,
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(
Icons.info_outline,
color: mainColor,
),
onPressed: () {
model.showDetailCaleg(
model.listCalegModel[i]);
},
),
IconButton(
icon: const Icon(
// trash
Icons.delete_outline,
color: dangerColor,
),
onPressed: () {
model.deleteCaleg(
model.listCalegModel[i]);
},
),
],
),
),
],
),
),
],
),
),
if (!model.isBusy && model.listCalegModel.isEmpty)
const Center(
Center(
child: Text(
'Tidak ada data caleg diinput sebelumnya',
model.status == true
? 'Tidak ada data caleg diinput sebelumnya'
: 'Gagal mengambil data caleg',
style: italicTextStyle,
textAlign: TextAlign.center,
),

View File

@ -1,3 +1,5 @@
import 'package:cek_suara/app/themes/app_colors.dart';
import '../../../../app/app.dialogs.dart';
import '../../../../app/app.logger.dart';
import '../../../../app/core/custom_base_view_model.dart';
@ -10,6 +12,7 @@ class HalamanCalegViewModel extends CustomBaseViewModel {
// variabel
List<CalegModel> listCalegModel = [];
int jumlahCaleg = 0;
bool status = false;
Future<void> init() async {
globalVar.backPressed = 'exitApp';
@ -31,8 +34,10 @@ class HalamanCalegViewModel extends CustomBaseViewModel {
log.i('listCalegModel: $listCalegModel');
log.i('jumlahCaleg: $jumlahCaleg');
status = true;
} catch (e) {
log.e(e);
status = false;
} finally {
globalVar.backPressed = 'exitApp';
setBusy(false);
@ -43,11 +48,64 @@ class HalamanCalegViewModel extends CustomBaseViewModel {
// log.i('addCaleg');
var res = await dialogService.showCustomDialog(
variant: DialogType.tambahEditCalegView,
data: {
'title': 'Tambah Caleg',
},
title: 'Tambah Caleg',
);
log.i(res?.confirmed);
if (res!.confirmed) {
snackbarService.showSnackbar(
message: 'Caleg berhasil ditambahkan',
title: 'Berhasil',
duration: const Duration(seconds: 3),
);
await getData();
}
}
deleteCaleg(CalegModel calegModel) {
dialogService
.showDialog(
title: 'Hapus Caleg',
description:
'Apakah anda yakin ingin menghapus caleg ${calegModel.namaCaleg} ?',
buttonTitle: 'Hapus',
cancelTitle: 'Batal',
buttonTitleColor: dangerColor,
cancelTitleColor: mainColor,
)
.then((value) async {
if (value!.confirmed) {
setBusy(true);
easyLoading.customLoading('Menghapus Caleg...');
try {
var response =
await httpService.delete('caleg/${calegModel.idCaleg}');
log.i(response.data);
await getData();
snackbarService.showSnackbar(
message: 'Caleg berhasil dihapus',
title: 'Berhasil',
duration: const Duration(seconds: 3),
);
} catch (e) {
log.e(e);
} finally {
easyLoading.dismissLoading();
setBusy(false);
}
}
});
}
showDetailCaleg(CalegModel calegModel) async {
var res = await dialogService.showCustomDialog(
variant: DialogType.tambahEditCalegView,
title: 'Detail Caleg',
data: calegModel,
);
// log.i('res ini: ${res!.confirmed}');
if (res!.confirmed) {
await deleteCaleg(calegModel);
}
}
}

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
import 'package:validatorless/validatorless.dart';
@ -10,13 +11,13 @@ import '../../../../widgets/my_textformfield.dart';
import './tambah_edit_caleg_view_model.dart';
class TambahEditCalegView extends StatelessWidget {
final DialogRequest? request;
final Function(DialogResponse)? completer;
final DialogRequest request;
final Function(DialogResponse) completer;
const TambahEditCalegView({
Key? key,
this.request,
this.completer,
required this.request,
required this.completer,
}) : super(key: key);
@override
@ -24,7 +25,7 @@ class TambahEditCalegView extends StatelessWidget {
return ViewModelBuilder<TambahEditCalegViewModel>.reactive(
viewModelBuilder: () => TambahEditCalegViewModel(),
onViewModelReady: (TambahEditCalegViewModel model) async {
await model.init();
await model.init(request.data);
},
builder: (
BuildContext context,
@ -45,73 +46,113 @@ class TambahEditCalegView extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
children: [
Text(
request?.data['title'] ?? '',
request.title ?? '',
),
const SizedBox(height: 10),
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,
if (model.calegModel == null)
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: warningColor,
child: IconButton(
onPressed: () {
model.addImage();
},
icon: const Icon(
Icons.add,
color: fontColor,
size: 15,
)),
),
Positioned(
bottom: 0,
right: 0,
child: CircleAvatar(
radius: 15,
backgroundColor: warningColor,
child: IconButton(
onPressed: () {
model.addImage();
},
icon: const Icon(
Icons.add,
color: fontColor,
size: 15,
)),
),
),
],
),
if (model.calegModel != null)
CircleAvatar(
radius: 50,
backgroundColor: fontParagraphColor,
child: ClipRRect(
borderRadius: BorderRadius.circular(50),
child: Image.network(
'${dotenv.env['api_url']}assets/caleg/${model.calegModel!.idCaleg}/${model.calegModel!.foto}',
width: 100,
height: 100,
fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return const Icon(
Icons.person,
size: 50,
color: greyBlueColor,
);
},
),
),
],
),
),
const SizedBox(height: 10),
MyTextFormField(
hintText: 'Masukan Nama Caleg',
labelText: 'Nama Caleg',
controller: model.namaController,
readOnly: model.calegModel != null,
validator: Validatorless.required(
'Nama Caleg tidak boleh kosong'),
),
const SizedBox(height: 10),
MyTextFormField(
hintText: 'Area',
labelText: 'Pencarian Area',
controller: model.cariAreaController,
suffixIcon: GestureDetector(
onTap: () {
// remove keyboard focus
FocusScope.of(context).unfocus();
model.cariArea();
},
child: const Icon(Icons.search),
hintText: 'Masukan Nomor Urut',
labelText: 'Nomor Urut',
controller: model.nomorUrutController,
readOnly: model.calegModel != null,
keyboardType: TextInputType.number,
validator: Validatorless.multiple(
// numbers only
[
Validatorless.required(
'Nomor Urut tidak boleh kosong'),
Validatorless.number('Nomor Urut harus berupa angka'),
],
),
// controller: model.partaiController,
),
if (model.calegModel == null) const SizedBox(height: 10),
if (model.calegModel == null)
MyTextFormField(
hintText: 'Area',
labelText: 'Pencarian Area',
controller: model.cariAreaController,
suffixIcon: GestureDetector(
onTap: () {
// remove keyboard focus
FocusScope.of(context).unfocus();
model.cariArea();
},
child: const Icon(Icons.search),
),
// controller: model.partaiController,
),
const SizedBox(height: 10),
SizedBox(
height: MediaQuery.of(context).size.height * 0.3,
@ -137,6 +178,7 @@ class TambahEditCalegView extends StatelessWidget {
value: model.listAreaId.contains(
model.listAreaModel[index].idArea!),
onChanged: (value) {
if (model.calegModel != null) return;
model.tambahHapusArea(
model.listAreaModel[index].idArea!,
);
@ -175,35 +217,111 @@ class TambahEditCalegView extends StatelessWidget {
),
),
const SizedBox(height: 10),
SizedBox(
width: 250,
child: MyButton(
text: 'Tambah Caleg',
onPressed: () async {
if (model.listAreaId.isEmpty) {
model.snackbarService.showSnackbar(
message:
'Pilih Area Calon Legislatif terlebih dahulu',
duration: const Duration(seconds: 2),
);
return;
}
if (model.calegModel == null)
SizedBox(
width: 250,
child: MyButton(
text: 'Tambah Caleg',
onPressed: () async {
if (model.listAreaId.isEmpty) {
model.snackbarService.showSnackbar(
message:
'Pilih Area Calon Legislatif terlebih dahulu',
duration: const Duration(seconds: 2),
);
return;
}
if (model.imageBytes == null) {
model.snackbarService.showSnackbar(
message:
'Pilih Foto Calon Legislatif terlebih dahulu',
duration: const Duration(seconds: 2),
);
return;
}
if (model.imageBytes == null) {
model.snackbarService.showSnackbar(
message:
'Pilih Foto Calon Legislatif terlebih dahulu',
duration: const Duration(seconds: 2),
);
return;
}
if (model.formKey.currentState!.validate()) {
model.addCaleg();
}
},
if (model.formKey.currentState!.validate()) {
// remove keyboard focus
FocusScope.of(context).unfocus();
model.dialogService
.showDialog(
title: 'Tambah Caleg',
description:
'Apakah anda yakin ingin menambah caleg ${model.namaController.text} ?',
buttonTitle: 'Ya',
cancelTitle: 'Batal',
)
.then((value) async {
if (value!.confirmed) {
bool res = await model.addCaleg();
model.log
.i('ini di res bagian dalamnya: $res');
if (res) {
// ignore: use_build_context_synchronously
// Navigator.of(context).pop();
completer(DialogResponse(confirmed: true));
}
// if (res.confirmed) {
// await model.addCaleg(completer!);
// completer!(DialogResponse(confirmed: true));
}
});
// bool res = await model.addCaleg();
// model.log.i('ini di res bagian dalamnya: $res');
// if (res) {
// // ignore: use_build_context_synchronously
// Navigator.of(context).pop();
// completer(DialogResponse(confirmed: true));
// }
// if (res.confirmed) {
// await model.addCaleg(completer!);
// completer!(DialogResponse(confirmed: true));
}
},
),
),
),
if (model.calegModel != null)
Row(
mainAxisSize: MainAxisSize.min,
children: [
// create rounde icon with one is delete and one is info
Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: mainColor.withOpacity(0.5),
borderRadius: BorderRadius.circular(50),
),
child: IconButton(
onPressed: () {},
icon: const Icon(
Icons.info,
color: Colors.white,
),
),
),
const SizedBox(width: 20),
Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: dangerColor,
borderRadius: BorderRadius.circular(50),
),
child: IconButton(
onPressed: () async {
completer(DialogResponse(confirmed: true));
},
icon: const Icon(
Icons.delete,
color: Colors.white,
),
),
),
],
)
],
),
),

View File

@ -11,6 +11,7 @@ import '../../../../../app/core/custom_base_view_model.dart';
import 'package:image_picker/image_picker.dart';
import '../../../../../model/area_model.dart';
import '../../../../../model/caleg_model.dart';
import '../../../../../model/my_response.model.dart';
class TambahEditCalegViewModel extends CustomBaseViewModel {
@ -24,6 +25,7 @@ class TambahEditCalegViewModel extends CustomBaseViewModel {
final formKey = GlobalKey<FormState>();
TextEditingController namaController = TextEditingController();
TextEditingController cariAreaController = TextEditingController();
TextEditingController nomorUrutController = TextEditingController();
List<int> listAreaId = [];
// image picker
@ -32,9 +34,49 @@ class TambahEditCalegViewModel extends CustomBaseViewModel {
XFile? imageFile;
Uint8List? imageBytes;
Future<void> init() async {
CalegModel? calegModel;
Future<void> init(data) async {
globalVar.backPressed = 'exitApp';
await getData();
calegModel = data;
if (calegModel != null) {
namaController.text = calegModel!.namaCaleg!;
nomorUrutController.text = calegModel!.nomorUrutCaleg!.toString();
getAreaList(calegModel!.idCaleg!);
// listAreaId = calegModel!.area!.map((e) => e.id!).toList();
}
log.i(data);
}
getAreaList(int idCaleg) async {
log.i('getAreaList');
setBusy(true);
globalVar.backPressed = 'cantBack';
try {
var response = await httpService.get('caleg/relasi_area/$idCaleg');
log.i(response.data);
MyResponseModel myResponseModel = MyResponseModel.fromJson(response.data);
var data = myResponseModel.data['area'];
for (var item in data) {
listAreaId.add(item['id_area']);
}
listAreaModel = [];
for (var item in allListAreaModel) {
if (listAreaId.contains(item.idArea)) {
listAreaModel.add(item);
}
}
log.i('listAreaModel: $listAreaModel');
// log.i('jumlahArea: $jumlahArea');
} catch (e) {
log.e(e);
} finally {
globalVar.backPressed = 'exitApp';
setBusy(false);
}
}
getData() async {
@ -105,61 +147,47 @@ class TambahEditCalegViewModel extends CustomBaseViewModel {
notifyListeners();
}
addCaleg() async {
dialogService
.showDialog(
title: 'Tambah Caleg',
description:
'Apakah anda yakin ingin menambahkan Caleg ${namaController.text}?',
buttonTitle: 'Ya',
cancelTitle: 'Tidak',
)
.then((value) async {
if (value!.confirmed) {
log.i('addCaleg');
setBusy(true);
globalVar.backPressed = 'cantBack';
easyLoading.customLoading('Tambah Caleg..');
Future<bool> addCaleg() async {
log.i('addCaleg');
setBusy(true);
globalVar.backPressed = 'cantBack';
easyLoading.customLoading('Tambah Caleg..');
var formData = FormData.fromMap({
'nama': namaController.text,
'area': const JsonEncoder().convert(listAreaId),
'foto': await MultipartFile.fromFile(
imageFile!.path,
filename: imageFile!.name,
contentType: MediaType('image', 'jpeg'),
)
});
try {
var response = await httpService.postWithFormData('caleg', formData);
log.i(response.data);
await getData();
navigationService.back();
// navigationService.back();
snackbarService.showSnackbar(
message: 'Berhasil menambahkan Caleg',
duration: const Duration(seconds: 2),
);
// navigationService.popRepeated(2);
} catch (e) {
log.e(e);
// navigationService.back();
snackbarService.showSnackbar(
message: 'Gagal menambahkan Caleg, ${e.toString()}',
duration: const Duration(seconds: 2),
);
} finally {
easyLoading.dismissLoading();
globalVar.backPressed = 'exitApp';
setBusy(false);
// navigationService.back();
// Navigator.of(context!).pop();
// remove all dialog
}
}
var formData = FormData.fromMap({
'nama': namaController.text,
'nomor_urut': nomorUrutController.text,
'area': const JsonEncoder().convert(listAreaId),
'foto': await MultipartFile.fromFile(
imageFile!.path,
filename: imageFile!.name,
contentType: MediaType('image', 'jpeg'),
)
});
try {
var response = await httpService.postWithFormData('caleg', formData);
log.i(response.data);
// await getData();
// navigationService.back();
// navigationService.back();
// snackbarService.showSnackbar(
// message: 'Berhasil menambahkan Caleg',
// duration: const Duration(seconds: 2),
// );
// completer(DialogResponse(confirmed: true));
return true;
// navigationService.popRepeated(2);
} catch (e) {
log.e(e);
return false;
} finally {
easyLoading.dismissLoading();
globalVar.backPressed = 'exitApp';
setBusy(false);
// navigationService.back();
// Navigator.of(context!).pop();
// remove all dialog
}
}
}

View File

@ -1,3 +1,4 @@
import 'package:cek_suara/ui/views/admin_index_tracking/admin_first_page/admin_first_page_view.dart';
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
@ -28,11 +29,7 @@ class HalamanPengaturanView extends StatelessWidget {
}
return false;
},
child: const Center(
child: Text(
'HalamanPengaturanView',
),
),
child: const AdminFirstPageView(),
),
);
},

View File

@ -0,0 +1,154 @@
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 '../../../../widgets/my_button.dart';
import '../../../../widgets/my_textformfield.dart';
import './tambah_detail_tim_survei_view_model.dart';
class TambahDetailTimSurveiView extends StatelessWidget {
final DialogRequest request;
final Function(DialogResponse) completer;
const TambahDetailTimSurveiView({
Key? key,
required this.request,
required this.completer,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return ViewModelBuilder<TambahDetailTimSurveiViewModel>.reactive(
viewModelBuilder: () => TambahDetailTimSurveiViewModel(),
onViewModelReady: (TambahDetailTimSurveiViewModel model) async {
await model.init(request.data);
},
builder: (
BuildContext context,
TambahDetailTimSurveiViewModel model,
Widget? child,
) {
return Dialog(
child: Container(
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: backgroundColor,
),
child: SingleChildScrollView(
child: Form(
key: model.formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
request.title ?? '',
),
const SizedBox(height: 15),
MyTextFormField(
hintText: 'NIK Tim Survei',
labelText: 'NIK Tim Survei',
keyboardType: TextInputType.number,
controller: model.nikController,
readOnly: model.timSurveiModel != null,
maxLength: 16,
validator: Validatorless.multiple(
[
Validatorless.required(
'NIK Tim Survei tidak boleh kosong'),
Validatorless.min(
16, 'NIK Tim Survei harus 16 digit'),
Validatorless.number(
'NIK Tim Survei harus berupa angka')
],
),
),
const SizedBox(height: 15),
MyTextFormField(
hintText: 'Nama Tim Survei',
labelText: 'Nama Tim Survei',
readOnly: model.timSurveiModel != null,
controller: model.namaController,
validator: Validatorless.required(
'Nama Tim Survei tidak boleh kosong'),
),
const SizedBox(height: 20),
if (model.timSurveiModel == null)
SizedBox(
width: 200,
child: MyButton(
text: 'Tambah',
onPressed: () {
if (model.formKey.currentState!.validate()) {
model.dialogService
.showConfirmationDialog(
title: 'Tambah Tim Survei',
description:
'Apakah anda yakin ingin menambahkan Tim Survei ${model.namaController.text} ?',
confirmationTitle: 'Ya',
cancelTitle: 'Tidak',
)
.then((value) async {
if (value!.confirmed) {
bool res = await model.tambahTimSurvei();
if (res) {
completer(DialogResponse(confirmed: true));
}
}
});
}
},
),
),
if (model.timSurveiModel != null)
Row(
mainAxisSize: MainAxisSize.min,
children: [
// create rounde icon with one is delete and one is info
Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: mainColor.withOpacity(0.5),
borderRadius: BorderRadius.circular(50),
),
child: IconButton(
onPressed: () {},
icon: const Icon(
Icons.list_alt_outlined,
color: Colors.white,
),
),
),
const SizedBox(width: 20),
Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: dangerColor,
borderRadius: BorderRadius.circular(50),
),
child: IconButton(
onPressed: () async {
completer(DialogResponse(confirmed: true));
},
icon: const Icon(
Icons.delete,
color: Colors.white,
),
),
),
],
)
],
),
),
),
),
);
},
);
}
}

View File

@ -0,0 +1,54 @@
import 'package:cek_suara/model/tim_survei_model.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import '../../../../../app/app.logger.dart';
import '../../../../../app/core/custom_base_view_model.dart';
class TambahDetailTimSurveiViewModel extends CustomBaseViewModel {
final log = getLogger('TambahDetailTimSurveiViewModel');
// form variabel
final formKey = GlobalKey<FormState>();
TextEditingController nikController = TextEditingController();
TextEditingController namaController = TextEditingController();
TimSurveiModel? timSurveiModel;
Future<void> init(data) async {
globalVar.backPressed = 'exitApp';
timSurveiModel = data;
if (timSurveiModel != null) {
nikController.text = timSurveiModel!.nik!;
namaController.text = timSurveiModel!.nama!;
}
}
Future<bool> tambahTimSurvei() async {
globalVar.backPressed = 'cantBack';
setBusy(true);
easyLoading.customLoading('Menambahkan Tim Survei...');
try {
var formData = FormData.fromMap({
'nik': nikController.text,
'nama': namaController.text,
});
var response = await httpService.postWithFormData('survei', formData);
log.i(response.data);
return true;
} catch (e) {
// log.e(e);
// snackbarService.showSnackbar(
// message: 'Gagal menambahkan Tim Survei\n${e.res}',
// title: 'Error',
// duration: const Duration(seconds: 2),
// );
return false;
} finally {
globalVar.backPressed = 'exitApp';
easyLoading.dismissLoading();
setBusy(false);
}
}
}

View File

@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:stacked/stacked.dart';
import '../../../../app/themes/app_colors.dart';
import '../../../widgets/top_container.dart';
import './tim_survei_view_model.dart';
class TimSurveiView extends StatelessWidget {
@ -28,12 +30,122 @@ class TimSurveiView extends StatelessWidget {
}
return false;
},
child: const Center(
child: Text(
'TimSurveiView',
child: SafeArea(
child: Container(
height: MediaQuery.of(context).size.height,
padding: const EdgeInsets.all(20),
child: Column(
children: [
TopContainer(
title: 'Tim\nSurvei',
value: '${model.jumlahTimSurvei} Orang',
icon: Icons.people_alt_outlined,
background: orangeColor,
),
const SizedBox(height: 15),
Expanded(
child: Container(
alignment: model.isBusy
? Alignment.center
: (model.listTimSurveiModel.isNotEmpty
? null
: Alignment.center),
width: double.infinity,
height: double.infinity,
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(10),
color: warningColor,
),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (model.isBusy)
const Center(
child: LinearProgressIndicator(
minHeight: 5,
color: mainColor,
),
),
if (!model.isBusy &&
model.listTimSurveiModel.isNotEmpty)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// create 10 list of survei person using card
for (int i = 0;
i < model.jumlahTimSurvei;
i++)
Card(
child: ListTile(
leading: Text('${i + 1}'),
title: Text(
model.listTimSurveiModel[i].nama!,
),
subtitle: Text(
model.listTimSurveiModel[i].nik!,
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
// one info and one delete button
IconButton(
onPressed: () {
model.showDetailTimSurvei(
model.listTimSurveiModel[
i]);
},
icon: const Icon(
Icons.info_outline,
color: mainColor,
),
),
IconButton(
onPressed: () {
model.deleteTimSurvei(model
.listTimSurveiModel[i]);
},
icon: const Icon(
Icons.delete_outline,
color: Colors.red,
),
),
],
)),
),
],
),
// if listTimSurveiModel is empty
if (!model.isBusy &&
model.listTimSurveiModel.isEmpty)
Center(
child: Text(
model.status == true
? 'Data Tim Survei Kosong\n'
'Silahkan Tambahkan Tim Survei Baru'
: 'Gagal Mengambil Data Tim Survei',
textAlign: TextAlign.center,
),
),
],
),
),
),
)
],
),
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
model.addTimSurvei();
},
child: const Icon(Icons.add),
),
);
},
);

View File

@ -1,7 +1,96 @@
import '../../../../app/app.dialogs.dart';
import '../../../../app/app.logger.dart';
import '../../../../app/core/custom_base_view_model.dart';
import '../../../../app/themes/app_colors.dart';
import '../../../../model/my_response.model.dart';
import '../../../../model/tim_survei_model.dart';
class TimSurveiViewModel extends CustomBaseViewModel {
final log = getLogger('TimSurveiViewModel');
// variabel
List<TimSurveiModel> listTimSurveiModel = [];
int jumlahTimSurvei = 0;
bool status = false;
Future<void> init() async {
globalVar.backPressed = 'exitApp';
await getData();
}
getData() async {
setBusy(true);
// easyLoading.showLoading();
// globalVar.backPressed = 'cantBack';
try {
var response = await httpService.get('survei');
MyResponseModel myResponseModel = MyResponseModel.fromJson(response.data);
TimSurveiListModel timSurveiListModel =
TimSurveiListModel.fromJson(myResponseModel.data);
listTimSurveiModel = timSurveiListModel.survei!;
jumlahTimSurvei = timSurveiListModel.jumlah!;
log.i('listTimSurveiModel: $listTimSurveiModel');
log.i('jumlahTimSurvei: $jumlahTimSurvei');
status = true;
} catch (e) {
log.e(e.toString());
status = false;
} finally {
setBusy(false);
// globalVar.backPressed = 'exitApp';
// easyLoading.dismissLoading();
}
}
addTimSurvei() async {
var res = await dialogService.showCustomDialog(
variant: DialogType.tambahDetailTimSurveiView,
title: 'Tambah Tim Survei',
);
if (res!.confirmed) {
await getData();
snackbarService.showSnackbar(
message: 'Berhasil menambahkan Tim Survei\nPassword default: 12345678',
title: 'Sukses',
duration: const Duration(seconds: 2),
);
}
}
deleteTimSurvei(TimSurveiModel listTimSurveiModel) async {
dialogService
.showDialog(
title: 'Hapus Tim Survei',
description:
'Apakah anda yakin ingin menghapus ${listTimSurveiModel.nama} ?',
buttonTitle: 'Hapus',
cancelTitle: 'Batal',
buttonTitleColor: dangerColor,
cancelTitleColor: mainColor,
)
.then((value) async {
if (value!.confirmed) {
await httpService.delete('survei/${listTimSurveiModel.nik}');
await getData();
snackbarService.showSnackbar(
message: 'Berhasil menghapus ${listTimSurveiModel.nama}',
title: 'Sukses',
duration: const Duration(seconds: 2),
);
}
});
}
showDetailTimSurvei(TimSurveiModel listTimSurveiModel) async {
var res = await dialogService.showCustomDialog(
variant: DialogType.tambahDetailTimSurveiView,
title: 'Detail Tim Survei',
data: listTimSurveiModel,
);
if (res!.confirmed) {
deleteTimSurvei(listTimSurveiModel);
}
}
}

View File

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