From da15e3cda621bdfab2a4caa7a23ea213ce37577b Mon Sep 17 00:00:00 2001 From: "sewmina7@gmail.com" Date: Wed, 9 Aug 2023 22:48:53 +0530 Subject: [PATCH] kinda done --- lib/CustomWidgets.dart | 25 ++++ lib/backend/DataManager.dart | 104 ++++++++++++++++- lib/backend/Dialogs.dart | 31 +++++ lib/home.dart | 217 ++++++++++++++++++++++++++++------- lib/login.dart | 124 +++++++++++++++----- pubspec.lock | 8 ++ pubspec.yaml | 1 + 7 files changed, 439 insertions(+), 71 deletions(-) create mode 100644 lib/CustomWidgets.dart diff --git a/lib/CustomWidgets.dart b/lib/CustomWidgets.dart new file mode 100644 index 0000000..9375521 --- /dev/null +++ b/lib/CustomWidgets.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; + +class GradientText extends StatelessWidget { + const GradientText({ + super.key, + required this.text, + required this.gradient, + this.style, + }); + + final String text; + final TextStyle? style; + final Gradient gradient; + + @override + Widget build(BuildContext context) { + return ShaderMask( + blendMode: BlendMode.srcIn, + shaderCallback: (bounds) => gradient.createShader( + Rect.fromLTWH(0, 0, bounds.width, bounds.height), + ), + child: Text(text, style: style), + ); + } +} \ No newline at end of file diff --git a/lib/backend/DataManager.dart b/lib/backend/DataManager.dart index df75015..f336054 100644 --- a/lib/backend/DataManager.dart +++ b/lib/backend/DataManager.dart @@ -3,11 +3,13 @@ import 'package:intl/intl.dart'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:queue_client/backend/DebugHelper.dart'; +import 'package:shared_preferences/shared_preferences.dart'; // DateFormat dateFormat; final String API_ENDPOINT= "https://vps.playpoolstudios.com/qms/api/"; -final dateTimeFormat = DateFormat("yyyy-MM-dd hh:mm"); +final dateTimeFormat = DateFormat("MM/dd hh:mm a"); +final timeFormat = DateFormat("hh:mm a"); class DataManager{ static _DataManager? m_instance = null; @@ -24,10 +26,20 @@ class _DataManager{ List services = []; List> AvailableServices = []; List> JoinedServices = []; - + String Username = ""; int userId = -1; + Future AutoLogin() async{ + final Prefs = await SharedPreferences.getInstance(); + if(Prefs.containsKey("username") && Prefs.containsKey("password")){ + return await Login(Prefs.getString("username") ?? "default",Prefs.getString("password") ?? "default"); + } + + return "-1"; + } + Future Login(String username,String password) async{ + String responseTxt = ""; try{ var response = (await http.post( @@ -39,6 +51,10 @@ class _DataManager{ responseTxt = response.body.toString(); int result = int.parse(response.body.toString()); userId = result; + final Prefs = await SharedPreferences.getInstance(); + Prefs.setString("username", username); + Prefs.setString("password",password); + Username = username; return "0"; }catch(e){ Debug.LogError(e); @@ -66,6 +82,9 @@ class _DataManager{ JoinedServices=[]; for (var m_service in services) { Map service= jsonDecode(m_service); + if(DateTime.parse(service['end_time']).isBefore(DateTime.now())){ + continue; + } List members = service['members'].toString().split(','); if(members.contains(userId.toString())){ @@ -113,4 +132,85 @@ class _DataManager{ ProcessServices(); } +} + +class Helpers{ + static int isPhoneNumber(String number){ + if(number.length != 10){ + return 1; + } + if(number[0] != "0"){ + return 2; + } + + + + if(number[1] != "7"){ + return 3; + } + if(number[2] == "3" || number[3] == "9"){ + return 4; + } + + return 0; + } + + static String DateTimeToRelative(DateTime dt){ + Duration diff = dt.difference(DateTime.now()); + if(diff.isNegative){return "Past";} + + final now = DateTime.now(); + final today = DateTime(now.year, now.month, now.day); + final yesterday = DateTime(now.year, now.month, now.day - 1); + final tomorrow = DateTime(now.year, now.month, now.day + 1); + final dateToCheck = dt; + final aDate = DateTime(dateToCheck.year, dateToCheck.month, dateToCheck.day); + if(aDate == today) { + return "Today at ${timeFormat.format(dt)}"; + } else if(aDate == tomorrow) { + return "Tomorrow at ${timeFormat.format(dt)}"; + } + + if(now.month == dt.month){ + String day = dt.day.toString(); + if(day == "1" || day=="21" || day=="31"){ + return "${day}st at ${timeFormat.format(dt)}"; + }else if(day =="2" || day=="22"){ + return "${day}nd at ${timeFormat.format(dt)}"; + }else if(day =="3" || day=="23"){ + return "${day}rd at ${timeFormat.format(dt)}"; + }else{ + return "${day}th at ${timeFormat.format(dt)}"; + } + } + + + return dateTimeFormat.format(dt); + } + 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 DurationExtension on Duration{ + + + String toCustomString(){ + if(this.inMinutes > 60){ + return "${this.inHours}:${this.inMinutes}"; + } + return "${this.inMinutes} mins"; + } +} + +extension ContextExtension on BuildContext { + bool get isTablet => MediaQuery.of(this).size.shortestSide > 600; + + bool get isPhone => MediaQuery.of(this).size.shortestSide < 600; + + bool get isSmall => MediaQuery.of(this).size.shortestSide < 340; } \ No newline at end of file diff --git a/lib/backend/Dialogs.dart b/lib/backend/Dialogs.dart index 6fbbc6d..6b82eeb 100644 --- a/lib/backend/Dialogs.dart +++ b/lib/backend/Dialogs.dart @@ -1,5 +1,6 @@ // import 'package:fhub/backend/login_mgr.dart'; +import 'package:duration_picker/duration_picker.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; // import 'package:flutter_spinkit/flutter_spinkit.dart'; @@ -66,6 +67,36 @@ class Dialogs{ ); } } + + static showDurationPicker(BuildContext context, String title) { + // set up the button + Widget okButton = TextButton( + child: Text("OK"), + onPressed: () { + Navigator.of(context).pop(); + }, + ); + + // set up the AlertDialog + AlertDialog alert = AlertDialog( + backgroundColor: Color(0xFF1F1F1F), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(40)), + title: Text(title,textAlign: TextAlign.center,), + // content: DurationPicker(onChange: (Duration value) { return value; },), + actions: [ + okButton, + ], + + ); + + // show the dialog + showDialog( + context: context, + builder: (BuildContext context) { + return alert; + }, + ); + } // static hide(){ // showing=false; // Navigator.of(navigatorKey.currentContext!).popUntil((route){ diff --git a/lib/home.dart b/lib/home.dart index 65fe6e1..b3fc7dc 100644 --- a/lib/home.dart +++ b/lib/home.dart @@ -12,7 +12,6 @@ class HomePage extends StatefulWidget { } class _HomePageState extends State { - @override void initState() { // TODO: implement initState @@ -21,71 +20,203 @@ class _HomePageState extends State { refresh(); } - void refresh()async{ + void refresh() async { await DataManager.instance().GetData(); - setState(() { - - }); + setState(() {}); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text("Queue Helper"), - ), - body: Container( - padding: EdgeInsets.all(20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + title: Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text("Joined"), - ListView.builder( - shrinkWrap: true, - itemCount: DataManager.instance().JoinedServices.length, - itemBuilder: (context, index){ - // Map service= jsonDecode(DataManager.instance().services[index]); - Map service= DataManager.instance().JoinedServices[index]; - return ServiceCard(tokenId:service['tokenId'],id: int.parse(service['id']),sTime: DateTime.parse(service['start_time'] ?? DateTime.now().toString()), name: service['name']!, memberCount: service['members']!.split(',').length-1); - }), - SizedBox(height:50), - Text("Available"), - ListView.builder( - shrinkWrap: true, - itemCount: DataManager.instance().AvailableServices.length, - itemBuilder: (context, index){ - // Map service= jsonDecode(DataManager.instance().services[index]); - Map service= DataManager.instance().AvailableServices[index]; - return ServiceCard(tokenId: -1,id: int.parse(service['id']),sTime: DateTime.parse(service['start_time'] ?? DateTime.now().toString()), name: service['name']!, memberCount: service['members']!.split(',').length-1); - }), + Text( + "${DataManager.instance().JoinedServices.length} Pending Queues",style: TextStyle(fontSize: 18),), + Row( + children: [ + Icon(Icons.person_2_rounded), + Text(" ${DataManager.instance().Username} ",style: TextStyle(fontSize: 18)), + ], + ), ], ), ), + body: SingleChildScrollView( + child: Container( + padding: EdgeInsets.all(20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Card( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.token), + Text(" Your Token ID") + ], + ), + Text(DataManager.instance().userId.toString(),style: TextStyle(fontSize: 45),) + ], + ), + ), + ), + Text("Joined"), + SizedBox(height: 10,), + GridView.count( + shrinkWrap: true, + crossAxisCount: context.isSmall ? 1 : ( context.isTablet ? 4: 2), + children: List.generate( + DataManager.instance().JoinedServices.length, (index) { + Map service = + DataManager.instance().JoinedServices[index]; + return ServiceCard( + tokenId: service['tokenId'], + id: int.parse(service['id']), + sTime: DateTime.parse( + service['start_time'] ?? DateTime.now().toString()), + eTime: DateTime.parse( + service['end_time'] ?? DateTime.now().toString()), + name: service['name']!, + memberCount: service['members']!.split(',').length - 1, + duration: Helpers.ParseDuration(service['duration']) + ); + }), + ), + // ListView.builder( + // shrinkWrap: true, + // itemCount: DataManager.instance().JoinedServices.length, + // itemBuilder: (context, index){ + // // Map service= jsonDecode(DataManager.instance().services[index]); + // Map service= DataManager.instance().JoinedServices[index]; + // return ServiceCard(tokenId:service['tokenId'],id: int.parse(service['id']),sTime: DateTime.parse(service['start_time'] ?? DateTime.now().toString()), name: service['name']!, memberCount: service['members']!.split(',').length-1); + // }), + SizedBox(height: 50), + Text("Available"), + SizedBox(height: 10,), + ListView.builder( + shrinkWrap: true, + itemCount: DataManager.instance().AvailableServices.length, + itemBuilder: (context, index) { + // Map service= jsonDecode(DataManager.instance().services[index]); + Map service = + DataManager.instance().AvailableServices[index]; + return ServiceCard( + tokenId: -1, + id: int.parse(service['id']), + sTime: DateTime.parse( + service['start_time'] ?? DateTime.now().toString()), + eTime: DateTime.parse( + service['end_time'] ?? DateTime.now().toString()), + name: service['name']!, + memberCount: service['members']!.split(',').length - 1, + duration: Helpers.ParseDuration(service['duration'])); + }), + ], + ), + ), + ), ); } Widget ServiceCard( - {required int id, required DateTime sTime, + {required int id, + required DateTime sTime, + required DateTime eTime, required String name, - required int memberCount, required int tokenId}) { + required int memberCount, + required int tokenId, + required Duration duration}) { + + + DateTime eta = sTime.add(duration * tokenId); + return InkWell( - onTap: () async{ - await Navigator.of(context).push(MaterialPageRoute(builder: (context)=> ServiceInfoPage(id: id,tokenId: tokenId,))); - setState(() { + onTap: () async { - }); + if(tokenId > 0){return;} + + await Navigator.of(context).push(MaterialPageRoute( + builder: (context) => ServiceInfoPage( + id: id, + tokenId: tokenId, + ))); + setState(() {}); }, child: Card( child: Padding( - padding: const EdgeInsets.all(8.0), - child: Row( + padding: const EdgeInsets.all(10.0), + child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text("${sTime.year}-${sTime.month}-${sTime.day}"), - Text(name), - Container(width: 25,height: 25,decoration: BoxDecoration(borderRadius: BorderRadius.circular(50),color: Colors.deepPurple),child: Center(child: Text(memberCount.toString()),),) - ]), + children: [ + Center( + child: Text( + name, + style: TextStyle(fontSize: 20), + )), + SizedBox(height: tokenId > 0 ? 0 : 10,), + (tokenId > 0) ? Column( + children: [ + Row(mainAxisAlignment: MainAxisAlignment.center, children: [ + Icon(Icons.people_alt_rounded,size: 25,), + Text( + " $tokenId", + style: TextStyle(fontSize: 40), + ), + ]), + ], + ) + : Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Expanded(flex:1,child: Icon(Icons.people)), + Expanded(flex: 4,child: Text("$memberCount in queue",maxLines: 3,overflow: TextOverflow.ellipsis,)) + ]), + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Expanded(flex:1,child: Icon(Icons.access_time)), + Expanded(flex: 4,child: Text("Be there around ${Helpers.DateTimeToRelative(eta)}",maxLines: 3,overflow: TextOverflow.ellipsis,)) + ]), + // (sTime.isAfter(DateTime.now())) + // ? Row( + // mainAxisAlignment: MainAxisAlignment.spaceAround, + // children: [ + // Expanded(flex:1,child: Icon(Icons.access_time)), + // Expanded(flex: 4,child: Text("Starts ${Helpers.DateTimeToRelative(sTime)}",maxLines: 3,overflow: TextOverflow.ellipsis,style: TextStyle(fontSize: 13),)) + // ]) + // : Row( + // mainAxisAlignment: MainAxisAlignment.spaceAround, + // children: [ + // Expanded(flex:1,child: Icon(Icons.directions_run_outlined)), + // Expanded(flex: 4, + // child: Text( + // "Ongoing until ${Helpers.DateTimeToRelative(eTime)}"), + // ) + // ]), + + (tokenId <=0) ? + Column( + children: [ + SizedBox(height: 10,), + Padding( + padding: const EdgeInsets.all(8.0), + child: Center( + child: Text("Tap to join the queue",style: TextStyle(fontSize: 12,color: Colors.grey),), + ), + ), + ], + ) : Container() + + ], + ), ), ), ); diff --git a/lib/login.dart b/lib/login.dart index 8640c04..2b3fcbd 100644 --- a/lib/login.dart +++ b/lib/login.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:queue_client/CustomWidgets.dart'; import 'package:queue_client/backend/DataManager.dart'; import 'package:queue_client/backend/Dialogs.dart'; import 'package:queue_client/home.dart'; @@ -13,36 +14,107 @@ class LoginPage extends StatefulWidget { class _LoginPageState extends State { TextEditingController usernameController = TextEditingController(); TextEditingController passwordController = TextEditingController(); - bool logging = false; + + @override + void initState() { + // TODO: implement initState + super.initState(); + + AutoLogin(); + } + + void AutoLogin()async{ + String results = await DataManager.instance().AutoLogin(); + isLogging = true; + setState(() { + + }); + if(results == "0"){ + Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (context)=>HomePage())); + }else if(results == "-1"){ + + }else{ + Dialogs.showAlertDialog(context, "Failed login", results); + } + + isLogging=false; + setState(() { + + }); + } + bool isLogging = true; @override Widget build(BuildContext context) { return Scaffold( - body: Center( - child: Card( - child: Container( - padding: EdgeInsets.all(20), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - Text("Login"), - SizedBox(height: 30,), - ListTile(title: Text("Phone Number"), - subtitle: TextField(controller: usernameController),), + body: SafeArea( + child: Container( + padding: EdgeInsets.all(20), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + children: [ + SizedBox(height: 40,), + // Text("Queue Helper",style:TextStyle(fontSize: 45)), + GradientText(text: "Digital Queue", gradient: LinearGradient(colors: [Colors.white.withOpacity(0.5), Colors.white.withOpacity(0.3)]),style: TextStyle(fontSize: 45,fontWeight: FontWeight.bold),), + Text("Stop waiting at queues!") + ], + ), + (isLogging) ? Container() : Center( + child: Card( + child: Container( + width: 350, + padding: EdgeInsets.all(20), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + // Text("Login"), + ListTile(title: Text("Enter your Phone Number"), + subtitle: TextField(controller: usernameController,decoration: InputDecoration(hintText: "ex: 0701234567"),),), + SizedBox(height: 30,), - ElevatedButton(onPressed: () async{ - setState(() { - logging=true; - }); - String results = await DataManager.instance().Login(usernameController.text, passwordController.text); - if(results == "0"){ - Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (context)=>HomePage())); - }else{ - Dialogs.showAlertDialog(context, "Failed login", results); - } - }, child: Text("Continue")) - ], - ), + ElevatedButton(autofocus: true,onPressed: () async{ + + int isPhoneNumber = Helpers.isPhoneNumber(usernameController.text); + if(isPhoneNumber==1){ + Dialogs.showAlertDialog(context, "Invalid Number", "Phone number must be 10 digits"); + return; + } + if(isPhoneNumber==2 || isPhoneNumber==4){ + Dialogs.showAlertDialog(context, "Invalid Number", "Please enter a valid phone number"); + return; + } + if(isPhoneNumber==3){ + Dialogs.showAlertDialog(context, "Invalid Number", "Please Enter your Personal Phone number"); + return; + } + + setState(() { + isLogging=true; + }); + String results = await DataManager.instance().Login(usernameController.text, usernameController.text); + if(results == "0"){ + Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (context)=>HomePage())); + }else{ + Dialogs.showAlertDialog(context, "Failed login", results); + } + + setState(() { + isLogging=false; + }); + }, child: Padding( + padding: const EdgeInsets.all(8.0), + child: Text("Login"), + + )) + ], + ), + ), + ), + ), + Text("Developed by Xperience Technologies",style: TextStyle(fontSize: 12,color: Colors.grey)) + ], ), ), ), diff --git a/pubspec.lock b/pubspec.lock index 9e099d7..54f2a3f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -49,6 +49,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.5" + duration_picker: + dependency: "direct main" + description: + name: duration_picker + sha256: "052b34dac04c29f3849bb3817a26c5aebe9e5f0697c3a374be87db2b84d75753" + url: "https://pub.dev" + source: hosted + version: "1.1.1" fake_async: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 8e42665..509aebe 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -35,6 +35,7 @@ dependencies: intl: ^0.18.1 shared_preferences: ^2.2.0 cupertino_icons: ^1.0.2 + duration_picker: ^1.1.1 dev_dependencies: flutter_test: