proto ready

This commit is contained in:
sewmina7@gmail.com 2023-08-21 08:24:26 +05:30
parent 6f26885c20
commit 7613adbdeb
8 changed files with 333 additions and 44 deletions

View File

@ -1,4 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>
<application <application
android:label="queue_mgr" android:label="queue_mgr"
android:name="${applicationName}" android:name="${applicationName}"
@ -30,4 +32,5 @@
android:name="flutterEmbedding" android:name="flutterEmbedding"
android:value="2" /> android:value="2" />
</application> </application>
</manifest> </manifest>

View File

@ -1,9 +1,11 @@
import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:queue_mgr/backend/DebugHelper.dart'; import 'package:queue_mgr/backend/DebugHelper.dart';
import 'package:queue_mgr/backend/Dialogs.dart';
// DateFormat dateFormat; // DateFormat dateFormat;
final String API_ENDPOINT= "https://vps.playpoolstudios.com/qms/api/"; final String API_ENDPOINT= "https://vps.playpoolstudios.com/qms/api/";
@ -16,6 +18,7 @@ class DataManager{
m_instance ??= _DataManager(); m_instance ??= _DataManager();
return m_instance!; return m_instance!;
} }
} }
@ -37,6 +40,10 @@ class _DataManager{
Debug.LogError(e); Debug.LogError(e);
} }
await ProcessServices();
}
Future<void> ProcessServices() async{
List<dynamic> _services= []; List<dynamic> _services= [];
for (var s in services) { for (var s in services) {
Map<String,dynamic> se = jsonDecode(s); Map<String,dynamic> se = jsonDecode(s);
@ -45,7 +52,19 @@ class _DataManager{
continue; continue;
} }
_services.add(s); try{
var response = (await http.post(
Uri.parse('${API_ENDPOINT}get_appointments.php'),
body: <String, String>{
'serviceId':se['id']
}));
se.putIfAbsent("appointments", () => jsonDecode(response.body.toString()));
}catch(e){
Debug.LogError(e);
}
_services.add(se);
} }
services = _services; services = _services;
} }
@ -66,11 +85,73 @@ class _DataManager{
}catch(e){ }catch(e){
Debug.LogError(e); Debug.LogError(e);
} }
await ProcessServices();
}
Future<void> DeleteService(String id) async {
try{
var response = (await http.post(
Uri.parse('${API_ENDPOINT}remove_service.php'),
body: <String, String>{
'id':id,
}));
Debug.LogResponse(response.body.toString());
services = jsonDecode(response.body.toString());
Debug.LogResponse(services);
}catch(e){
Debug.LogError(e);
}
await ProcessServices();
}
Future<void> 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: <String, String>{
'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<String> 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: <String, String>{
'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<String,dynamic> GetServiceById(String id){ Map<String,dynamic> GetServiceById(String id){
for (var service in services) { for (var service in services) {
Map<String,dynamic> s = jsonDecode(service.toString()); Map<String,dynamic> s = (service);
if(s['id']==id){ if(s['id']==id){
return s; 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{ extension DurationExtensions on Duration{
String ToHoursAndMinutes(){ String ToHoursAndMinutes(){
return "${this.inHours}:${this.inMinutes}"; int mins = this.inMinutes %60;
return "${this.inHours}:${mins}";
} }
} }

View File

@ -15,6 +15,8 @@ class Debug{
static void LogError(Object? msg){ static void LogError(Object? msg){
if(!enableLogging){return;} if(!enableLogging){return;}
if(!enableErrorLoggin) {return;} if(!enableErrorLoggin) {return;}
print(StackTrace.current);
print('\x1B[31m$msg\x1B[0m'); print('\x1B[31m$msg\x1B[0m');
} }

View File

@ -42,8 +42,7 @@ class Dialogs{
} }
static bool showing = false; static bool showing = false;
static BuildContext? context; static waiting(BuildContext context){
static waiting(){
showing=true; showing=true;
// context=navigatorKey.currentContext; // context=navigatorKey.currentContext;
if(context!=null) { if(context!=null) {
@ -68,6 +67,36 @@ class Dialogs{
); );
} }
} }
static Future<bool> 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) { static showDurationPicker(BuildContext context, String title) {
// set up the button // set up the button
@ -103,11 +132,12 @@ class Dialogs{
}, },
); );
} }
// static hide(){
// showing=false; static hide(BuildContext context){
// Navigator.of(navigatorKey.currentContext!).popUntil((route){ showing=false;
// return route.settings.name!="Progress"; Navigator.of(context).popUntil((route){
// }); return route.settings.name!="Progress";
// } });
}
} }

View File

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -38,12 +39,24 @@ class Home extends StatefulWidget {
class _HomeState extends State<Home> { class _HomeState extends State<Home> {
Timer timer = Timer(Duration(hours: 1), () { });
@override @override
void initState() { void initState() {
// TODO: implement initState // TODO: implement initState
super.initState(); super.initState();
refresh(); refresh();
timer = Timer.periodic(const Duration(seconds: 10), (timer) {
refresh();
});
}
@override
void dispose() {
// TODO: implement dispose
timer.cancel();
super.dispose();
} }
void refresh()async{ void refresh()async{
@ -66,10 +79,12 @@ class _HomeState extends State<Home> {
shrinkWrap: true, shrinkWrap: true,
itemCount:DataManager.instance().services.length, itemCount:DataManager.instance().services.length,
itemBuilder: (context,index){ itemBuilder: (context,index){
Map<String,dynamic> service = jsonDecode(DataManager.instance().services[index]); Map<String,dynamic> service = (DataManager.instance().services[index]);
return InkWell( return InkWell(
onTap: (){ 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: Card(
child: Center( child: Center(
@ -80,7 +95,7 @@ class _HomeState extends State<Home> {
children: [ children: [
Text(service['start_time']), Text(service['start_time']),
Text(service['name']), 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<Home> {
SizedBox(height: 50,), SizedBox(height: 50,),
InkWell( InkWell(
onTap: (){ onTap: (){
Navigator.of(context).push(MaterialPageRoute(builder: (context)=> NewService())); Navigator.of(context).push(MaterialPageRoute(builder: (context)=> NewService())).then((value) {setState(() {
});});
}, },
child: Card( child: Card(
child: Container( child: Container(

View File

@ -1,10 +1,11 @@
import 'package:duration_picker/duration_picker.dart'; import 'package:duration_picker/duration_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:queue_mgr/backend/DataManager.dart'; import 'package:queue_mgr/backend/DataManager.dart';
import 'package:queue_mgr/backend/Dialogs.dart';
class NewService extends StatefulWidget { class NewService extends StatefulWidget {
const NewService({Key? key}) : super(key: key); NewService({Key? key,this.id}) : super(key: key);
String? id;
@override @override
State<NewService> createState() => _NewServiceState(); State<NewService> createState() => _NewServiceState();
} }
@ -17,10 +18,31 @@ class _NewServiceState extends State<NewService> {
TimeOfDay eTime = TimeOfDay(hour: 2, minute: 2); TimeOfDay eTime = TimeOfDay(hour: 2, minute: 2);
Duration duration = Duration(hours:0, minutes: 15); Duration duration = Duration(hours:0, minutes: 15);
@override
void initState() {
// TODO: implement initState
super.initState();
bool isEdit = widget.id != null;
if(isEdit){
Map<String, dynamic> 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
bool isEdit = widget.id != null;
return Scaffold( return Scaffold(
appBar: AppBar(title: Text("New Service")), appBar: AppBar(title: Text(isEdit? "Edit Service" :"New Service")),
body: Container( body: Container(
padding: EdgeInsets.all(20), padding: EdgeInsets.all(20),
child: Column( child: Column(
@ -55,7 +77,7 @@ class _NewServiceState extends State<NewService> {
ElevatedButton( ElevatedButton(
child: Padding( child: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Text("${sTime.hour}:${sTime.minute}"), child: Text(sTime.format(context)),
), ),
onPressed: () async{ onPressed: () async{
sTime = await showTimePicker(context: context, initialTime: sTime) ?? sTime; sTime = await showTimePicker(context: context, initialTime: sTime) ?? sTime;
@ -89,7 +111,7 @@ class _NewServiceState extends State<NewService> {
ElevatedButton( ElevatedButton(
child: Padding( child: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Text("${eTime.hour}:${eTime.minute}"), child: Text(eTime.format(context)),
), ),
onPressed: () async{ onPressed: () async{
eTime = await showTimePicker(context: context, initialTime: eTime) ?? eTime; eTime = await showTimePicker(context: context, initialTime: eTime) ?? eTime;
@ -104,10 +126,10 @@ class _NewServiceState extends State<NewService> {
ListTile( ListTile(
title:Text("Session Duration"), title:Text("Session Duration"),
subtitle: ElevatedButton( subtitle: ElevatedButton(
child: Text("${duration.inHours}:${duration.inMinutes}"), child: Text("${duration.ToHoursAndMinutes()}"),
onPressed: () async{ onPressed: () async{
// duration = await showTimePicker(context: context, initialTime: duration) ?? duration; // 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(() { setState(() {
}); });
@ -116,17 +138,52 @@ class _NewServiceState extends State<NewService> {
) )
], ],
), ),
Container(margin: EdgeInsets.all(20),width:200,height:50,child: ElevatedButton(onPressed: () async{ Column(
DateTime s_date = DateTime(sDate.year, sDate.month, sDate.day, sTime.hour, sTime.minute); children: [
DateTime e_date = DateTime(eDate.year, eDate.month, eDate.day, eTime.hour, eTime.minute); Container(width:200,height:50,child: ElevatedButton(onPressed: () async{
await DataManager.instance().AddService(nameController.text, s_date, e_date,duration);
setState(() {
}); 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")))
], ],
), ),
), ),

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:queue_mgr/backend/DataManager.dart'; import 'package:queue_mgr/backend/DataManager.dart';
import 'package:queue_mgr/backend/Dialogs.dart';
class ScanningPage extends StatefulWidget { class ScanningPage extends StatefulWidget {
ScanningPage({super.key,required this.id}); ScanningPage({super.key,required this.id});
@ -65,7 +66,21 @@ class _ScanningPageState extends State<ScanningPage> {
padding: const EdgeInsets.all(18.0), padding: const EdgeInsets.all(18.0),
child: Text("Clear",style: TextStyle(fontSize: 15)), 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), padding: const EdgeInsets.all(18.0),
child: Text("Complete",style: TextStyle(fontSize: 15)), child: Text("Complete",style: TextStyle(fontSize: 15)),
)) ))

View File

@ -1,5 +1,11 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:queue_mgr/backend/DataManager.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'; import 'package:queue_mgr/scanning_page.dart';
class ServicePage extends StatefulWidget { class ServicePage extends StatefulWidget {
@ -10,13 +16,62 @@ class ServicePage extends StatefulWidget {
} }
class _ServicePageState extends State<ServicePage> { class _ServicePageState extends State<ServicePage> {
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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Map<String, dynamic> service = DataManager.instance().GetServiceById(widget.serviceId.toString()); Map<String, dynamic> service = DataManager.instance().GetServiceById(widget.serviceId.toString());
List<String> members = service['members'].toString().split(','); // List<String> members = service['members'].toString().split(',');
members.removeAt(0); // members.removeAt(0);
List<dynamic> appointments = service['appointments'];
Debug.Log(appointments);
return Scaffold( 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<String, dynamic> _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( body: SafeArea(
child: SingleChildScrollView( child: SingleChildScrollView(
child: Padding( child: Padding(
@ -66,7 +121,7 @@ class _ServicePageState extends State<ServicePage> {
Text(" Queue Length"), 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<ServicePage> {
SizedBox( SizedBox(
height: 50, height: 50,
), ),
ElevatedButton(onPressed: (){ ElevatedButton(onPressed: () async{
Navigator.of(context).push(MaterialPageRoute(builder: (context)=> ScanningPage(id: widget.serviceId))); Navigator.of(context).push(MaterialPageRoute(builder: (context)=> ScanningPage(id: widget.serviceId))).then((value){setState(() {
});});
}, child: Padding( }, child: Padding(
padding: const EdgeInsets.all(20.0), padding: const EdgeInsets.all(20.0),
child: Column( child: Column(
@ -87,7 +144,7 @@ class _ServicePageState extends State<ServicePage> {
), ),
)), )),
SizedBox(height: 50,), SizedBox(height: 50,),
(members.length >0) ? Card( (appointments.length >0) ? Card(
child: Padding( child: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Column( child: Column(
@ -102,12 +159,29 @@ class _ServicePageState extends State<ServicePage> {
mainAxisSpacing: 10, mainAxisSpacing: 10,
crossAxisSpacing: 10, crossAxisSpacing: 10,
shrinkWrap: true, shrinkWrap: true,
children: List.generate(members.length, (index){ children: List.generate(appointments.length, (index){
if(members[index].length <=0){return Container();} Map<String,dynamic> appointment = jsonDecode(appointments[index].toString());
return Container(decoration: BoxDecoration(borderRadius: BorderRadius.circular(20),color: Colors.black.withOpacity(0.2)),child: Padding( Debug.Log(appointment);
padding: const EdgeInsets.all(8.0),
child: Center(child: Text(members[index],style: TextStyle(fontSize: 18),)), 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),)),
)),
);
}), }),
), ),
], ],