modify reservation table page, add socket io client for real time data update, modify makanan list page

This commit is contained in:
kicap
2023-08-25 04:21:55 +08:00
parent 6c5bfde828
commit d96a14e062
30 changed files with 726 additions and 165 deletions

View File

@ -85,9 +85,18 @@ class MejaDetailView extends StatelessWidget {
style: regularTextStyle,
children: [
TextSpan(
text: 'Tersedia',
text: model.theBool
? (model.reservasiMejaModel == null
? 'Loading'
: model.reservasiMejaModel!.status!
.toUpperCase())
: 'Tersedia',
style: regularTextStyle.copyWith(
color: Colors.green,
color: model.theBool
? (model.reservasiMejaModel == null
? Colors.grey
: Colors.red)
: Colors.green,
fontWeight: FontWeight.bold,
),
),
@ -146,10 +155,14 @@ class MejaDetailView extends StatelessWidget {
),
),
floatingActionButton: FloatingActionButton.extended(
onPressed: () {},
onPressed: () {
if (model.theBool == false) {
model.showReservasiMeja();
}
},
label: const Text('Pesan'),
icon: const Icon(Icons.add_shopping_cart),
backgroundColor: mainColor,
backgroundColor: mainColor.withOpacity(model.theBool ? 0.5 : 1),
),
floatingActionButtonAnimator: FloatingActionButtonAnimator.scaling,
floatingActionButtonLocation:

View File

@ -1,17 +1,24 @@
import 'package:dio/dio.dart';
import 'package:reza_app/model/reservasi_meja_model.dart';
import '../../../app/app.logger.dart';
import '../../../app/core/custom_base_view_model.dart';
import '../../../model/my_model.dart';
class MejaDetailViewModel extends CustomBaseViewModel {
final log = getLogger('MejaDetailViewModel');
late String mejaId;
late String namaMeja;
late int idMeja;
String? imgAsset;
bool theBool = false;
ReservasiMejaModel? reservasiMejaModel;
Future<void> init(String mejaId) async {
log.i('MejaDetailViewModel init');
log.i('mejaId : $mejaId');
// log.i('MejaDetailViewModel init');
// log.i('mejaId : $mejaId');
this.mejaId = mejaId;
globalVar.backPressed = 'backNormal';
// seperate the number from the string
@ -27,9 +34,76 @@ class MejaDetailViewModel extends CustomBaseViewModel {
namaMeja = 'Meja';
imgAsset = 'assets/reza_meja_2.jpeg';
}
idMeja = number;
namaMeja = '$namaMeja $number';
log.i('imgAsset : $imgAsset');
// log.i('imgAsset : $imgAsset');
getData();
}
getData() async {
easyLoading.showLoading();
setBusy(true);
try {
var response = await httpService.get('table/detail/$idMeja');
log.i('response : $response');
MyModel myModel = MyModel.fromJson(response.data);
theBool = myModel.theBool!;
reservasiMejaModel =
theBool ? ReservasiMejaModel.fromJson(myModel.data) : null;
log.i('reservasiMejaModel : $reservasiMejaModel');
} catch (e) {
log.e('error : $e');
} finally {
setBusy(false);
easyLoading.dismissLoading();
}
}
showReservasiMeja() async {
await dialogService
.showDialog(
title: 'Reservasi Meja',
description: 'Apakah anda ingin reservasi meja ini?',
buttonTitle: 'Ya',
cancelTitle: 'Tidak',
)
.then((value) {
log.i('value : $value');
if (value!.confirmed) {
reservasiMeja();
log.i('confirmed');
} else {
log.i('not confirmed');
}
});
}
reservasiMeja() async {
easyLoading.customLoading('Melakukan reservasi meja...');
setBusy(true);
try {
var formData = FormData.fromMap({'id_user': 1, 'status': 'booking'});
String path = 'table/reservation/$idMeja';
await httpService.postWithFormData(path, formData);
// log.i('res : $res');
getData();
snackbarService.showSnackbar(
message:
'Reservasi meja berhasil\nSila Bayar Rp. 20 ribu jika tiba di cafe',
title: 'Berhasil',
duration: const Duration(seconds: 2),
);
} catch (e) {
log.e('error : $e');
} finally {
setBusy(false);
easyLoading.dismissLoading();
}
}
}

View File

@ -1,9 +1,14 @@
import 'package:reza_app/app/app.router.dart';
import 'package:reza_app/app/core/custom_base_view_model.dart';
import '../../../app/app.locator.dart';
import '../../../services/my_socket_io_client.dart';
class SplashScreenViewModel extends CustomBaseViewModel {
final socketIoClient = locator<MySocketIoClient>();
Future<void> init() async {
// after 2 second, navigate to login page
socketIoClient.init();
await Future.delayed(const Duration(seconds: 2));
await navigationService.navigateToLoginUserView();
}

View File

@ -62,8 +62,8 @@ class AkunUserView extends StatelessWidget {
Center(
child: ClipRRect(
borderRadius: BorderRadius.circular(150),
child: Image.network(
'https://a.cdn-hotels.com/gdcs/production0/d1513/35c1c89e-408c-4449-9abe-f109068f40c0.jpg?impolicy=fcrop&w=800&h=533&q=medium',
child: Image.asset(
'assets/nasi_goreng.jpg',
height: 150,
width: 150,
fit: BoxFit.fill,

View File

@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:reza_app/model/makanan_model.dart';
import 'package:stacked/stacked.dart';
import '../../../../../app/themes/app_colors.dart';
@ -8,8 +10,12 @@ import '../../../../widgets/my_white_container.dart';
import './detail_makanan_view_model.dart';
class DetailMakananView extends HookWidget {
const DetailMakananView({Key? key}) : super(key: key);
final MakananModel makananModel;
const DetailMakananView({
Key? key,
required this.makananModel,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final scrollController = useScrollController();
@ -21,7 +27,7 @@ class DetailMakananView extends HookWidget {
return ViewModelBuilder<DetailMakananViewModel>.reactive(
viewModelBuilder: () => DetailMakananViewModel(),
onViewModelReady: (DetailMakananViewModel model) async {
await model.init();
await model.init(makananModel);
},
builder: (
BuildContext context,
@ -127,7 +133,9 @@ class DetailMakananView extends HookWidget {
),
// bikin dummy text tentang nasi goreng
Text(
"Nasi goreng adalah makanan yang terbuat dari nasi yang digoreng dan diaduk dalam minyak goreng atau margarin, biasanya ditambah kecap manis, bawang merah, bawang putih, daging ayam, telur, dan bumbu-bumbu lainnya. Nasi goreng sering dianggap sebagai makanan nasional Indonesia. Nasi goreng dapat ditemukan di seluruh Indonesia, dari restoran pinggir jalan, warung, hingga hotel bintang lima dan restoran mewah.",
model.isBusy
? 'Loading...'
: model.makananModel!.deskripsiMakanan!,
style: regularTextStyle.copyWith(
fontSize: 13,
color: fontGrey,
@ -184,7 +192,9 @@ class DetailMakananView extends HookWidget {
height: 5,
),
Text(
"Rp. 35.000",
model.isBusy
? 'Loading...'
: 'Rp. ${int.parse(makananModel.hargaMakanan!) + 10000}',
style: boldTextStyle.copyWith(
fontSize: 16,
color: dangerColor,
@ -342,7 +352,9 @@ class SecondWidget extends ViewModelWidget<DetailMakananViewModel> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Burger King Handcrafted Burgers ",
viewModel.isBusy
? 'Loading...'
: viewModel.makananModel!.namaMakanan!,
style: regularTextStyle.copyWith(
fontSize: 17,
),
@ -351,7 +363,9 @@ class SecondWidget extends ViewModelWidget<DetailMakananViewModel> {
height: 10,
),
Text(
"Rp. 25.000",
viewModel.isBusy
? 'Loading...'
: 'Rp .${viewModel.makananModel!.hargaMakanan!}',
style: regularTextStyle.copyWith(
fontSize: 18,
color: dangerColor,
@ -405,37 +419,38 @@ class TopMenuWidget extends ViewModelWidget<DetailMakananViewModel> {
return Stack(
children: [
SizedBox(
width: double.infinity,
height: MediaQuery.of(context).size.height * 0.35,
child: Image.network(
'https://a.cdn-hotels.com/gdcs/production0/d1513/35c1c89e-408c-4449-9abe-f109068f40c0.jpg?impolicy=fcrop&w=800&h=533&q=medium',
fit: BoxFit.cover,
),
),
Positioned(
bottom: 10,
right: 10,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 5,
),
// width: 20,
height: 20,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
),
child: const Center(
child: Text(
'1 / 2',
style: TextStyle(
color: fontGrey,
fontSize: 12,
),
),
),
),
),
width: double.infinity,
height: MediaQuery.of(context).size.height * 0.35,
child: viewModel.isBusy
? const Center(child: CircularProgressIndicator())
: Image.network(
'${dotenv.env['url']}assets/makanan/${viewModel.makananModel!.imgUrl}',
fit: BoxFit.cover,
)),
// Positioned(
// bottom: 10,
// right: 10,
// child: Container(
// padding: const EdgeInsets.symmetric(
// horizontal: 5,
// ),
// // width: 20,
// height: 20,
// decoration: BoxDecoration(
// color: Colors.white,
// borderRadius: BorderRadius.circular(10),
// ),
// child: const Center(
// child: Text(
// '1 / 2',
// style: TextStyle(
// color: fontGrey,
// fontSize: 12,
// ),
// ),
// ),
// ),
// ),
],
);
}

View File

@ -1,10 +1,16 @@
import 'package:reza_app/model/makanan_model.dart';
import '../../../../../app/app.logger.dart';
import '../../../../../app/core/custom_base_view_model.dart';
class DetailMakananViewModel extends CustomBaseViewModel {
final log = getLogger('DetailMakananViewModel');
MakananModel? makananModel;
Future<void> init() async {
Future<void> init(MakananModel makananModel) async {
setBusy(true);
globalVar.backPressed = 'backNormal';
this.makananModel = makananModel;
setBusy(false);
}
}

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:reza_app/ui/widgets/my_textformfield.dart';
import 'package:reza_app/ui/widgets/my_white_container.dart';
import 'package:stacked/stacked.dart';
@ -147,66 +148,95 @@ class MakananListView extends StatelessWidget {
height: 10,
),
Expanded(
child: Center(
child: SingleChildScrollView(
child: Wrap(
spacing: 10,
runSpacing: 10,
children: [
for (var i = 0; i < 10; i++)
GestureDetector(
onTap: () => model.goToDetailMakanan(),
child: Container(
width: MediaQuery.of(context).size.width * 0.46,
color: Colors.white,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Image.network(
'https://a.cdn-hotels.com/gdcs/production0/d1513/35c1c89e-408c-4449-9abe-f109068f40c0.jpg?impolicy=fcrop&w=800&h=533&q=medium',
height: 150,
width: double.infinity,
fit: BoxFit.fill,
),
const SizedBox(
height: 5,
),
const Padding(
padding: EdgeInsets.only(
left: 5,
),
child: Text(
'Product Name',
style: TextStyle(
fontWeight: FontWeight.bold,
child: model.isBusy
? const Center(
child: CircularProgressIndicator(),
)
: (model.listMakanan.isEmpty
? const Center(
child: Text('Data Kosong'),
)
: SingleChildScrollView(
child: Wrap(
spacing: 5,
runSpacing: 10,
// alignment: WrapAlignment.spaceAround,
// crossAxisAlignment: WrapCrossAlignment.center,
children: [
for (var i = 0;
i < model.listMakanan.length;
i++)
GestureDetector(
onTap: () => model.goToDetailMakanan(
model.listMakanan[i]),
child: Padding(
padding: const EdgeInsets.only(
left: 10,
// right: 5,
),
child: Container(
width: MediaQuery.of(context)
.size
.width *
0.46,
color: Colors.white,
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Image.network(
'${dotenv.env['url']}assets/makanan/${model.listMakanan[i].imgUrl}',
height: 150,
width: double.infinity,
fit: BoxFit.fill,
),
// Image.asset(
// 'assets/nasi_goreng.jpg',
// height: 150,
// width: double.infinity,
// fit: BoxFit.fill,
// ),
const SizedBox(
height: 5,
),
Padding(
padding: const EdgeInsets.only(
left: 5,
),
child: Text(
model.listMakanan[i]
.namaMakanan!,
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(
height: 5,
),
Padding(
padding: const EdgeInsets.only(
left: 5,
),
child: Text(
model.listMakanan[i]
.deskripsiMakanan!,
style: const TextStyle(
color: Colors.grey,
),
),
),
const SizedBox(
height: 5,
),
],
),
),
),
),
const SizedBox(
height: 5,
),
const Padding(
padding: EdgeInsets.only(
left: 5,
),
child: Text(
'Rp. 100.000',
style: TextStyle(
color: Colors.grey,
),
),
),
const SizedBox(
height: 5,
),
],
),
],
),
),
],
),
),
),
)),
),
],
),

View File

@ -1,15 +1,50 @@
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/makanan_model.dart';
import '../../../../model/my_model.dart';
import '../../../../services/my_socket_io_client.dart';
class MakananListViewModel extends CustomBaseViewModel {
final log = getLogger('MakananListViewModel');
final socketIoClient = locator<MySocketIoClient>();
List<MakananModel> listMakanan = [];
Future<void> init() async {
globalVar.backPressed = 'exitApp';
getData();
socketIoClient.on('makanan_user', (data) {
log.i('data : $data');
listMakanan.clear();
getData();
// webViewController!.reload();
});
}
goToDetailMakanan() {
getData() async {
setBusy(true);
easyLoading.showLoading();
try {
var res = await httpService.get('table/makanan');
MyModel myModel = MyModel.fromJson(res.data);
if (myModel.data.length > 0) {
for (var item in myModel.data) {
listMakanan.add(MakananModel.fromJson(item));
}
}
log.i(listMakanan);
} catch (e) {
log.e(e.toString());
} finally {
easyLoading.dismissLoading();
setBusy(false);
}
}
goToDetailMakanan(MakananModel makananModel) {
log.i('goToDetailMakanan');
navigationService.navigateTo(Routes.detailMakananView);
navigationService.navigateTo(Routes.detailMakananView,
arguments: DetailMakananViewArguments(makananModel: makananModel));
}
}

View File

@ -72,8 +72,8 @@ class KeranjangSayaView extends StatelessWidget {
),
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.network(
'https://a.cdn-hotels.com/gdcs/production0/d1513/35c1c89e-408c-4449-9abe-f109068f40c0.jpg?impolicy=fcrop&w=800&h=533&q=medium',
child: Image.asset(
'assets/nasi_goreng.jpg',
height: 150,
width: double.infinity,
fit: BoxFit.fill,

View File

@ -157,8 +157,8 @@ class PesananListView extends StatelessWidget {
),
child: ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.network(
'https://a.cdn-hotels.com/gdcs/production0/d1513/35c1c89e-408c-4449-9abe-f109068f40c0.jpg?impolicy=fcrop&w=800&h=533&q=medium',
child: Image.asset(
'assets/nasi_goreng.jpg',
height: 100,
width: double.infinity,
fit: BoxFit.fill,

View File

@ -104,16 +104,14 @@ class ReservasiMejaView extends StatelessWidget {
),
),
Expanded(
// child: SizedBox(),
child: WebView(
initialUrl: dotenv.env['table_url'],
// initialUrl: 'http://172.29.85.181/parkir/user',
// initialUrl: 'https://rekam-medis.airlangga-it.com/',
javascriptMode: JavascriptMode.unrestricted,
onWebViewCreated: (WebViewController webViewController) {
// _controller.complete(webViewController);
// model.controllerCompleter.future
// .then((value) => model.webViewController = value);
// model.controllerCompleter.complete(webViewController);
model.webViewController = webViewController;
},
onProgress: (int progress) {
// model.log.i('WebView is loading (progress : $progress%)');
@ -196,19 +194,19 @@ class ReservasiMejaView extends StatelessWidget {
),
),
const SizedBox(width: 5),
const Text('Tidak Tersedia'),
const Text('Dibooking'),
const SizedBox(width: 10),
Container(
width: 20,
height: 20,
decoration: const BoxDecoration(
color: Colors.blue,
color: Colors.grey,
shape: BoxShape.circle,
),
),
const SizedBox(width: 5),
const Text('Dipesan'),
const Text('Tidak Tersedia'),
const SizedBox(width: 10),
],
),

View File

@ -1,10 +1,16 @@
import 'dart:async';
import 'package:webview_flutter/webview_flutter.dart';
import '../../../../app/app.locator.dart';
import '../../../../app/app.logger.dart';
import '../../../../app/core/custom_base_view_model.dart';
import '../../../../services/my_socket_io_client.dart';
class ReservasiMejaViewModel extends CustomBaseViewModel {
final log = getLogger('ReservasiMejaViewModel');
WebViewController? webViewController;
final socketIoClient = locator<MySocketIoClient>();
List<String> imagePaths = [
'assets/reza_gazebo.jpeg',
@ -14,5 +20,10 @@ class ReservasiMejaViewModel extends CustomBaseViewModel {
Future<void> init() async {
globalVar.backPressed = 'exitApp';
socketIoClient.on('table_admin', (data) {
log.i('data : $data');
// getData();
webViewController!.reload();
});
}
}