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/base_bottom_sheet.dart'; class AttendanceLogViewButton extends StatelessWidget { final dynamic employee; final dynamic attendanceController; const AttendanceLogViewButton({ Key? key, required this.employee, required this.attendanceController, }) : super(key: key); Future _openGoogleMaps( BuildContext context, double lat, double lon) async { final url = 'https://www.google.com/maps/search/?api=1&query=$lat,$lon'; if (await canLaunchUrl(Uri.parse(url))) { await launchUrl( Uri.parse(url), mode: LaunchMode.externalApplication, ); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Could not open Google Maps')), ); } } void _showImageDialog(BuildContext context, String imageUrl) { showDialog( context: context, builder: (_) => Dialog( child: Image.network( imageUrl, fit: BoxFit.cover, height: 400, errorBuilder: (context, error, stackTrace) { return const Icon( Icons.broken_image, size: 50, color: Colors.grey, ); }, ), ), ); } 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: 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, ), 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), ), ), 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), ], ), ], ), ); }, ), ), ); } @override Widget build(BuildContext context) { return SizedBox( height: 30, child: ElevatedButton( onPressed: () => _showLogsBottomSheet(context), style: ElevatedButton.styleFrom( backgroundColor: Colors.indigo, textStyle: const TextStyle(fontSize: 12), padding: const EdgeInsets.symmetric(horizontal: 12), ), child: const FittedBox( fit: BoxFit.scaleDown, child: Text( "View", overflow: TextOverflow.ellipsis, style: TextStyle(fontSize: 12, color: Colors.white), ), ), ), ); } Widget _getLogIcon(dynamic log) { int activity = log.activity ?? -1; IconData iconData; Color iconColor; bool isTodayOrYesterday = false; try { if (log.formattedDate != null) { final logDate = DateTime.parse(log.formattedDate); final now = DateTime.now(); final today = DateTime(now.year, now.month, now.day); final logDay = DateTime(logDate.year, logDate.month, logDate.day); final yesterday = today.subtract(Duration(days: 1)); isTodayOrYesterday = (logDay == today) || (logDay == yesterday); } } catch (_) { isTodayOrYesterday = false; } switch (activity) { case 0: case 1: iconData = Icons.arrow_circle_right; iconColor = Colors.green; break; case 2: iconData = Icons.hourglass_top; iconColor = Colors.blueGrey; break; case 4: if (isTodayOrYesterday) { iconData = Icons.arrow_circle_left; iconColor = Colors.red; } else { iconData = Icons.check; iconColor = Colors.green; } break; case 5: iconData = Icons.close; iconColor = Colors.red; break; default: iconData = Icons.info; iconColor = Colors.grey; } return Icon(iconData, color: iconColor, size: 20); } }