From 7613adbdeb5ce68896dacfa4badbc1d193c071bd Mon Sep 17 00:00:00 2001 From: "sewmina7@gmail.com" Date: Mon, 21 Aug 2023 08:24:26 +0530 Subject: [PATCH] proto ready --- android/app/src/main/AndroidManifest.xml | 3 + lib/backend/DataManager.dart | 97 +++++++++++++++++++++- lib/backend/DebugHelper.dart | 2 + lib/backend/Dialogs.dart | 46 +++++++++-- lib/main.dart | 25 +++++- lib/new_service.dart | 87 ++++++++++++++++---- lib/scanning_page.dart | 17 +++- lib/service_info.dart | 100 ++++++++++++++++++++--- 8 files changed, 333 insertions(+), 44 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 872ece0..6fbdf13 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,6 @@ + + + diff --git a/lib/backend/DataManager.dart b/lib/backend/DataManager.dart index 446d25e..49d9a3a 100644 --- a/lib/backend/DataManager.dart +++ b/lib/backend/DataManager.dart @@ -1,9 +1,11 @@ +import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:intl/intl.dart'; import 'package:queue_mgr/backend/DebugHelper.dart'; +import 'package:queue_mgr/backend/Dialogs.dart'; // DateFormat dateFormat; final String API_ENDPOINT= "https://vps.playpoolstudios.com/qms/api/"; @@ -16,6 +18,7 @@ class DataManager{ m_instance ??= _DataManager(); return m_instance!; } + } @@ -37,6 +40,10 @@ class _DataManager{ Debug.LogError(e); } + await ProcessServices(); + } + + Future ProcessServices() async{ List _services= []; for (var s in services) { Map se = jsonDecode(s); @@ -45,7 +52,19 @@ class _DataManager{ continue; } - _services.add(s); + try{ + var response = (await http.post( + Uri.parse('${API_ENDPOINT}get_appointments.php'), + body: { + 'serviceId':se['id'] + })); + + se.putIfAbsent("appointments", () => jsonDecode(response.body.toString())); + }catch(e){ + Debug.LogError(e); + } + + _services.add(se); } services = _services; } @@ -66,11 +85,73 @@ class _DataManager{ }catch(e){ Debug.LogError(e); } + await ProcessServices(); + + } + + Future DeleteService(String id) async { + try{ + var response = (await http.post( + Uri.parse('${API_ENDPOINT}remove_service.php'), + body: { + 'id':id, + })); + Debug.LogResponse(response.body.toString()); + services = jsonDecode(response.body.toString()); + Debug.LogResponse(services); + }catch(e){ + Debug.LogError(e); + } + await ProcessServices(); + } + + Future EditService(String id,String name, DateTime sTime, DateTime eTime,Duration duration) async { + try{ + var response = (await http.post( + Uri.parse('${API_ENDPOINT}edit_service.php'), + body: { + 'id':id, + 'name':name, + 'stime':sTime.toString(), + 'etime':eTime.toString(), + 'duration':"${duration.ToHoursAndMinutes()}" + })); + Debug.LogResponse(response.body.toString()); + services = jsonDecode(response.body.toString()); + Debug.LogResponse(services); + }catch(e){ + Debug.LogError(e); + } + await ProcessServices(); + + } + + Future CompleteService(dynamic serviceId, dynamic tokenId) async { + String res = ""; + Debug.Log("Completing $tokenId from Service $serviceId"); + try{ + var response = (await http.post( + Uri.parse('${API_ENDPOINT}complete_token.php'), + body: { + 'service_id':serviceId.toString(), + 'user_id':tokenId.toString(), + })); + res = response.body.toString(); + Debug.LogResponse(response.body.toString()); + services = jsonDecode(response.body.toString()); + await ProcessServices(); + + Debug.LogResponse(services); + return "0"; + }catch(e){ + Debug.LogError(e); + return res; + } } Map GetServiceById(String id){ for (var service in services) { - Map s = jsonDecode(service.toString()); + Map s = (service); if(s['id']==id){ return s; @@ -80,9 +161,19 @@ class _DataManager{ } } +class Helpers{ + static Duration ParseDuration(String input){ + if(input.contains(":")){ + return Duration(hours: int.parse(input.split(":")[0]), minutes: int.parse(input.split(":")[1])); + } + + return Duration(minutes: 0); + } +} extension DurationExtensions on Duration{ String ToHoursAndMinutes(){ - return "${this.inHours}:${this.inMinutes}"; + int mins = this.inMinutes %60; + return "${this.inHours}:${mins}"; } } \ No newline at end of file diff --git a/lib/backend/DebugHelper.dart b/lib/backend/DebugHelper.dart index 89a0c61..bf6dc40 100644 --- a/lib/backend/DebugHelper.dart +++ b/lib/backend/DebugHelper.dart @@ -15,6 +15,8 @@ class Debug{ static void LogError(Object? msg){ if(!enableLogging){return;} if(!enableErrorLoggin) {return;} + print(StackTrace.current); + print('\x1B[31m$msg\x1B[0m'); } diff --git a/lib/backend/Dialogs.dart b/lib/backend/Dialogs.dart index fe7efe7..0d5ed14 100644 --- a/lib/backend/Dialogs.dart +++ b/lib/backend/Dialogs.dart @@ -42,8 +42,7 @@ class Dialogs{ } static bool showing = false; - static BuildContext? context; - static waiting(){ + static waiting(BuildContext context){ showing=true; // context=navigatorKey.currentContext; if(context!=null) { @@ -68,6 +67,36 @@ class Dialogs{ ); } } + static Future AskQuestion(BuildContext context, String title, String message) async{ + bool yes = false; + + AlertDialog alert = AlertDialog( + backgroundColor: Color(0xFF1F1F1F), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(40)), + title: Text(title,textAlign: TextAlign.center,), + content: Text(message,textAlign: TextAlign.center,), + actions: [ + TextButton(onPressed: (){ + yes=true; + Navigator.of(context).pop(); + }, child: Text("Yes")), + TextButton(onPressed: (){ + Navigator.of(context).pop(); + }, child: Text("No")), + ], + + ); + + // show the dialog + await showDialog( + context: context, + builder: (BuildContext context) { + return alert; + }, + ); + + return yes; + } static showDurationPicker(BuildContext context, String title) { // set up the button @@ -103,11 +132,12 @@ class Dialogs{ }, ); } -// static hide(){ -// showing=false; -// Navigator.of(navigatorKey.currentContext!).popUntil((route){ -// return route.settings.name!="Progress"; -// }); -// } + + static hide(BuildContext context){ + showing=false; + Navigator.of(context).popUntil((route){ + return route.settings.name!="Progress"; + }); + } } diff --git a/lib/main.dart b/lib/main.dart index 2681163..4663dd0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; @@ -38,12 +39,24 @@ class Home extends StatefulWidget { class _HomeState extends State { + + Timer timer = Timer(Duration(hours: 1), () { }); @override void initState() { // TODO: implement initState super.initState(); refresh(); + timer = Timer.periodic(const Duration(seconds: 10), (timer) { + refresh(); + }); + } + + @override + void dispose() { + // TODO: implement dispose + timer.cancel(); + super.dispose(); } void refresh()async{ @@ -66,10 +79,12 @@ class _HomeState extends State { shrinkWrap: true, itemCount:DataManager.instance().services.length, itemBuilder: (context,index){ - Map service = jsonDecode(DataManager.instance().services[index]); + Map service = (DataManager.instance().services[index]); return InkWell( onTap: (){ - Navigator.of(context).push(MaterialPageRoute(builder: (context)=> ServicePage(serviceId: service['id']))); + Navigator.of(context).push(MaterialPageRoute(builder: (context)=> ServicePage(serviceId: service['id']))).then((value){setState(() { + + });}); }, child: Card( child: Center( @@ -80,7 +95,7 @@ class _HomeState extends State { children: [ Text(service['start_time']), Text(service['name']), - Container(decoration: BoxDecoration(borderRadius: BorderRadius.circular(50),color: Colors.deepPurple), child: SizedBox(width:20,height: 20,child: Center(child: Text(service['members'].toString().split(',').length.toString()))),) + Container(decoration: BoxDecoration(borderRadius: BorderRadius.circular(50),color: Colors.deepPurple), child: SizedBox(width:20,height: 20,child: Center(child: Text((service['appointments'].length).toString()))),) ], ), ), @@ -92,7 +107,9 @@ class _HomeState extends State { SizedBox(height: 50,), InkWell( onTap: (){ - Navigator.of(context).push(MaterialPageRoute(builder: (context)=> NewService())); + Navigator.of(context).push(MaterialPageRoute(builder: (context)=> NewService())).then((value) {setState(() { + + });}); }, child: Card( child: Container( diff --git a/lib/new_service.dart b/lib/new_service.dart index a27e5bd..39cc3ea 100644 --- a/lib/new_service.dart +++ b/lib/new_service.dart @@ -1,10 +1,11 @@ import 'package:duration_picker/duration_picker.dart'; import 'package:flutter/material.dart'; import 'package:queue_mgr/backend/DataManager.dart'; +import 'package:queue_mgr/backend/Dialogs.dart'; class NewService extends StatefulWidget { - const NewService({Key? key}) : super(key: key); - + NewService({Key? key,this.id}) : super(key: key); + String? id; @override State createState() => _NewServiceState(); } @@ -17,10 +18,31 @@ class _NewServiceState extends State { TimeOfDay eTime = TimeOfDay(hour: 2, minute: 2); Duration duration = Duration(hours:0, minutes: 15); + @override + void initState() { + // TODO: implement initState + super.initState(); + bool isEdit = widget.id != null; + + if(isEdit){ + Map service = DataManager.instance().GetServiceById(widget.id.toString()); + nameController.text = service['name']; + sDate = DateTime.parse(service['start_time']); + sTime = TimeOfDay.fromDateTime(sDate); + eDate = DateTime.parse(service['end_time']); + eTime = TimeOfDay.fromDateTime(eDate); + duration = Helpers.ParseDuration(service['duration']); + setState(() { + + }); + } + } + @override Widget build(BuildContext context) { + bool isEdit = widget.id != null; return Scaffold( - appBar: AppBar(title: Text("New Service")), + appBar: AppBar(title: Text(isEdit? "Edit Service" :"New Service")), body: Container( padding: EdgeInsets.all(20), child: Column( @@ -55,7 +77,7 @@ class _NewServiceState extends State { ElevatedButton( child: Padding( padding: const EdgeInsets.all(8.0), - child: Text("${sTime.hour}:${sTime.minute}"), + child: Text(sTime.format(context)), ), onPressed: () async{ sTime = await showTimePicker(context: context, initialTime: sTime) ?? sTime; @@ -89,7 +111,7 @@ class _NewServiceState extends State { ElevatedButton( child: Padding( padding: const EdgeInsets.all(8.0), - child: Text("${eTime.hour}:${eTime.minute}"), + child: Text(eTime.format(context)), ), onPressed: () async{ eTime = await showTimePicker(context: context, initialTime: eTime) ?? eTime; @@ -104,10 +126,10 @@ class _NewServiceState extends State { ListTile( title:Text("Session Duration"), subtitle: ElevatedButton( - child: Text("${duration.inHours}:${duration.inMinutes}"), + child: Text("${duration.ToHoursAndMinutes()}"), onPressed: () async{ // duration = await showTimePicker(context: context, initialTime: duration) ?? duration; - duration = await showDurationPicker(context: context, initialTime: Duration(minutes: 15)) ?? Duration(minutes: 15); + duration = await showDurationPicker(context: context, initialTime: duration) ?? Duration(minutes: 15); setState(() { }); @@ -116,17 +138,52 @@ class _NewServiceState extends State { ) ], ), - Container(margin: EdgeInsets.all(20),width:200,height:50,child: ElevatedButton(onPressed: () async{ - DateTime s_date = DateTime(sDate.year, sDate.month, sDate.day, sTime.hour, sTime.minute); - DateTime e_date = DateTime(eDate.year, eDate.month, eDate.day, eTime.hour, eTime.minute); - await DataManager.instance().AddService(nameController.text, s_date, e_date,duration); - setState(() { + Column( + children: [ + Container(width:200,height:50,child: ElevatedButton(onPressed: () async{ - }); + Dialogs.waiting(context); + DateTime s_date = DateTime(sDate.year, sDate.month, sDate.day, sTime.hour, sTime.minute); + DateTime e_date = DateTime(eDate.year, eDate.month, eDate.day, eTime.hour, eTime.minute); + if(isEdit){ + await DataManager.instance().EditService(widget.id.toString(),nameController.text, s_date, e_date,duration); + }else{ + await DataManager.instance().AddService(nameController.text, s_date, e_date,duration); + } + setState(() { - Navigator.pop(context); + }); + Dialogs.hide(context); + + Navigator.pop(context); + + }, child: Text(isEdit ? "Save" : "Add"))), + + (isEdit) ? Container(margin: EdgeInsets.all(20),width:200,height:50,child: ElevatedButton(onPressed: () async{ + bool confirm = await Dialogs.AskQuestion(context, "CAUTION", "Are you sure to delete this service?"); + if(!confirm){return;} + + Dialogs.waiting(context); + + await DataManager.instance().DeleteService(widget.id.toString()); + + setState(() { + + }); + Dialogs.hide(context); + + Navigator.pop(context); + + }, child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.delete), + Text(" Delete"), + ], + ),style: ElevatedButton.styleFrom(backgroundColor: Colors.redAccent, foregroundColor: Colors.white),)) : Container(), + ], + ), - }, child: Text("Add"))) ], ), ), diff --git a/lib/scanning_page.dart b/lib/scanning_page.dart index e66958a..66c4403 100644 --- a/lib/scanning_page.dart +++ b/lib/scanning_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:queue_mgr/backend/DataManager.dart'; +import 'package:queue_mgr/backend/Dialogs.dart'; class ScanningPage extends StatefulWidget { ScanningPage({super.key,required this.id}); @@ -65,7 +66,21 @@ class _ScanningPageState extends State { padding: const EdgeInsets.all(18.0), child: Text("Clear",style: TextStyle(fontSize: 15)), ),), - ElevatedButton(onPressed: (){}, child: Padding( + ElevatedButton(onPressed: () async{ + Dialogs.waiting(context); + String result = await DataManager.instance().CompleteService(widget.id, tokenID); + tokenID=0; + setState(() { + + }); + Dialogs.hide(context); + + if(result == "0"){ + //success + }else{ + Dialogs.showAlertDialog(context, "Error", result); + } + }, child: Padding( padding: const EdgeInsets.all(18.0), child: Text("Complete",style: TextStyle(fontSize: 15)), )) diff --git a/lib/service_info.dart b/lib/service_info.dart index 294044b..0fda75a 100644 --- a/lib/service_info.dart +++ b/lib/service_info.dart @@ -1,5 +1,11 @@ +import 'dart:async'; +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:queue_mgr/backend/DataManager.dart'; +import 'package:queue_mgr/backend/DebugHelper.dart'; +import 'package:queue_mgr/backend/Dialogs.dart'; +import 'package:queue_mgr/new_service.dart'; import 'package:queue_mgr/scanning_page.dart'; class ServicePage extends StatefulWidget { @@ -10,13 +16,62 @@ class ServicePage extends StatefulWidget { } class _ServicePageState extends State { + + Timer timer = Timer(Duration(hours: 1), () { }); + @override + void initState() { + // TODO: implement initState + super.initState(); + timer = Timer.periodic(const Duration(seconds: 10), (timer) { + refresh(); + }); + } + + void refresh() async{ + await DataManager.instance().GetData(); + setState(() { + + }); + } + + @override + void dispose() { + // TODO: implement dispose + timer.cancel(); + Debug.Log("Cancelling timer"); + super.dispose(); + } + @override Widget build(BuildContext context) { Map service = DataManager.instance().GetServiceById(widget.serviceId.toString()); - List members = service['members'].toString().split(','); - members.removeAt(0); + // List members = service['members'].toString().split(','); + // members.removeAt(0); + List appointments = service['appointments']; + Debug.Log(appointments); return Scaffold( - appBar: AppBar(title: Text(service['name']),), + appBar: AppBar(title: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(service['name']), + InkWell( + onTap: (){ + Navigator.of(context).push(MaterialPageRoute(builder:(_)=> NewService(id: widget.serviceId))).then((value) { + Map _service = DataManager.instance().GetServiceById(widget.serviceId.toString()); + if(_service == null || _service.isEmpty){ + Navigator.of(context).pop(); + } + setState(() { + + });}); + }, + child: Padding( + padding: const EdgeInsets.all(12.0), + child: Icon(Icons.edit), + ), + ) + ], + ),), body: SafeArea( child: SingleChildScrollView( child: Padding( @@ -66,7 +121,7 @@ class _ServicePageState extends State { Text(" Queue Length"), ], ), - Text(members.length.toString(),style: TextStyle(fontSize: 25),) + Text(appointments.length.toString(),style: TextStyle(fontSize: 25),) ], ), ), @@ -74,8 +129,10 @@ class _ServicePageState extends State { SizedBox( height: 50, ), - ElevatedButton(onPressed: (){ - Navigator.of(context).push(MaterialPageRoute(builder: (context)=> ScanningPage(id: widget.serviceId))); + ElevatedButton(onPressed: () async{ + Navigator.of(context).push(MaterialPageRoute(builder: (context)=> ScanningPage(id: widget.serviceId))).then((value){setState(() { + + });}); }, child: Padding( padding: const EdgeInsets.all(20.0), child: Column( @@ -87,7 +144,7 @@ class _ServicePageState extends State { ), )), SizedBox(height: 50,), - (members.length >0) ? Card( + (appointments.length >0) ? Card( child: Padding( padding: const EdgeInsets.all(8.0), child: Column( @@ -102,12 +159,29 @@ class _ServicePageState extends State { mainAxisSpacing: 10, crossAxisSpacing: 10, shrinkWrap: true, - children: List.generate(members.length, (index){ - if(members[index].length <=0){return Container();} - return Container(decoration: BoxDecoration(borderRadius: BorderRadius.circular(20),color: Colors.black.withOpacity(0.2)),child: Padding( - padding: const EdgeInsets.all(8.0), - child: Center(child: Text(members[index],style: TextStyle(fontSize: 18),)), - )); + children: List.generate(appointments.length, (index){ + Map appointment = jsonDecode(appointments[index].toString()); + Debug.Log(appointment); + + bool past = DateTime.parse(service['start_time']).add(Helpers.ParseDuration(service['duration'])).isBefore(DateTime.now()); + return InkWell( + onTap: () async{ + bool confirm = await Dialogs.AskQuestion(context, "Complete Token ${appointment['id']}", "Press Yes to remove this token from queue"); + + if(confirm){ + Dialogs.waiting(context); + await DataManager.instance().CompleteService(service['id'],appointment['user_id']); + Dialogs.hide(context); + } + setState(() { + + }); + }, + child: Container(decoration: BoxDecoration(borderRadius: BorderRadius.circular(20),color: (past) ? Colors.red.withOpacity(0.2) :Colors.black.withOpacity(0.2)),child: Padding( + padding: const EdgeInsets.all(8.0), + child: Center(child: Text(appointment['appointment_id'],style: TextStyle(fontSize: 18),)), + )), + ); }), ), ],