diff --git a/campus/frontend/.vscode/settings.json b/campus/frontend/.vscode/settings.json new file mode 100644 index 00000000..b242572e --- /dev/null +++ b/campus/frontend/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "githubPullRequests.ignoredPullRequestBranches": [ + "main" + ] +} \ No newline at end of file diff --git a/campus/mobile/.vscode/launch.json b/campus/mobile/.vscode/launch.json index 73a6ee10..780c2e1d 100644 --- a/campus/mobile/.vscode/launch.json +++ b/campus/mobile/.vscode/launch.json @@ -24,6 +24,18 @@ // "development" // ] }, + { + "name": "debug pysical device", + "request": "launch", + "type": "dart", + "flutterMode": "debug", + "deviceId": "TECNO KG5j", + "runTestsOnDevice": false, + // "args": [ + // "--flavor", + // "development" + // ] + }, { "name": "mobile (release mode)", "request": "launch", diff --git a/campus/mobile/.vscode/settings.json b/campus/mobile/.vscode/settings.json new file mode 100644 index 00000000..b242572e --- /dev/null +++ b/campus/mobile/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "githubPullRequests.ignoredPullRequestBranches": [ + "main" + ] +} \ No newline at end of file diff --git a/campus/mobile/assets/config/dev.json b/campus/mobile/assets/config/dev.json index d7b9df4e..42a0f754 100644 --- a/campus/mobile/assets/config/dev.json +++ b/campus/mobile/assets/config/dev.json @@ -1,6 +1,6 @@ { - "campusProfileBffApiUrl" : "http://10.0.2.2:9090", - "campusAttendanceBffApiUrl" : "http://10.0.2.2:9091", + "campusProfileBffApiUrl" : "http://192.168.8.192:9090", + "campusAttendanceBffApiUrl" : "http://192.168.8.192:9091", "campusPctiNotesBffApiUrl" : "http://10.0.2.2:9092", "campusPctiFeedbackBffApiUrl" : "http://10.0.2.2:9093", "campusAssetsBffApiUrl" : "http://10.0.2.2:9094", diff --git a/campus/mobile/lib/auth.dart b/campus/mobile/lib/auth.dart index f9187efc..74292076 100644 --- a/campus/mobile/lib/auth.dart +++ b/campus/mobile/lib/auth.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'dart:developer'; // import 'dart:html'; +import 'package:flutter_appauth/flutter_appauth.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:universal_html/html.dart'; @@ -18,17 +19,18 @@ import 'package:jwt_decoder/jwt_decoder.dart'; /// A mock authentication service class CampusAppsPortalAuth extends ChangeNotifier { bool _signedIn = false; - var _openid_tokens; Future getSignedIn() async { - log("_guard signed in uuuuuu"); - if (_signedIn) - return _signedIn; // already signed in -- todo - remove before production release - // var tokens = window.localStorage['openid_client:tokens']; SharedPreferences prefs = await SharedPreferences.getInstance(); String accessToken = prefs.getString('access_token') ?? ''; String refreshToken = prefs.getString('refresh_token') ?? ''; String idToken = prefs.getString('id_token') ?? ''; + + log("_guard signed in uuuuuu"); + if (_signedIn) + return _signedIn; // already signed in -- todo - remove before production release + // var tokens = window.localStorage['openid_client:tokens']; + if (accessToken.isNotEmpty && idToken.isNotEmpty) { log("_guard signed in truetrue"); // _openid_tokens = json.decode(tokens); @@ -198,6 +200,48 @@ class CampusAppsPortalAuth extends ChangeNotifier { int get hashCode => _signedIn.hashCode; } +Future authenticate(Uri uri, String clientId, List scopes, + String redirectURL, String discoveryUrl) async { + const FlutterAppAuth flutterAppAuth = FlutterAppAuth(); + + try { + final AuthorizationTokenResponse? result = + await flutterAppAuth.authorizeAndExchangeCode( + AuthorizationTokenRequest( + clientId, + redirectURL, + discoveryUrl: discoveryUrl, + promptValues: ['login'], + scopes: scopes, + ), + ); + + print('Access token bla bla bla: ${result?.accessToken}'); + String accessToken = result?.accessToken ?? ''; + String refreshToken = result?.refreshToken ?? ''; + String idToken = result?.idToken ?? ''; + SharedPreferences prefs = await SharedPreferences.getInstance(); + await prefs.setString('access_token', accessToken); + await prefs.setString('refresh_token', refreshToken); + await prefs.setString('id_token', idToken); + + // final _auth = CampusAppsPortalAuth(); + // final signedIn = await _auth.getSignedIn(); + + // setState(() { + // _isUserLoggedIn = true; + // _idToken = result?.idToken; + // _accessToken = result?.accessToken; + // _pageIndex = 2; + // }); + } catch (e, s) { + print('Error while login to the system: $e - stack: $s'); + // setState(() { + // _isUserLoggedIn = false; + // }); + } +} + class SMSAuthScope extends InheritedNotifier { const SMSAuthScope({ required super.notifier, diff --git a/campus/mobile/lib/avinya/attendance/lib/data/attendance_data.dart b/campus/mobile/lib/avinya/attendance/lib/data/attendance_data.dart new file mode 100644 index 00000000..3dcee55a --- /dev/null +++ b/campus/mobile/lib/avinya/attendance/lib/data/attendance_data.dart @@ -0,0 +1,43 @@ +// ignore_for_file: non_constant_identifier_names + +class AttendanceData { + final int? activity_instance_id; + final int? person_id; + final String? preferred_name; + final String sign_in_time; + final String? sign_out_time; + final String? in_marked_by; + + AttendanceData({ + required this.activity_instance_id, + required this.person_id, + required this.preferred_name, + required this.sign_in_time, + required this.sign_out_time, + required this.in_marked_by, + }); + + // Factory constructor for deserialization + factory AttendanceData.fromJson(Map json) { + return AttendanceData( + activity_instance_id: json['activity_instance_id'], + person_id: json['person_id'], + preferred_name: json['preferred_name'], + sign_in_time: json['sign_in_time'], + sign_out_time: json['sign_out_time'], + in_marked_by: json['in_marked_by'], + ); + } + + // Method to convert the object to a Map for serialization + Map toJson() { + return { + 'activity_instance_id': activity_instance_id, + 'person_id': person_id, + 'preferred_name': preferred_name, + 'sign_in_time': sign_in_time, + 'sign_out_time': sign_out_time, + 'in_marked_by': in_marked_by, + }; + } +} diff --git a/campus/mobile/lib/avinya/attendance/lib/screens/attendance_marker.dart b/campus/mobile/lib/avinya/attendance/lib/screens/attendance_marker.dart index 13bc9c40..7bf18cf2 100644 --- a/campus/mobile/lib/avinya/attendance/lib/screens/attendance_marker.dart +++ b/campus/mobile/lib/avinya/attendance/lib/screens/attendance_marker.dart @@ -25,22 +25,21 @@ class _AttendanceMarkerScreenState extends State { @override Widget build(BuildContext context) => Scaffold( appBar: AppBar( - title: Text('Attendance Marker'), + title: const Text('Attendance Marker'), ), body: SingleChildScrollView( child: Center( child: Column( children: [ - SizedBox(height: 40), + const SizedBox(height: 40), AttendanceMarker(), - SizedBox(height: 20), - Text('Person Attendance Report'), - SizedBox(height: 5), - Container( - width: 500, - height: 500, - child: PersonAttendanceMarkerReport() - ), + const SizedBox(height: 20), + const Text('Person Attendance Report'), + const SizedBox(height: 5), + const SizedBox( + width: 500, + height: 500, + child: PersonAttendanceMarkerReport()), ], ), ), diff --git a/campus/mobile/lib/avinya/attendance/lib/widgets/attedance_marker.dart b/campus/mobile/lib/avinya/attendance/lib/widgets/attedance_marker.dart index 5cf9b960..e6ed554f 100644 --- a/campus/mobile/lib/avinya/attendance/lib/widgets/attedance_marker.dart +++ b/campus/mobile/lib/avinya/attendance/lib/widgets/attedance_marker.dart @@ -1,12 +1,14 @@ // ignore_for_file: use_build_context_synchronously import 'package:flutter/material.dart'; +import 'package:mobile/avinya/attendance/lib/data/attendance_data.dart'; import '../data.dart'; import '../data/activity_attendance.dart'; import 'package:attendance/data/evaluation.dart'; // import 'package:attendance/widgets/evaluation_list.dart'; import 'package:mobile/avinya/attendance/lib/widgets/evaluation_list.dart'; import 'package:mobile/avinya/attendance/lib/widgets/qr_image.dart'; +import 'dart:convert'; class AttendanceMarker extends StatefulWidget { @override @@ -27,24 +29,78 @@ class _AttendanceMarkerState extends State { var activityInstance = await campusAttendanceSystemInstance.getCheckinActivityInstance( campusAppsPortalInstance.activityIds['school-day']); - // call the API to check-in - await createActivityAttendance(ActivityAttendance( + + final AttendanceData data = AttendanceData( activity_instance_id: activityInstance.id, person_id: campusAppsPortalInstance.getUserPerson().id, + preferred_name: campusAppsPortalInstance.getUserPerson().preferred_name, sign_in_time: DateTime.now().toString(), + sign_out_time: null, in_marked_by: campusAppsPortalInstance.getUserPerson().digital_id, + ); + // call the API to check-in + await createActivityAttendance(ActivityAttendance( + activity_instance_id: data.activity_instance_id, + person_id: data.person_id, + sign_in_time: data.sign_in_time, + in_marked_by: data.in_marked_by, )); + String dataJson = jsonEncode(data); await refreshPersonActivityAttendanceToday(); Navigator.push( context, MaterialPageRoute( builder: ((context) { - return QRImage(sign_in_time); + return QRImage(dataJson); }), ), ); setState(() { - qrCodeData = sign_in_time; + qrCodeData = dataJson; + }); + print('Checked in for today.'); + } + + Future _handleQr() async { + var activityInstance = + await campusAttendanceSystemInstance.getCheckinActivityInstance( + campusAppsPortalInstance.activityIds['school-day']); + _personAttendanceToday = await getPersonActivityAttendanceToday( + campusAppsPortalInstance.getUserPerson().id!, + campusAppsPortalInstance.activityIds['school-day']!); + + final AttendanceData data; + if (_personAttendanceToday.length == 1) { + data = AttendanceData( + activity_instance_id: activityInstance.id, + person_id: campusAppsPortalInstance.getUserPerson().id, + preferred_name: campusAppsPortalInstance.getUserPerson().preferred_name, + sign_in_time: _personAttendanceToday[0].sign_in_time.toString(), + sign_out_time: null, + in_marked_by: campusAppsPortalInstance.getUserPerson().digital_id, + ); + } else { + data = AttendanceData( + activity_instance_id: activityInstance.id, + person_id: campusAppsPortalInstance.getUserPerson().id, + preferred_name: campusAppsPortalInstance.getUserPerson().preferred_name, + sign_in_time: _personAttendanceToday[0].sign_in_time.toString(), + sign_out_time: _personAttendanceToday[1].sign_out_time.toString(), + in_marked_by: campusAppsPortalInstance.getUserPerson().digital_id, + ); + } + + String dataJson = jsonEncode(data); + Navigator.push( + context, + MaterialPageRoute( + builder: ((context) { + return QRImage(dataJson); + }), + ), + ); + setState(() { + qrCodeData = dataJson; }); print('Checked in for today.'); } @@ -53,6 +109,14 @@ class _AttendanceMarkerState extends State { var activityInstance = await campusAttendanceSystemInstance.getCheckoutActivityInstance( campusAppsPortalInstance.activityIds['school-day']); + final AttendanceData data = AttendanceData( + activity_instance_id: activityInstance.id, + person_id: campusAppsPortalInstance.getUserPerson().id, + preferred_name: campusAppsPortalInstance.getUserPerson().preferred_name, + sign_in_time: DateTime.now().toString(), + sign_out_time: null, + in_marked_by: campusAppsPortalInstance.getUserPerson().digital_id, + ); // call the API to check-out await createActivityAttendance(ActivityAttendance( activity_instance_id: activityInstance.id, @@ -60,10 +124,22 @@ class _AttendanceMarkerState extends State { sign_out_time: DateTime.now().toString(), out_marked_by: campusAppsPortalInstance.getUserPerson().digital_id, )); + String dataJson = jsonEncode(data); await refreshPersonActivityAttendanceToday(); + Navigator.push( + context, + MaterialPageRoute( + builder: ((context) { + return QRImage(dataJson); + }), + ), + ); setState(() { - //_isCheckedOut = true; + qrCodeData = dataJson; }); + // setState(() { + // //_isCheckedOut = true; + // }); print('Checked out for today.'); } @@ -184,13 +260,40 @@ class _AttendanceMarkerState extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ const Text('Attendance marked for today.'), - if (_personAttendanceToday.isNotEmpty) - Text( - 'Checked in at ${_personAttendanceToday[0].sign_in_time!}'), - if (_personAttendanceToday.length > 1) - Text( - 'Checked out at ${_personAttendanceToday[1].sign_out_time!}'), - const SizedBox(width: 20), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (_personAttendanceToday.isNotEmpty) + Text( + 'Checked in at ${_personAttendanceToday[0].sign_in_time!}'), + const SizedBox(width: 20), + IconButton( + icon: const Icon(Icons.qr_code), + onPressed: () { + // Navigate to the QR code view screen when the button is clicked + _handleQr(); + }, + tooltip: 'View QR Code', + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (_personAttendanceToday.length > 1) + Text( + 'Checked out at ${_personAttendanceToday[1].sign_out_time!}'), + const SizedBox(width: 20), + IconButton( + icon: const Icon(Icons.qr_code), + onPressed: () { + // Navigate to the QR code view screen when the button is clicked + _handleQr(); + }, + tooltip: 'View QR Code', + ), + ], + ), ], ) else if (_isAbsent) @@ -210,14 +313,7 @@ class _AttendanceMarkerState extends State { icon: const Icon(Icons.qr_code), onPressed: () { // Navigate to the QR code view screen when the button is clicked - Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return QRImage(qrCodeData); - }, - ), - ); + _handleQr(); }, tooltip: 'View QR Code', ), diff --git a/campus/mobile/lib/avinya/attendance/lib/widgets/qr_attedance_checkin.dart b/campus/mobile/lib/avinya/attendance/lib/widgets/qr_attedance_checkin.dart new file mode 100644 index 00000000..fc748495 --- /dev/null +++ b/campus/mobile/lib/avinya/attendance/lib/widgets/qr_attedance_checkin.dart @@ -0,0 +1,278 @@ +// ignore_for_file: non_constant_identifier_names, unused_element, use_build_context_synchronously, use_key_in_widget_constructors, library_private_types_in_public_api + +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:mobile/avinya/attendance/lib/data/attendance_data.dart'; +import '../data.dart'; +import '../data/activity_attendance.dart'; +import 'package:attendance/data/evaluation.dart'; +// import 'package:attendance/widgets/evaluation_list.dart'; +import 'package:mobile/avinya/attendance/lib/widgets/evaluation_list.dart'; +import 'package:mobile/avinya/attendance/lib/widgets/qr_image.dart'; +import 'package:qr_code_scanner/qr_code_scanner.dart'; + +class QrAttendanceCheckIn extends StatefulWidget { + @override + _QrAttendanceCheckInState createState() => _QrAttendanceCheckInState(); +} + +class _QrAttendanceCheckInState extends State { + bool _isCheckedIn = false; + bool _isCheckedOut = false; + bool _isAbsent = false; + bool markedAttendance = false; + bool inValidQr = false; + bool isFirstTime = true; + List _personAttendanceToday = []; + List _fechedEvaluations = []; + + String sign_in_time = "ee"; + + final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); + QRViewController? controller; + AttendanceData qrCodeData = AttendanceData( + activity_instance_id: 0, + person_id: 0, + preferred_name: '', + sign_in_time: '', + sign_out_time: '', + in_marked_by: '', + ); + var activityId = 0; + var afterSchoolActivityId = 0; + @override + void initState() { + super.initState(); + activityId = campusAppsPortalInstance.activityIds['homeroom']!; + afterSchoolActivityId = + campusAppsPortalInstance.activityIds['after-school']!; + } + + @override + void dispose() { + controller?.dispose(); + super.dispose(); + } + + void _onQRViewCreated(QRViewController controller) { + this.controller = controller; + controller.scannedDataStream.listen((scanData) { + try { + AttendanceData deserializedQrCodeData = + AttendanceData.fromJson(jsonDecode(scanData.code!)); + + setState(() { + qrCodeData = deserializedQrCodeData; + inValidQr = false; + }); + if (qrCodeData.person_id != 0) { + _handleCheckIn(); + } + } catch (e) { + setState(() { + inValidQr = true; + }); + } + }); + } + + Future _handleCheckIn() async { + var activityInstance = + await campusAttendanceSystemInstance.getCheckinActivityInstance( + campusAppsPortalInstance.activityIds['homeroom']); + + _personAttendanceToday = await getPersonActivityAttendanceToday( + qrCodeData.person_id!, + campusAppsPortalInstance.activityIds['homeroom']!); + if (_personAttendanceToday.isEmpty) { + // call the API to check-in + await createActivityAttendance(ActivityAttendance( + activity_instance_id: activityInstance.id, + person_id: qrCodeData.person_id, + sign_in_time: DateTime.now().toString(), + in_marked_by: campusAppsPortalInstance.getUserPerson().digital_id, + )); + } else { + setState(() { + markedAttendance = true; + isFirstTime = false; + }); + } + + await refreshPersonActivityAttendanceToday(); + print('Checked in for today.'); + } + + Future _handleCheckOut() async { + var activityInstance = + await campusAttendanceSystemInstance.getCheckoutActivityInstance( + campusAppsPortalInstance.activityIds['school-day']); + // call the API to check-out + await createActivityAttendance(ActivityAttendance( + activity_instance_id: activityInstance.id, + person_id: campusAppsPortalInstance.getUserPerson().id, + sign_out_time: DateTime.now().toString(), + out_marked_by: campusAppsPortalInstance.getUserPerson().digital_id, + )); + await refreshPersonActivityAttendanceToday(); + setState(() { + //_isCheckedOut = true; + }); + print('Checked out for today.'); + } + + Future> + refreshPersonActivityAttendanceToday() async { + _personAttendanceToday = await getPersonActivityAttendanceToday( + qrCodeData.person_id!, + campusAppsPortalInstance.activityIds['homeroom']!); + if (_personAttendanceToday.isNotEmpty) { + _isCheckedIn = _personAttendanceToday[0].sign_in_time != null; + } + if (_personAttendanceToday.length > 1) { + _isCheckedOut = _personAttendanceToday[1].sign_out_time != null; + } + + // if (!_isCheckedIn) { + // var activityInstance = + // await campusAttendanceSystemInstance.getCheckinActivityInstance( + // campusAppsPortalInstance.activityIds['homeroom']!); + // _fechedEvaluations = + // await getActivityInstanceEvaluations(activityInstance.id!); + + // if (_fechedEvaluations.indexWhere((element) => + // element.evaluator_id == + // qrCodeData.person_id!) == + // -1) { + // _isCheckedIn = false; + // } else { + // _isCheckedIn = true; + // _isCheckedOut = true; + // _isAbsent = true; + // } + // } + + return _personAttendanceToday; + } + + @override + Widget build(BuildContext context) { + return FutureBuilder>( + future: refreshPersonActivityAttendanceToday(), + builder: (context, snapshot) { + if (snapshot.hasData) { + if (snapshot.data!.isNotEmpty) { + _isCheckedIn = snapshot.data![0].sign_in_time != null; + } + if (snapshot.data!.length > 1) { + _isCheckedOut = snapshot.data![1].sign_out_time != null; + } + return Scaffold( + appBar: AppBar( + title: const Text( + 'Check-In by (QR)'), // Customize your app title here + ), + body: Center( + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox(height: 5), + SizedBox( + height: 300, + width: 300, + child: QRView( + key: qrKey, + onQRViewCreated: _onQRViewCreated, + ), + ), + const SizedBox(height: 20), + if (!inValidQr) ...[ + // if (!isFirstTime) ...[ + // Column( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // Text( + // 'Checked in for today at ${_personAttendanceToday[0].sign_in_time!}'), + // const SizedBox(width: 20), + // ], + // ), + // ] + if (qrCodeData.person_id != 0) ...[ + Column( + children: [ + // Text( + // 'QR Code Data:', + // style: TextStyle( + // fontSize: 18, fontWeight: FontWeight.bold), + // ), + // Text( + // 'Person ID: ${qrCodeData.person_id}', + // style: const TextStyle(fontSize: 18), + // ), + Text( + 'Student Name: ${qrCodeData.preferred_name}', + style: const TextStyle(fontSize: 18), + ), + Text( + 'Check-In Time: ${qrCodeData.sign_in_time}', + style: const TextStyle(fontSize: 18), + ), + if (markedAttendance) const SizedBox(height: 20), + const Text( + 'Check-In completed successfully!', + style: TextStyle( + fontSize: 18, + color: Colors.green, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ], + // if (_isCheckedOut && _isCheckedIn) ...[ + // Column( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // const Text('Attendance Marked for Today.'), + // if (_personAttendanceToday.isNotEmpty) + // Text( + // 'Checked in at ${_personAttendanceToday[0].sign_in_time!}'), + // if (_personAttendanceToday.length > 1) + // Text( + // 'Checked out at ${_personAttendanceToday[1].sign_out_time!}'), + // const SizedBox(width: 20), + // ], + // ), + // ], + ...[ + if (qrCodeData.person_id == 0 && isFirstTime) + const Text( + 'Scan QR Code to Mark Attendance', + style: TextStyle(fontSize: 18), + ), + ], + ] else ...[ + const Text( + 'Invalid QR Code', + style: TextStyle(fontSize: 18), + ), + ], + ], + ), + ), + ), + ); + } else if (snapshot.hasError) { + return Text('${snapshot.error}'); + } + + // By default, show a loading spinner. + return const CircularProgressIndicator(); + }, + ); + // + } +} diff --git a/campus/mobile/lib/avinya/attendance/lib/widgets/qr_attedance_checkout.dart b/campus/mobile/lib/avinya/attendance/lib/widgets/qr_attedance_checkout.dart new file mode 100644 index 00000000..33cdfced --- /dev/null +++ b/campus/mobile/lib/avinya/attendance/lib/widgets/qr_attedance_checkout.dart @@ -0,0 +1,276 @@ +// ignore_for_file: non_constant_identifier_names, unused_element, use_build_context_synchronously, use_key_in_widget_constructors, library_private_types_in_public_api + +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:mobile/avinya/attendance/lib/data/attendance_data.dart'; +import '../data.dart'; +import '../data/activity_attendance.dart'; +import 'package:attendance/data/evaluation.dart'; +// import 'package:attendance/widgets/evaluation_list.dart'; +import 'package:mobile/avinya/attendance/lib/widgets/evaluation_list.dart'; +import 'package:mobile/avinya/attendance/lib/widgets/qr_image.dart'; +import 'package:qr_code_scanner/qr_code_scanner.dart'; + +class QrAttendanceCheckOut extends StatefulWidget { + @override + _QrAttendanceCheckOutState createState() => _QrAttendanceCheckOutState(); +} + +class _QrAttendanceCheckOutState extends State { + bool _isCheckedIn = false; + bool _isCheckedOut = false; + bool _isAbsent = false; + bool markedAttendance = false; + bool inValidQr = false; + bool isFirstTime = true; + List _personAttendanceToday = []; + List _fechedEvaluations = []; + + String sign_in_time = "ee"; + + final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); + QRViewController? controller; + AttendanceData qrCodeData = AttendanceData( + activity_instance_id: 0, + person_id: 0, + preferred_name: '', + sign_in_time: '', + sign_out_time: '', + in_marked_by: '', + ); + var activityId = 0; + var afterSchoolActivityId = 0; + @override + void initState() { + super.initState(); + activityId = campusAppsPortalInstance.activityIds['homeroom']!; + afterSchoolActivityId = + campusAppsPortalInstance.activityIds['after-school']!; + } + + @override + void dispose() { + controller?.dispose(); + super.dispose(); + } + + void _onQRViewCreated(QRViewController controller) { + this.controller = controller; + controller.scannedDataStream.listen((scanData) { + try { + AttendanceData deserializedQrCodeData = + AttendanceData.fromJson(jsonDecode(scanData.code!)); + + setState(() { + qrCodeData = deserializedQrCodeData; + inValidQr = false; + }); + if (qrCodeData.person_id != 0) _handleCheckOut(); + } catch (e) { + setState(() { + inValidQr = true; + }); + } + }); + } + + Future _handleCheckOut() async { + var activityInstance = + await campusAttendanceSystemInstance.getCheckinActivityInstance( + campusAppsPortalInstance.activityIds['homeroom']); + + _personAttendanceToday = await getPersonActivityAttendanceToday( + qrCodeData.person_id!, + campusAppsPortalInstance.activityIds['homeroom']!); + if (_personAttendanceToday.length == 1) { + setState(() { + isFirstTime = true; + }); + // call the API to check-out + await createActivityAttendance(ActivityAttendance( + activity_instance_id: activityInstance.id, + person_id: qrCodeData.person_id, + sign_out_time: DateTime.now().toString(), + out_marked_by: campusAppsPortalInstance.getUserPerson().digital_id, + )); + } else { + setState(() { + markedAttendance = true; + isFirstTime = false; + }); + } + + await refreshPersonActivityAttendanceToday(); + + print('Checked in for today.'); + } + + // Future _handleCheckOut() async { + // var activityInstance = + // await campusAttendanceSystemInstance.getCheckoutActivityInstance( + // campusAppsPortalInstance.activityIds['school-day']); + // // call the API to check-out + // await createActivityAttendance(ActivityAttendance( + // activity_instance_id: activityInstance.id, + // person_id: campusAppsPortalInstance.getUserPerson().id, + // sign_out_time: DateTime.now().toString(), + // out_marked_by: campusAppsPortalInstance.getUserPerson().digital_id, + // )); + // await refreshPersonActivityAttendanceToday(); + // setState(() { + // //_isCheckedOut = true; + // }); + // print('Checked out for today.'); + // } + + Future> + refreshPersonActivityAttendanceToday() async { + _personAttendanceToday = await getPersonActivityAttendanceToday( + qrCodeData.person_id!, + campusAppsPortalInstance.activityIds['homeroom']!); + if (_personAttendanceToday.isNotEmpty) { + _isCheckedIn = _personAttendanceToday[0].sign_in_time != null; + } + if (_personAttendanceToday.length > 1) { + _isCheckedOut = _personAttendanceToday[1].sign_out_time != null; + } + + // if (!_isCheckedIn) { + // var activityInstance = + // await campusAttendanceSystemInstance.getCheckinActivityInstance( + // campusAppsPortalInstance.activityIds['homeroom']!); + // _fechedEvaluations = + // await getActivityInstanceEvaluations(activityInstance.id!); + + // if (_fechedEvaluations.indexWhere((element) => + // element.evaluator_id == + // qrCodeData.person_id!) == + // -1) { + // _isCheckedIn = false; + // } else { + // _isCheckedIn = true; + // _isCheckedOut = true; + // _isAbsent = true; + // } + // } + + return _personAttendanceToday; + } + + @override + Widget build(BuildContext context) { + return FutureBuilder>( + future: refreshPersonActivityAttendanceToday(), + builder: (context, snapshot) { + if (snapshot.hasData) { + if (snapshot.data!.isNotEmpty) { + _isCheckedIn = snapshot.data![0].sign_in_time != null; + } + if (snapshot.data!.length > 1) { + _isCheckedOut = snapshot.data![1].sign_out_time != null; + } + return Scaffold( + appBar: AppBar( + title: const Text( + 'Check-Out by (QR)'), // Customize your app title here + ), + body: Center( + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox(height: 5), + SizedBox( + height: 300, + width: 300, + child: QRView( + key: qrKey, + onQRViewCreated: _onQRViewCreated, + ), + ), + const SizedBox(height: 20), + if (!inValidQr) ...[ + // ignore: prefer_is_empty + // if (!isFirstTime) ...[ + // Column( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // Text( + // 'Checked out for today at ${_personAttendanceToday[1].sign_out_time!}'), + // const SizedBox(width: 20), + // ], + // ), + // ] + if (qrCodeData.person_id != 0) ...[ + Column( + children: [ + // Text( + // 'Person ID: ${qrCodeData.person_id}', + // style: const TextStyle(fontSize: 18), + // ), + Text( + 'Student Name: ${qrCodeData.preferred_name}', + style: const TextStyle(fontSize: 18), + ), + Text( + 'Check-Out Time: ${qrCodeData.sign_out_time}', + style: const TextStyle(fontSize: 18), + ), + if (markedAttendance) const SizedBox(height: 20), + const Text( + 'Check-Out completed successfully!', + style: TextStyle( + fontSize: 18, + color: Colors.green, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ], + // if (_isCheckedOut && _isCheckedIn) ...[ + // Column( + // mainAxisAlignment: MainAxisAlignment.center, + // children: [ + // const Text('Attendance Marked for Today.'), + // if (_personAttendanceToday.isNotEmpty) + // Text( + // 'Checked in at ${_personAttendanceToday[0].sign_in_time!}'), + // if (_personAttendanceToday.length > 1) + // Text( + // 'Checked out at ${_personAttendanceToday[1].sign_out_time!}'), + // const SizedBox(width: 20), + // ], + // ), + // ], + ...[ + if (qrCodeData.person_id == 0 && isFirstTime) + const Text( + 'Scan QR Code to Mark Attendance', + style: TextStyle(fontSize: 18), + ), + ], + ] else ...[ + const Text( + 'Invalid QR Code', + style: TextStyle(fontSize: 18), + ), + ], + ], + ), + ), + ), + ); + } else if (snapshot.hasError) { + return Text('${snapshot.error}'); + } + + // By default, show a loading spinner. + return const CircularProgressIndicator(); + }, + ); + // + } +} diff --git a/campus/mobile/lib/avinya/attendance/lib/widgets/qr_attedance_marker.dart b/campus/mobile/lib/avinya/attendance/lib/widgets/qr_attedance_marker.dart index 1727830d..6600a901 100644 --- a/campus/mobile/lib/avinya/attendance/lib/widgets/qr_attedance_marker.dart +++ b/campus/mobile/lib/avinya/attendance/lib/widgets/qr_attedance_marker.dart @@ -1,7 +1,12 @@ // ignore_for_file: non_constant_identifier_names, unused_element, use_build_context_synchronously, use_key_in_widget_constructors, library_private_types_in_public_api +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:mobile/avinya/attendance/lib/data/attendance_data.dart'; +import 'package:mobile/avinya/attendance/lib/widgets/qr_attedance_checkin.dart'; +import 'package:mobile/avinya/attendance/lib/widgets/qr_attedance_checkout.dart'; import '../data.dart'; import '../data/activity_attendance.dart'; import 'package:attendance/data/evaluation.dart'; @@ -19,6 +24,8 @@ class _QrAttendanceMarkerState extends State { bool _isCheckedIn = false; bool _isCheckedOut = false; bool _isAbsent = false; + bool markedAttendance = true; + bool inValidQr = false; List _personAttendanceToday = []; List _fechedEvaluations = []; @@ -26,7 +33,23 @@ class _QrAttendanceMarkerState extends State { final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); QRViewController? controller; - String qrCodeData = ""; + AttendanceData qrCodeData = AttendanceData( + activity_instance_id: 0, + person_id: 0, + preferred_name: '', + sign_in_time: '', + sign_out_time: '', + in_marked_by: '', + ); + var activityId = 0; + var afterSchoolActivityId = 0; + @override + void initState() { + super.initState(); + activityId = campusAppsPortalInstance.activityIds['homeroom']!; + afterSchoolActivityId = + campusAppsPortalInstance.activityIds['after-school']!; + } @override void dispose() { @@ -37,34 +60,43 @@ class _QrAttendanceMarkerState extends State { void _onQRViewCreated(QRViewController controller) { this.controller = controller; controller.scannedDataStream.listen((scanData) { - setState(() { - qrCodeData = scanData.code!; - }); + try { + AttendanceData deserializedQrCodeData = + AttendanceData.fromJson(jsonDecode(scanData.code!)); + + setState(() { + qrCodeData = deserializedQrCodeData; + inValidQr = false; + }); + } catch (e) { + setState(() { + inValidQr = true; + }); + } }); } Future _handleCheckIn() async { var activityInstance = await campusAttendanceSystemInstance.getCheckinActivityInstance( - campusAppsPortalInstance.activityIds['school-day']); - // call the API to check-in - await createActivityAttendance(ActivityAttendance( - activity_instance_id: activityInstance.id, - person_id: campusAppsPortalInstance.getUserPerson().id, - sign_in_time: DateTime.now().toString(), - in_marked_by: campusAppsPortalInstance.getUserPerson().digital_id, - )); + campusAppsPortalInstance.activityIds['homeroom']); + + _personAttendanceToday = await getPersonActivityAttendanceToday( + qrCodeData.person_id!, + campusAppsPortalInstance.activityIds['homeroom']!); + if (_personAttendanceToday.isEmpty) { + // call the API to check-in + await createActivityAttendance(ActivityAttendance( + activity_instance_id: activityInstance.id, + person_id: qrCodeData.person_id, + sign_in_time: DateTime.now().toString(), + in_marked_by: campusAppsPortalInstance.getUserPerson().digital_id, + )); + } + await refreshPersonActivityAttendanceToday(); - Navigator.push( - context, - MaterialPageRoute( - builder: ((context) { - return QRImage(sign_in_time); - }), - ), - ); setState(() { - qrCodeData = sign_in_time; + markedAttendance = true; }); print('Checked in for today.'); } @@ -90,8 +122,8 @@ class _QrAttendanceMarkerState extends State { Future> refreshPersonActivityAttendanceToday() async { _personAttendanceToday = await getPersonActivityAttendanceToday( - campusAppsPortalInstance.getUserPerson().id!, - campusAppsPortalInstance.activityIds['school-day']!); + qrCodeData.person_id!, + campusAppsPortalInstance.activityIds['homeroom']!); if (_personAttendanceToday.isNotEmpty) { _isCheckedIn = _personAttendanceToday[0].sign_in_time != null; } @@ -99,24 +131,24 @@ class _QrAttendanceMarkerState extends State { _isCheckedOut = _personAttendanceToday[1].sign_out_time != null; } - if (!_isCheckedIn) { - var activityInstance = - await campusAttendanceSystemInstance.getCheckinActivityInstance( - campusAppsPortalInstance.activityIds['school-day']!); - _fechedEvaluations = - await getActivityInstanceEvaluations(activityInstance.id!); + // if (!_isCheckedIn) { + // var activityInstance = + // await campusAttendanceSystemInstance.getCheckinActivityInstance( + // campusAppsPortalInstance.activityIds['homeroom']!); + // _fechedEvaluations = + // await getActivityInstanceEvaluations(activityInstance.id!); - if (_fechedEvaluations.indexWhere((element) => - element.evaluator_id == - campusAppsPortalInstance.getUserPerson().id!) == - -1) { - _isCheckedIn = false; - } else { - _isCheckedIn = true; - _isCheckedOut = true; - _isAbsent = true; - } - } + // if (_fechedEvaluations.indexWhere((element) => + // element.evaluator_id == + // qrCodeData.person_id!) == + // -1) { + // _isCheckedIn = false; + // } else { + // _isCheckedIn = true; + // _isCheckedOut = true; + // _isAbsent = true; + // } + // } return _personAttendanceToday; } @@ -134,91 +166,40 @@ class _QrAttendanceMarkerState extends State { _isCheckedOut = snapshot.data![1].sign_out_time != null; } return SingleChildScrollView( - child: Column( - children: [ - if (!_isCheckedIn) + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ Row(mainAxisAlignment: MainAxisAlignment.center, children: [ - // ElevatedButton( - // child: Text('Check-In'), - // onPressed: _handleCheckIn, - // style: ButtonStyle( - // // increase the fontSize - // textStyle: MaterialStateProperty.all( - // TextStyle(fontSize: 20), - // ), - // elevation: MaterialStateProperty.all( - // 20), // increase the elevation - // // Add outline around button - // backgroundColor: - // MaterialStateProperty.all(Colors.greenAccent), - // foregroundColor: MaterialStateProperty.all(Colors.black), - // ), - // ), - Expanded( - flex: 5, - child: - QRView(key: qrKey, onQRViewCreated: _onQRViewCreated), + ElevatedButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => QrAttendanceCheckIn()), + ); + }, + style: ButtonStyle( + // increase the fontSize + textStyle: MaterialStateProperty.all( + const TextStyle(fontSize: 20), + ), + elevation: MaterialStateProperty.all( + 20), // increase the elevation + // Add outline around button + backgroundColor: + MaterialStateProperty.all(Colors.greenAccent), + foregroundColor: MaterialStateProperty.all(Colors.black), + ), + child: const Text('Check-In'), ), - Expanded( - flex: 1, - child: Center( - child: Text( - 'Scan Result: $qrCodeData', - style: const TextStyle(fontSize: 18), - ))), - Expanded( - flex: 1, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - ElevatedButton( - onPressed: () { - if (qrCodeData.isNotEmpty) { - Clipboard.setData( - ClipboardData(text: qrCodeData)); - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: - Text('Copied to Clipboard'))); - // _handleCheckIn(); - } - }, - child: const Text('Copy')), - ], - )), const SizedBox(width: 20), ElevatedButton( - onPressed: () async { - var activityInstance = - await campusAttendanceSystemInstance - .getCheckinActivityInstance( - campusAppsPortalInstance - .activityIds['school-day']!); - var evaluation = Evaluation( - evaluator_id: - campusAppsPortalInstance.getUserPerson().id, - evaluatee_id: - campusAppsPortalInstance.getUserPerson().id, - activity_instance_id: activityInstance.id, - grade: 0, - evaluation_criteria_id: 54, - response: "Unexcused absence", - ); - var result = await Navigator.push( + onPressed: () { + Navigator.push( context, MaterialPageRoute( - builder: (context) => AddEvaluationPage( - evaluation: evaluation, - )), + builder: (context) => QrAttendanceCheckOut()), ); - if (result != null) { - await refreshPersonActivityAttendanceToday(); - setState(() {}); - } - // _fetchedEvaluations = - // await getActivityInstanceEvaluations( - // activityInstance.id!); - // setState(() {}); }, style: ButtonStyle( textStyle: MaterialStateProperty.all( @@ -228,72 +209,12 @@ class _QrAttendanceMarkerState extends State { foregroundColor: MaterialStateProperty.all(Colors.white), ), - child: const Text('Absent'), + child: const Text('Check-Out'), ), ]), - if (_isCheckedOut && !_isAbsent) - Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('Attendance marked for today.'), - if (_personAttendanceToday.isNotEmpty) - Text( - 'Checked in at ${_personAttendanceToday[0].sign_in_time!}'), - if (_personAttendanceToday.length > 1) - Text( - 'Checked out at ${_personAttendanceToday[1].sign_out_time!}'), - const SizedBox(width: 20), - ], - ) - else if (_isAbsent) - const Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [Text('Attendance marked as Absent today.')], - ) - else if (_isCheckedIn) - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (_personAttendanceToday.isNotEmpty) - Text( - 'Checked in for today at ${_personAttendanceToday[0].sign_in_time!}'), - const SizedBox(width: 20), - IconButton( - icon: const Icon(Icons.qr_code), - onPressed: () { - // Navigate to the QR code view screen when the button is clicked - Navigator.push( - context, - MaterialPageRoute( - builder: (context) { - return QRImage(qrCodeData); - }, - ), - ); - }, - tooltip: 'View QR Code', - ), - ], - ), - if (_isCheckedIn && !_isCheckedOut) - ElevatedButton( - onPressed: _handleCheckOut, - style: ButtonStyle( - // increase the fontSize - textStyle: MaterialStateProperty.all( - const TextStyle(fontSize: 20), - ), - elevation: - MaterialStateProperty.all(20), // increase the elevation - // Add outline around button - backgroundColor: - MaterialStateProperty.all(Colors.orangeAccent), - foregroundColor: MaterialStateProperty.all(Colors.black), - ), - child: const Text('Check-Out'), - ), - ], - )); + ], + ), + ); } else if (snapshot.hasError) { return Text('${snapshot.error}'); } diff --git a/campus/mobile/lib/avinya/attendance/lib/widgets/qr_attedance_marker_old copy.dart b/campus/mobile/lib/avinya/attendance/lib/widgets/qr_attedance_marker_old copy.dart new file mode 100644 index 00000000..1727830d --- /dev/null +++ b/campus/mobile/lib/avinya/attendance/lib/widgets/qr_attedance_marker_old copy.dart @@ -0,0 +1,307 @@ +// ignore_for_file: non_constant_identifier_names, unused_element, use_build_context_synchronously, use_key_in_widget_constructors, library_private_types_in_public_api + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import '../data.dart'; +import '../data/activity_attendance.dart'; +import 'package:attendance/data/evaluation.dart'; +// import 'package:attendance/widgets/evaluation_list.dart'; +import 'package:mobile/avinya/attendance/lib/widgets/evaluation_list.dart'; +import 'package:mobile/avinya/attendance/lib/widgets/qr_image.dart'; +import 'package:qr_code_scanner/qr_code_scanner.dart'; + +class QrAttendanceMarker extends StatefulWidget { + @override + _QrAttendanceMarkerState createState() => _QrAttendanceMarkerState(); +} + +class _QrAttendanceMarkerState extends State { + bool _isCheckedIn = false; + bool _isCheckedOut = false; + bool _isAbsent = false; + List _personAttendanceToday = []; + List _fechedEvaluations = []; + + String sign_in_time = "ee"; + + final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); + QRViewController? controller; + String qrCodeData = ""; + + @override + void dispose() { + controller?.dispose(); + super.dispose(); + } + + void _onQRViewCreated(QRViewController controller) { + this.controller = controller; + controller.scannedDataStream.listen((scanData) { + setState(() { + qrCodeData = scanData.code!; + }); + }); + } + + Future _handleCheckIn() async { + var activityInstance = + await campusAttendanceSystemInstance.getCheckinActivityInstance( + campusAppsPortalInstance.activityIds['school-day']); + // call the API to check-in + await createActivityAttendance(ActivityAttendance( + activity_instance_id: activityInstance.id, + person_id: campusAppsPortalInstance.getUserPerson().id, + sign_in_time: DateTime.now().toString(), + in_marked_by: campusAppsPortalInstance.getUserPerson().digital_id, + )); + await refreshPersonActivityAttendanceToday(); + Navigator.push( + context, + MaterialPageRoute( + builder: ((context) { + return QRImage(sign_in_time); + }), + ), + ); + setState(() { + qrCodeData = sign_in_time; + }); + print('Checked in for today.'); + } + + Future _handleCheckOut() async { + var activityInstance = + await campusAttendanceSystemInstance.getCheckoutActivityInstance( + campusAppsPortalInstance.activityIds['school-day']); + // call the API to check-out + await createActivityAttendance(ActivityAttendance( + activity_instance_id: activityInstance.id, + person_id: campusAppsPortalInstance.getUserPerson().id, + sign_out_time: DateTime.now().toString(), + out_marked_by: campusAppsPortalInstance.getUserPerson().digital_id, + )); + await refreshPersonActivityAttendanceToday(); + setState(() { + //_isCheckedOut = true; + }); + print('Checked out for today.'); + } + + Future> + refreshPersonActivityAttendanceToday() async { + _personAttendanceToday = await getPersonActivityAttendanceToday( + campusAppsPortalInstance.getUserPerson().id!, + campusAppsPortalInstance.activityIds['school-day']!); + if (_personAttendanceToday.isNotEmpty) { + _isCheckedIn = _personAttendanceToday[0].sign_in_time != null; + } + if (_personAttendanceToday.length > 1) { + _isCheckedOut = _personAttendanceToday[1].sign_out_time != null; + } + + if (!_isCheckedIn) { + var activityInstance = + await campusAttendanceSystemInstance.getCheckinActivityInstance( + campusAppsPortalInstance.activityIds['school-day']!); + _fechedEvaluations = + await getActivityInstanceEvaluations(activityInstance.id!); + + if (_fechedEvaluations.indexWhere((element) => + element.evaluator_id == + campusAppsPortalInstance.getUserPerson().id!) == + -1) { + _isCheckedIn = false; + } else { + _isCheckedIn = true; + _isCheckedOut = true; + _isAbsent = true; + } + } + + return _personAttendanceToday; + } + + @override + Widget build(BuildContext context) { + return FutureBuilder>( + future: refreshPersonActivityAttendanceToday(), + builder: (context, snapshot) { + if (snapshot.hasData) { + if (snapshot.data!.isNotEmpty) { + _isCheckedIn = snapshot.data![0].sign_in_time != null; + } + if (snapshot.data!.length > 1) { + _isCheckedOut = snapshot.data![1].sign_out_time != null; + } + return SingleChildScrollView( + child: Column( + children: [ + if (!_isCheckedIn) + Row(mainAxisAlignment: MainAxisAlignment.center, children: [ + // ElevatedButton( + // child: Text('Check-In'), + // onPressed: _handleCheckIn, + // style: ButtonStyle( + // // increase the fontSize + // textStyle: MaterialStateProperty.all( + // TextStyle(fontSize: 20), + // ), + // elevation: MaterialStateProperty.all( + // 20), // increase the elevation + // // Add outline around button + // backgroundColor: + // MaterialStateProperty.all(Colors.greenAccent), + // foregroundColor: MaterialStateProperty.all(Colors.black), + // ), + // ), + Expanded( + flex: 5, + child: + QRView(key: qrKey, onQRViewCreated: _onQRViewCreated), + ), + Expanded( + flex: 1, + child: Center( + child: Text( + 'Scan Result: $qrCodeData', + style: const TextStyle(fontSize: 18), + ))), + Expanded( + flex: 1, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + ElevatedButton( + onPressed: () { + if (qrCodeData.isNotEmpty) { + Clipboard.setData( + ClipboardData(text: qrCodeData)); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: + Text('Copied to Clipboard'))); + // _handleCheckIn(); + } + }, + child: const Text('Copy')), + ], + )), + const SizedBox(width: 20), + ElevatedButton( + onPressed: () async { + var activityInstance = + await campusAttendanceSystemInstance + .getCheckinActivityInstance( + campusAppsPortalInstance + .activityIds['school-day']!); + var evaluation = Evaluation( + evaluator_id: + campusAppsPortalInstance.getUserPerson().id, + evaluatee_id: + campusAppsPortalInstance.getUserPerson().id, + activity_instance_id: activityInstance.id, + grade: 0, + evaluation_criteria_id: 54, + response: "Unexcused absence", + ); + var result = await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => AddEvaluationPage( + evaluation: evaluation, + )), + ); + if (result != null) { + await refreshPersonActivityAttendanceToday(); + setState(() {}); + } + // _fetchedEvaluations = + // await getActivityInstanceEvaluations( + // activityInstance.id!); + // setState(() {}); + }, + style: ButtonStyle( + textStyle: MaterialStateProperty.all( + const TextStyle(fontSize: 20)), + backgroundColor: + MaterialStateProperty.all(Colors.blue), + foregroundColor: + MaterialStateProperty.all(Colors.white), + ), + child: const Text('Absent'), + ), + ]), + if (_isCheckedOut && !_isAbsent) + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text('Attendance marked for today.'), + if (_personAttendanceToday.isNotEmpty) + Text( + 'Checked in at ${_personAttendanceToday[0].sign_in_time!}'), + if (_personAttendanceToday.length > 1) + Text( + 'Checked out at ${_personAttendanceToday[1].sign_out_time!}'), + const SizedBox(width: 20), + ], + ) + else if (_isAbsent) + const Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [Text('Attendance marked as Absent today.')], + ) + else if (_isCheckedIn) + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (_personAttendanceToday.isNotEmpty) + Text( + 'Checked in for today at ${_personAttendanceToday[0].sign_in_time!}'), + const SizedBox(width: 20), + IconButton( + icon: const Icon(Icons.qr_code), + onPressed: () { + // Navigate to the QR code view screen when the button is clicked + Navigator.push( + context, + MaterialPageRoute( + builder: (context) { + return QRImage(qrCodeData); + }, + ), + ); + }, + tooltip: 'View QR Code', + ), + ], + ), + if (_isCheckedIn && !_isCheckedOut) + ElevatedButton( + onPressed: _handleCheckOut, + style: ButtonStyle( + // increase the fontSize + textStyle: MaterialStateProperty.all( + const TextStyle(fontSize: 20), + ), + elevation: + MaterialStateProperty.all(20), // increase the elevation + // Add outline around button + backgroundColor: + MaterialStateProperty.all(Colors.orangeAccent), + foregroundColor: MaterialStateProperty.all(Colors.black), + ), + child: const Text('Check-Out'), + ), + ], + )); + } else if (snapshot.hasError) { + return Text('${snapshot.error}'); + } + + // By default, show a loading spinner. + return const CircularProgressIndicator(); + }, + ); + // + } +} diff --git a/campus/mobile/lib/avinya/attendance/lib/widgets/qr_image.dart b/campus/mobile/lib/avinya/attendance/lib/widgets/qr_image.dart index e0cf637e..f939747e 100644 --- a/campus/mobile/lib/avinya/attendance/lib/widgets/qr_image.dart +++ b/campus/mobile/lib/avinya/attendance/lib/widgets/qr_image.dart @@ -2,9 +2,9 @@ import 'package:flutter/material.dart'; import 'package:qr_flutter/qr_flutter.dart'; class QRImage extends StatelessWidget { - final String sign_in_time; + final String data; - const QRImage(this.sign_in_time, {Key? key}) : super(key: key); + const QRImage(this.data, {Key? key}) : super(key: key); @override Widget build(BuildContext context) { @@ -15,8 +15,7 @@ class QRImage extends StatelessWidget { ), body: Center( child: QrImage( - data: - sign_in_time, // Use the sign_in_time string directly as the data. + data: data, // Use the sign_in_time string directly as the data. size: 280, // You can include embeddedImageStyle Property if you // wanna embed an image from your Asset folder diff --git a/campus/mobile/lib/main.dart b/campus/mobile/lib/main.dart index 6fbfc05b..310c09cb 100644 --- a/campus/mobile/lib/main.dart +++ b/campus/mobile/lib/main.dart @@ -6,6 +6,7 @@ import 'dart:developer'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart' show timeDilation; +import 'package:flutter_appauth/flutter_appauth.dart'; import 'package:mobile/auth.dart'; import 'package:mobile/constants.dart'; import 'package:mobile/data/campus_apps_portal.dart'; @@ -14,6 +15,7 @@ import 'package:mobile/pages/backdrop.dart'; import 'package:mobile/pages/splash.dart'; import 'package:mobile/routes.dart'; import 'package:mobile/themes/gallery_theme_data.dart'; +import 'package:shared_preferences/shared_preferences.dart'; // import 'package:google_fonts/google_fonts.dart' as google_fonts; import 'package:sizer/sizer.dart'; import 'package:url_strategy/url_strategy.dart'; @@ -53,6 +55,23 @@ void main() async { await AppConfig.forEnvironment('dev'); } + String _clientId = AppConfig.asgardeoClientId; + final String _issuerUrl = AppConfig.asgardeoTokenEndpoint; + final String _redirectUrl = AppConfig.redirectURL; + final String _discoveryUrl = AppConfig.asgardeoDiscoveryURL; + + final List _scopes = [ + 'openid', + 'profile', + 'email', + 'groups', + 'address', + 'phone' + ]; + + await authenticate( + Uri.parse(_issuerUrl), _clientId, _scopes, _redirectUrl, _discoveryUrl); + // google_fonts.GoogleFonts.config.allowRuntimeFetching = false; GalleryApp galleryApp = GalleryApp(); campusAppsPortalInstance.setAuth(galleryApp._auth); @@ -82,6 +101,48 @@ abstract class Constants { ); } +Future authenticate(Uri uri, String clientId, List scopes, + String redirectURL, String discoveryUrl) async { + const FlutterAppAuth flutterAppAuth = FlutterAppAuth(); + + try { + final AuthorizationTokenResponse? result = + await flutterAppAuth.authorizeAndExchangeCode( + AuthorizationTokenRequest( + clientId, + redirectURL, + discoveryUrl: discoveryUrl, + promptValues: ['login'], + scopes: scopes, + ), + ); + + print('Access token bla bla bla: ${result?.accessToken}'); + String accessToken = result?.accessToken ?? ''; + String refreshToken = result?.refreshToken ?? ''; + String idToken = result?.idToken ?? ''; + SharedPreferences prefs = await SharedPreferences.getInstance(); + await prefs.setString('access_token', accessToken); + await prefs.setString('refresh_token', refreshToken); + await prefs.setString('id_token', idToken); + + // final _auth = CampusAppsPortalAuth(); + // final signedIn = await _auth.getSignedIn(); + + // setState(() { + // _isUserLoggedIn = true; + // _idToken = result?.idToken; + // _accessToken = result?.accessToken; + // _pageIndex = 2; + // }); + } catch (e, s) { + print('Error while login to the system: $e - stack: $s'); + // setState(() { + // _isUserLoggedIn = false; + // }); + } +} + class GalleryApp extends StatefulWidget { // GalleryApp({super.key}); GalleryApp({ diff --git a/campus/mobile/lib/pages/backdrop.dart b/campus/mobile/lib/pages/backdrop.dart index 0004c645..2e20c422 100644 --- a/campus/mobile/lib/pages/backdrop.dart +++ b/campus/mobile/lib/pages/backdrop.dart @@ -7,7 +7,9 @@ import 'dart:developer'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_appauth/flutter_appauth.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; +import 'package:mobile/auth.dart'; import 'package:mobile/constants.dart'; import 'package:mobile/data/gallery_options.dart'; import 'package:mobile/layout/adaptive.dart'; @@ -15,6 +17,7 @@ import 'package:mobile/pages/home.dart'; import 'package:mobile/pages/login.dart'; import 'package:mobile/pages/settings.dart'; import 'package:mobile/pages/settings_icon/icon.dart' as settings_icon; +import 'package:shared_preferences/shared_preferences.dart'; import '../data/campus_apps_portal.dart'; import 'dart:async'; @@ -37,6 +40,7 @@ RouteObserver routeObserver = RouteObserver(); class _BackdropState extends State with TickerProviderStateMixin, RouteAware { + bool signedIn = false; @override void didChangeDependencies() async { super.didChangeDependencies(); diff --git a/campus/mobile/lib/pages/login.dart b/campus/mobile/lib/pages/login.dart index 025b33a3..80fa48f3 100644 --- a/campus/mobile/lib/pages/login.dart +++ b/campus/mobile/lib/pages/login.dart @@ -1,10 +1,12 @@ import 'dart:developer'; import 'dart:io'; +import 'package:mobile/auth.dart'; import 'package:mobile/config/app_config.dart'; import 'package:flutter/material.dart'; // import 'package:openid_client/openid_client_browser.dart'; import 'package:flutter_appauth/flutter_appauth.dart'; +import 'package:mobile/data/campus_apps_portal.dart'; import 'package:shared_preferences/shared_preferences.dart'; const FlutterAppAuth flutterAppAuth = FlutterAppAuth(); @@ -205,6 +207,9 @@ class _LogInScreenState extends State { await prefs.setString('refresh_token', refreshToken); await prefs.setString('id_token', idToken); + // final _auth = CampusAppsPortalAuth(); + // final signedIn = await _auth.getSignedIn(); + // setState(() { // _isUserLoggedIn = true; // _idToken = result?.idToken;