diff --git a/lib/helpers/utils/base_bottom_sheet.dart b/lib/helpers/utils/base_bottom_sheet.dart index b7ed51c..485f0dd 100644 --- a/lib/helpers/utils/base_bottom_sheet.dart +++ b/lib/helpers/utils/base_bottom_sheet.dart @@ -11,6 +11,7 @@ class BaseBottomSheet extends StatelessWidget { final String submitText; final Color submitColor; final IconData submitIcon; + final bool showButtons; const BaseBottomSheet({ super.key, @@ -22,6 +23,7 @@ class BaseBottomSheet extends StatelessWidget { this.submitText = 'Submit', this.submitColor = Colors.indigo, this.submitIcon = Icons.check_circle_outline, + this.showButtons = true, }); @override @@ -32,8 +34,7 @@ class BaseBottomSheet extends StatelessWidget { return SingleChildScrollView( padding: mediaQuery.viewInsets, child: Padding( - padding: const EdgeInsets.only( - top: 60), + padding: const EdgeInsets.only(top: 60), child: Container( decoration: BoxDecoration( color: theme.cardColor, @@ -65,49 +66,48 @@ class BaseBottomSheet extends StatelessWidget { MySpacing.height(12), child, MySpacing.height(24), - Row( - children: [ - Expanded( - child: ElevatedButton.icon( - onPressed: onCancel, - icon: const Icon(Icons.close, color: Colors.white), - label: MyText.bodyMedium( - "Cancel", - color: Colors.white, - fontWeight: 600, - ), - style: ElevatedButton.styleFrom( - backgroundColor: Colors.grey, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), + if (showButtons) + Row( + children: [ + Expanded( + child: ElevatedButton.icon( + onPressed: onCancel, + icon: const Icon(Icons.close, color: Colors.white), + label: MyText.bodyMedium( + "Cancel", + color: Colors.white, + fontWeight: 600, + ), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.grey, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + padding: const EdgeInsets.symmetric(vertical: 8), ), - padding: const EdgeInsets.symmetric( - vertical: 8), ), ), - ), - const SizedBox(width: 12), - Expanded( - child: ElevatedButton.icon( - onPressed: isSubmitting ? null : onSubmit, - icon: Icon(submitIcon, color: Colors.white), - label: MyText.bodyMedium( - isSubmitting ? "Submitting..." : submitText, - color: Colors.white, - fontWeight: 600, - ), - style: ElevatedButton.styleFrom( - backgroundColor: submitColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), + const SizedBox(width: 12), + Expanded( + child: ElevatedButton.icon( + onPressed: isSubmitting ? null : onSubmit, + icon: Icon(submitIcon, color: Colors.white), + label: MyText.bodyMedium( + isSubmitting ? "Submitting..." : submitText, + color: Colors.white, + fontWeight: 600, + ), + style: ElevatedButton.styleFrom( + backgroundColor: submitColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + padding: const EdgeInsets.symmetric(vertical: 8), ), - padding: const EdgeInsets.symmetric( - vertical: 8), ), ), - ), - ], - ), + ], + ), ], ), ), diff --git a/lib/model/attendance/attendence_action_button.dart b/lib/model/attendance/attendence_action_button.dart index 08b725e..90d263d 100644 --- a/lib/model/attendance/attendence_action_button.dart +++ b/lib/model/attendance/attendence_action_button.dart @@ -5,16 +5,17 @@ import 'package:marco/helpers/widgets/my_snackbar.dart'; import 'package:marco/controller/dashboard/attendance_screen_controller.dart'; import 'package:marco/helpers/utils/attendance_actions.dart'; import 'package:marco/controller/project_controller.dart'; +import 'package:marco/helpers/utils/base_bottom_sheet.dart'; class AttendanceActionButton extends StatefulWidget { final dynamic employee; final AttendanceController attendanceController; const AttendanceActionButton({ - Key? key, + super.key, required this.employee, required this.attendanceController, - }) : super(key: key); + }); @override State createState() => _AttendanceActionButtonState(); @@ -24,81 +25,59 @@ Future _showCommentBottomSheet( BuildContext context, String actionText) async { final TextEditingController commentController = TextEditingController(); String? errorText; - Get.find().selectedProject?.id; + return showModalBottomSheet( context: context, isScrollControlled: true, - backgroundColor: Colors.white, + backgroundColor: Colors.transparent, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), builder: (context) { return StatefulBuilder( builder: (context, setModalState) { + void submit() { + final comment = commentController.text.trim(); + if (comment.isEmpty) { + setModalState(() => errorText = 'Comment cannot be empty.'); + return; + } + Navigator.of(context).pop(comment); + } + return Padding( padding: EdgeInsets.only( - left: 16, - right: 16, - top: 24, - bottom: MediaQuery.of(context).viewInsets.bottom + 24, + bottom: MediaQuery.of(context).viewInsets.bottom, ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - 'Add Comment for ${capitalizeFirstLetter(actionText)}', - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - const SizedBox(height: 16), - TextField( - controller: commentController, - maxLines: 4, - decoration: InputDecoration( - hintText: 'Type your comment here...', - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), - ), - filled: true, - fillColor: Colors.grey.shade100, - errorText: errorText, - ), - onChanged: (_) { - if (errorText != null) { - setModalState(() => errorText = null); - } - }, - ), - const SizedBox(height: 16), - Row( - children: [ - Expanded( - child: OutlinedButton( - onPressed: () => Navigator.of(context).pop(), - child: const Text('Cancel'), + child: BaseBottomSheet( + title: 'Add Comment for ${capitalizeFirstLetter(actionText)}', + onCancel: () => Navigator.of(context).pop(), + onSubmit: submit, + isSubmitting: false, + submitText: 'Submit', + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + controller: commentController, + maxLines: 4, + decoration: InputDecoration( + hintText: 'Type your comment here...', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), ), + filled: true, + fillColor: Colors.grey.shade100, + errorText: errorText, ), - const SizedBox(width: 12), - Expanded( - child: ElevatedButton( - onPressed: () { - final comment = commentController.text.trim(); - if (comment.isEmpty) { - setModalState(() { - errorText = 'Comment cannot be empty.'; - }); - return; - } - Navigator.of(context).pop(comment); - }, - child: const Text('Submit'), - ), - ), - ], - ), - ], + onChanged: (_) { + if (errorText != null) { + setModalState(() => errorText = null); + } + }, + ), + ], + ), ), ); }, @@ -119,13 +98,15 @@ class _AttendanceActionButtonState extends State { void initState() { super.initState(); uniqueLogKey = AttendanceButtonHelper.getUniqueKey( - widget.employee.employeeId, widget.employee.id); + widget.employee.employeeId, + widget.employee.id, + ); WidgetsBinding.instance.addPostFrameCallback((_) { - if (!widget.attendanceController.uploadingStates - .containsKey(uniqueLogKey)) { - widget.attendanceController.uploadingStates[uniqueLogKey] = false.obs; - } + widget.attendanceController.uploadingStates.putIfAbsent( + uniqueLogKey, + () => false.obs, + ); }); } @@ -167,6 +148,7 @@ class _AttendanceActionButtonState extends State { return selectedDateTime; } + return null; } @@ -228,7 +210,6 @@ class _AttendanceActionButtonState extends State { DateTime? selectedTime; - // ✅ New condition: Yesterday Check-In + CheckOut action final isYesterdayCheckIn = widget.employee.checkIn != null && DateUtils.isSameDay( widget.employee.checkIn, @@ -257,15 +238,16 @@ class _AttendanceActionButtonState extends State { } bool success = false; + if (actionText == ButtonActions.requestRegularize) { final regularizeTime = selectedTime ?? await showTimePickerForRegularization( context: context, checkInTime: widget.employee.checkIn!, ); + if (regularizeTime != null) { - final formattedSelectedTime = - DateFormat("hh:mm a").format(regularizeTime); + final formattedTime = DateFormat("hh:mm a").format(regularizeTime); success = await widget.attendanceController.captureAndUploadAttendance( widget.employee.id, widget.employee.employeeId, @@ -273,12 +255,11 @@ class _AttendanceActionButtonState extends State { comment: userComment, action: updatedAction, imageCapture: imageCapture, - markTime: formattedSelectedTime, + markTime: formattedTime, ); } } else if (selectedTime != null) { - // ✅ If selectedTime was picked in the new condition - final formattedSelectedTime = DateFormat("hh:mm a").format(selectedTime); + final formattedTime = DateFormat("hh:mm a").format(selectedTime); success = await widget.attendanceController.captureAndUploadAttendance( widget.employee.id, widget.employee.employeeId, @@ -286,7 +267,7 @@ class _AttendanceActionButtonState extends State { comment: userComment, action: updatedAction, imageCapture: imageCapture, - markTime: formattedSelectedTime, + markTime: formattedTime, ); } else { success = await widget.attendanceController.captureAndUploadAttendance( @@ -312,8 +293,7 @@ class _AttendanceActionButtonState extends State { if (success) { widget.attendanceController.fetchEmployeesByProject(selectedProjectId); widget.attendanceController.fetchAttendanceLogs(selectedProjectId); - await widget.attendanceController - .fetchRegularizationLogs(selectedProjectId); + await widget.attendanceController.fetchRegularizationLogs(selectedProjectId); await widget.attendanceController.fetchProjectData(selectedProjectId); widget.attendanceController.update(); } @@ -327,12 +307,19 @@ class _AttendanceActionButtonState extends State { false; final isYesterday = AttendanceButtonHelper.isLogFromYesterday( - widget.employee.checkIn, widget.employee.checkOut); + widget.employee.checkIn, + widget.employee.checkOut, + ); + final isTodayApproved = AttendanceButtonHelper.isTodayApproved( - widget.employee.activity, widget.employee.checkIn); - final isApprovedButNotToday = - AttendanceButtonHelper.isApprovedButNotToday( - widget.employee.activity, isTodayApproved); + widget.employee.activity, + widget.employee.checkIn, + ); + + final isApprovedButNotToday = AttendanceButtonHelper.isApprovedButNotToday( + widget.employee.activity, + isTodayApproved, + ); final isButtonDisabled = AttendanceButtonHelper.isButtonDisabled( isUploading: isUploading, @@ -359,8 +346,7 @@ class _AttendanceActionButtonState extends State { isButtonDisabled: isButtonDisabled, buttonText: buttonText, buttonColor: buttonColor, - onPressed: - isButtonDisabled ? null : () => _handleButtonPressed(context), + onPressed: isButtonDisabled ? null : () => _handleButtonPressed(context), ); }); } @@ -374,20 +360,20 @@ class AttendanceActionButtonUI extends StatelessWidget { final VoidCallback? onPressed; const AttendanceActionButtonUI({ - Key? key, + super.key, required this.isUploading, required this.isButtonDisabled, required this.buttonText, required this.buttonColor, required this.onPressed, - }) : super(key: key); + }); @override Widget build(BuildContext context) { return SizedBox( height: 30, child: ElevatedButton( - onPressed: isButtonDisabled ? null : onPressed, + onPressed: onPressed, style: ElevatedButton.styleFrom( backgroundColor: buttonColor, padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 12), diff --git a/lib/model/attendance/attendence_filter_sheet.dart b/lib/model/attendance/attendence_filter_sheet.dart index e51f5d9..4b11265 100644 --- a/lib/model/attendance/attendence_filter_sheet.dart +++ b/lib/model/attendance/attendence_filter_sheet.dart @@ -4,6 +4,7 @@ import 'package:marco/controller/dashboard/attendance_screen_controller.dart'; import 'package:intl/intl.dart'; import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/helpers/utils/permission_constants.dart'; +import 'package:marco/helpers/utils/base_bottom_sheet.dart'; class AttendanceFilterBottomSheet extends StatefulWidget { final AttendanceController controller; @@ -62,20 +63,20 @@ class _AttendanceFilterBottomSheetState List widgets = [ Padding( - padding: const EdgeInsets.fromLTRB(16, 12, 16, 4), + padding: const EdgeInsets.only(bottom: 4), child: Align( alignment: Alignment.centerLeft, - child: MyText.titleSmall( - "View", - fontWeight: 600, - ), + child: MyText.titleSmall("View", fontWeight: 600), ), ), ...filteredViewOptions.map((item) { return RadioListTile( dense: true, - contentPadding: const EdgeInsets.symmetric(horizontal: 12), - title: Text(item['label']!), + contentPadding: EdgeInsets.zero, + title: MyText.bodyMedium( + item['label']!, + fontWeight: 500, + ), value: item['value']!, groupValue: tempSelectedTab, onChanged: (value) => setState(() => tempSelectedTab = value!), @@ -87,49 +88,38 @@ class _AttendanceFilterBottomSheetState widgets.addAll([ const Divider(), Padding( - padding: const EdgeInsets.fromLTRB(16, 12, 16, 4), + padding: const EdgeInsets.only(top: 12, bottom: 4), child: Align( alignment: Alignment.centerLeft, - child: MyText.titleSmall( - "Date Range", - fontWeight: 600, - ), + child: MyText.titleSmall("Date Range", fontWeight: 600), ), ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - child: InkWell( - borderRadius: BorderRadius.circular(10), - onTap: () => widget.controller.selectDateRangeForAttendance( - context, - widget.controller, + InkWell( + borderRadius: BorderRadius.circular(10), + onTap: () => widget.controller.selectDateRangeForAttendance( + context, + widget.controller, + ), + child: Ink( + decoration: BoxDecoration( + color: Colors.white, + border: Border.all(color: Colors.grey.shade400), + borderRadius: BorderRadius.circular(10), ), - child: Ink( - decoration: BoxDecoration( - color: Colors.white, - border: Border.all(color: Colors.grey.shade400), - borderRadius: BorderRadius.circular(10), - ), - padding: - const EdgeInsets.symmetric(horizontal: 16, vertical: 14), - child: Row( - children: [ - Icon(Icons.date_range, color: Colors.black87), - const SizedBox(width: 12), - Expanded( - child: Text( - getLabelText(), - style: const TextStyle( - fontSize: 16, - color: Colors.black87, - fontWeight: FontWeight.w500, - ), - overflow: TextOverflow.ellipsis, - ), + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), + child: Row( + children: [ + const Icon(Icons.date_range, color: Colors.black87), + const SizedBox(width: 12), + Expanded( + child: MyText.bodyMedium( + getLabelText(), + fontWeight: 500, + color: Colors.black87, ), - const Icon(Icons.arrow_drop_down, color: Colors.black87), - ], - ), + ), + const Icon(Icons.arrow_drop_down, color: Colors.black87), + ], ), ), ), @@ -141,49 +131,20 @@ class _AttendanceFilterBottomSheetState @override Widget build(BuildContext context) { - return Padding( - padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), - child: SingleChildScrollView( + return ClipRRect( + borderRadius: const BorderRadius.vertical(top: Radius.circular(24)), + child: BaseBottomSheet( + title: "Attendance Filter", + onCancel: () => Navigator.pop(context), + onSubmit: () => Navigator.pop(context, { + 'selectedTab': tempSelectedTab, + }), + submitText: "Apply Filter", + submitIcon: Icons.filter_alt_outlined, + submitColor: const Color.fromARGB(255, 95, 132, 255), child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.only(top: 12, bottom: 8), - child: Center( - child: Container( - width: 40, - height: 4, - decoration: BoxDecoration( - color: Colors.grey[400], - borderRadius: BorderRadius.circular(4), - ), - ), - ), - ), - ...buildMainFilters(), - const Divider(), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), - child: SizedBox( - width: double.infinity, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: const Color.fromARGB(255, 95, 132, 255), - padding: const EdgeInsets.symmetric(vertical: 12), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - ), - child: const Text('Apply Filter'), - onPressed: () { - Navigator.pop(context, { - 'selectedTab': tempSelectedTab, - }); - }, - ), - ), - ), - ], + crossAxisAlignment: CrossAxisAlignment.start, + children: buildMainFilters(), ), ), ); diff --git a/lib/model/attendance/log_details_view.dart b/lib/model/attendance/log_details_view.dart index deb0331..b2ad720 100644 --- a/lib/model/attendance/log_details_view.dart +++ b/lib/model/attendance/log_details_view.dart @@ -2,11 +2,11 @@ import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/helpers/utils/attendance_actions.dart'; +import 'package:marco/helpers/utils/base_bottom_sheet.dart'; class AttendanceLogViewButton extends StatelessWidget { final dynamic employee; - final dynamic attendanceController; // Use correct types as needed - + final dynamic attendanceController; const AttendanceLogViewButton({ Key? key, required this.employee, @@ -50,191 +50,164 @@ class AttendanceLogViewButton extends StatelessWidget { void _showLogsBottomSheet(BuildContext context) async { await attendanceController.fetchLogsView(employee.id.toString()); + showModalBottomSheet( context: context, isScrollControlled: true, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), - backgroundColor: Theme.of(context).cardColor, - builder: (context) => Padding( - padding: EdgeInsets.only( - left: 16, - right: 16, - top: 16, - bottom: MediaQuery.of(context).viewInsets.bottom + 16, - ), - child: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - // Header - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - MyText.titleMedium( - "Attendance Log", - fontWeight: 700, - ), - IconButton( - icon: const Icon(Icons.close), - onPressed: () => Navigator.pop(context), - ), - ], - ), - const SizedBox(height: 12), - if (attendanceController.attendenceLogsView.isEmpty) - Padding( - padding: const EdgeInsets.symmetric(vertical: 24.0), - child: Column( - children: const [ - Icon(Icons.info_outline, size: 40, color: Colors.grey), - SizedBox(height: 8), - Text("No attendance logs available."), - ], - ), - ) - else - ListView.separated( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: attendanceController.attendenceLogsView.length, - separatorBuilder: (_, __) => const SizedBox(height: 16), - itemBuilder: (_, index) { - final log = attendanceController.attendenceLogsView[index]; - return Container( - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surfaceVariant, - borderRadius: BorderRadius.circular(12), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.05), - blurRadius: 6, - offset: const Offset(0, 2), - ) - ], - ), - padding: const EdgeInsets.all(8), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - flex: 3, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - _getLogIcon(log), - const SizedBox(width: 10), - Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - MyText.bodyLarge( - log.formattedDate ?? '-', - fontWeight: 600, - ), - MyText.bodySmall( - "Time: ${log.formattedTime ?? '-'}", - color: Colors.grey[700], - ), - ], - ), - ], - ), - const SizedBox(height: 12), - Row( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - if (log.latitude != null && - log.longitude != null) - GestureDetector( - onTap: () { - final lat = double.tryParse(log - .latitude - .toString()) ?? - 0.0; - final lon = double.tryParse(log - .longitude - .toString()) ?? - 0.0; - if (lat >= -90 && - lat <= 90 && - lon >= -180 && - lon <= 180) { - _openGoogleMaps( - context, lat, lon); - } else { - ScaffoldMessenger.of(context) - .showSnackBar( - const SnackBar( - content: Text( - 'Invalid location coordinates')), - ); - } - }, - child: const Padding( - padding: - EdgeInsets.only(right: 8.0), - child: Icon(Icons.location_on, - size: 18, color: Colors.blue), - ), + backgroundColor: Colors.transparent, + builder: (context) => BaseBottomSheet( + title: "Attendance Log", + onCancel: () => Navigator.pop(context), + onSubmit: () => Navigator.pop(context), + showButtons: false, + child: attendanceController.attendenceLogsView.isEmpty + ? Padding( + padding: const EdgeInsets.symmetric(vertical: 24.0), + child: Column( + children: const [ + Icon(Icons.info_outline, size: 40, color: Colors.grey), + SizedBox(height: 8), + Text("No attendance logs available."), + ], + ), + ) + : ListView.separated( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: attendanceController.attendenceLogsView.length, + separatorBuilder: (_, __) => const SizedBox(height: 16), + itemBuilder: (_, index) { + final log = attendanceController.attendenceLogsView[index]; + return Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surfaceVariant, + borderRadius: BorderRadius.circular(12), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.05), + blurRadius: 6, + offset: const Offset(0, 2), + ) + ], + ), + padding: const EdgeInsets.all(8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + flex: 3, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + _getLogIcon(log), + const SizedBox(width: 10), + Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + MyText.bodyLarge( + log.formattedDate ?? '-', + fontWeight: 600, ), - Expanded( - child: MyText.bodyMedium( - log.comment?.isNotEmpty == true - ? log.comment - : "No description provided", - fontWeight: 500, + MyText.bodySmall( + "Time: ${log.formattedTime ?? '-'}", + color: Colors.grey[700], ), - ), - ], - ), - ], - ), - ), - const SizedBox(width: 16), - if (log.thumbPreSignedUrl != null) - GestureDetector( - onTap: () { - if (log.preSignedUrl != null) { - _showImageDialog( - context, log.preSignedUrl!); - } - }, - child: ClipRRect( - borderRadius: BorderRadius.circular(8), - child: Image.network( - log.thumbPreSignedUrl!, - height: 60, - width: 60, - fit: BoxFit.cover, - errorBuilder: - (context, error, stackTrace) { - return const Icon(Icons.broken_image, - size: 20, color: Colors.grey); - }, - ), + ], + ), + ], ), - ) - else - const Icon(Icons.broken_image, - size: 20, color: Colors.grey), - ], - ), - ], - ), - ); - }, - ) - ], - ), - ), + const SizedBox(height: 12), + Row( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + if (log.latitude != null && + log.longitude != null) + GestureDetector( + onTap: () { + final lat = double.tryParse( + log.latitude.toString()) ?? + 0.0; + final lon = double.tryParse( + log.longitude.toString()) ?? + 0.0; + if (lat >= -90 && + lat <= 90 && + lon >= -180 && + lon <= 180) { + _openGoogleMaps( + context, lat, lon); + } else { + ScaffoldMessenger.of(context) + .showSnackBar( + const SnackBar( + content: Text( + 'Invalid location coordinates')), + ); + } + }, + child: const Padding( + padding: + EdgeInsets.only(right: 8.0), + child: Icon(Icons.location_on, + size: 18, color: Colors.blue), + ), + ), + Expanded( + child: MyText.bodyMedium( + log.comment?.isNotEmpty == true + ? log.comment + : "No description provided", + fontWeight: 500, + ), + ), + ], + ), + ], + ), + ), + const SizedBox(width: 16), + if (log.thumbPreSignedUrl != null) + GestureDetector( + onTap: () { + if (log.preSignedUrl != null) { + _showImageDialog( + context, log.preSignedUrl!); + } + }, + child: ClipRRect( + borderRadius: BorderRadius.circular(8), + child: Image.network( + log.thumbPreSignedUrl!, + height: 60, + width: 60, + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + return const Icon(Icons.broken_image, + size: 20, color: Colors.grey); + }, + ), + ), + ) + else + const Icon(Icons.broken_image, + size: 20, color: Colors.grey), + ], + ), + ], + ), + ); + }, + ), ), ); } diff --git a/lib/view/dashboard/Attendence/attendance_screen.dart b/lib/view/dashboard/Attendence/attendance_screen.dart index 1f019ef..f2fcda7 100644 --- a/lib/view/dashboard/Attendence/attendance_screen.dart +++ b/lib/view/dashboard/Attendence/attendance_screen.dart @@ -157,7 +157,8 @@ class _AttendanceScreenState extends State with UIMixin { Map>( context: context, isScrollControlled: true, - backgroundColor: Colors.white, + + backgroundColor: Colors.transparent, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical( top: Radius.circular(12)),