diff --git a/lib/helpers/services/auth_service.dart b/lib/helpers/services/auth_service.dart index 3c9b02b..08770bc 100644 --- a/lib/helpers/services/auth_service.dart +++ b/lib/helpers/services/auth_service.dart @@ -83,7 +83,7 @@ class AuthService { logSafe("Login payload (raw): $data"); logSafe("Login payload (JSON): ${jsonEncode(data)}"); - final responseData = await _post("/auth/app/login", data); + final responseData = await _post("/auth/login-mobile", data); if (responseData == null) return {"error": "Network error. Please check your connection."}; @@ -179,7 +179,7 @@ class AuthService { if (employeeInfo == null) return null; final token = await LocalStorage.getJwtToken(); return _post( - "/auth/login-mpin", + "/auth/login-mpin/v1", { "employeeId": employeeInfo.id, "mpin": mpin, @@ -202,7 +202,7 @@ class AuthService { required String email, required String otp, }) async { - final data = await _post("/auth/login-otp", {"email": email, "otp": otp}); + final data = await _post("/auth/login-otp/v1", {"email": email, "otp": otp}); if (data != null && data['data'] != null) { await _handleLoginSuccess(data['data']); return null; diff --git a/lib/helpers/utils/base_bottom_sheet.dart b/lib/helpers/utils/base_bottom_sheet.dart index 359f82c..b07a361 100644 --- a/lib/helpers/utils/base_bottom_sheet.dart +++ b/lib/helpers/utils/base_bottom_sheet.dart @@ -4,6 +4,7 @@ import 'package:marco/helpers/widgets/my_text.dart'; class BaseBottomSheet extends StatelessWidget { final String title; + final String? subtitle; final Widget child; final VoidCallback onCancel; final VoidCallback onSubmit; @@ -20,6 +21,7 @@ class BaseBottomSheet extends StatelessWidget { required this.child, required this.onCancel, required this.onSubmit, + this.subtitle, this.isSubmitting = false, this.submitText = 'Submit', this.submitColor = Colors.indigo, @@ -50,24 +52,37 @@ class BaseBottomSheet extends StatelessWidget { ], ), child: SafeArea( - // 👈 prevents overlap with nav bar top: false, child: Padding( padding: const EdgeInsets.fromLTRB(20, 16, 20, 32), child: Column( mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, children: [ MySpacing.height(5), - Container( - width: 40, - height: 5, - decoration: BoxDecoration( - color: Colors.grey.shade300, - borderRadius: BorderRadius.circular(10), + Center( + child: Container( + width: 40, + height: 5, + decoration: BoxDecoration( + color: Colors.grey.shade300, + borderRadius: BorderRadius.circular(10), + ), ), ), MySpacing.height(12), MyText.titleLarge(title, fontWeight: 700), + + // ✅ Subtitle shown just below header if provided + if (subtitle != null && subtitle!.isNotEmpty) ...[ + MySpacing.height(4), + MyText.bodySmall( + subtitle!, + fontWeight: 600, + color: Colors.grey[700], + ), + ], + MySpacing.height(12), child, MySpacing.height(12), diff --git a/lib/model/attendance/attendence_action_button.dart b/lib/model/attendance/attendence_action_button.dart index 7fc49b1..6959268 100644 --- a/lib/model/attendance/attendence_action_button.dart +++ b/lib/model/attendance/attendence_action_button.dart @@ -7,6 +7,7 @@ import 'package:marco/controller/attendance/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'; +import 'package:marco/helpers/widgets/my_text.dart'; class AttendanceActionButton extends StatefulWidget { final dynamic employee; @@ -84,6 +85,8 @@ class _AttendanceActionButtonState extends State { final controller = widget.attendanceController; final projectController = Get.find(); final selectedProjectId = projectController.selectedProject?.id; + final projectName = + projectController.selectedProject?.name ?? 'Unknown Project'; if (selectedProjectId == null) { showAppSnackbar( @@ -108,8 +111,10 @@ class _AttendanceActionButtonState extends State { break; case 1: - final isOldCheckIn = AttendanceButtonHelper.isOlderThanDays(widget.employee.checkIn, 2); - final isOldCheckOut = AttendanceButtonHelper.isOlderThanDays(widget.employee.checkOut, 2); + final isOldCheckIn = + AttendanceButtonHelper.isOlderThanDays(widget.employee.checkIn, 2); + final isOldCheckOut = + AttendanceButtonHelper.isOlderThanDays(widget.employee.checkOut, 2); if (widget.employee.checkOut == null && isOldCheckIn) { action = 2; @@ -158,7 +163,9 @@ class _AttendanceActionButtonState extends State { actionText, selectedTime: selectedTime, checkInDate: widget.employee.checkIn, + projectName: projectName, ); + if (comment == null || comment.isEmpty) { controller.uploadingStates[uniqueLogKey]?.value = false; return; @@ -167,7 +174,9 @@ class _AttendanceActionButtonState extends State { String? markTime; if (actionText == ButtonActions.requestRegularize) { selectedTime ??= await _pickRegularizationTime(widget.employee.checkIn!); - markTime = selectedTime != null ? DateFormat("hh:mm a").format(selectedTime) : null; + markTime = selectedTime != null + ? DateFormat("hh:mm a").format(selectedTime) + : null; } else if (selectedTime != null) { markTime = DateFormat("hh:mm a").format(selectedTime); } @@ -205,13 +214,17 @@ class _AttendanceActionButtonState extends State { Widget build(BuildContext context) { return Obx(() { final controller = widget.attendanceController; - final isUploading = controller.uploadingStates[uniqueLogKey]?.value ?? false; + final isUploading = + controller.uploadingStates[uniqueLogKey]?.value ?? false; final emp = widget.employee; - final isYesterday = AttendanceButtonHelper.isLogFromYesterday(emp.checkIn, emp.checkOut); - final isTodayApproved = AttendanceButtonHelper.isTodayApproved(emp.activity, emp.checkIn); + final isYesterday = + AttendanceButtonHelper.isLogFromYesterday(emp.checkIn, emp.checkOut); + final isTodayApproved = + AttendanceButtonHelper.isTodayApproved(emp.activity, emp.checkIn); final isApprovedButNotToday = - AttendanceButtonHelper.isApprovedButNotToday(emp.activity, isTodayApproved); + AttendanceButtonHelper.isApprovedButNotToday( + emp.activity, isTodayApproved); final isButtonDisabled = AttendanceButtonHelper.isButtonDisabled( isUploading: isUploading, @@ -288,15 +301,17 @@ class AttendanceActionButtonUI extends StatelessWidget { if (buttonText.toLowerCase() == 'rejected') const Icon(Icons.close, size: 16, color: Colors.red), if (buttonText.toLowerCase() == 'requested') - const Icon(Icons.hourglass_top, size: 16, color: Colors.orange), + const Icon(Icons.hourglass_top, + size: 16, color: Colors.orange), if (['approved', 'rejected', 'requested'] .contains(buttonText.toLowerCase())) const SizedBox(width: 4), Flexible( - child: Text( + child: MyText.bodySmall( buttonText, overflow: TextOverflow.ellipsis, - style: const TextStyle(fontSize: 12), + fontWeight: 500, + color: Colors.white, ), ), ], @@ -311,15 +326,18 @@ Future _showCommentBottomSheet( String actionText, { DateTime? selectedTime, DateTime? checkInDate, + String? projectName, }) async { final commentController = TextEditingController(); String? errorText; - // Prepare title - String sheetTitle = "Add Comment for ${capitalizeFirstLetter(actionText)}"; + String sheetTitle = + "Adding Comment for ${capitalizeFirstLetter(actionText)}"; if (selectedTime != null && checkInDate != null) { sheetTitle = - "${capitalizeFirstLetter(actionText)} for ${DateFormat('dd MMM yyyy').format(checkInDate)} at ${DateFormat('hh:mm a').format(selectedTime)}"; + "${capitalizeFirstLetter(actionText)} • ${DateFormat('dd MMM yyyy').format(checkInDate)} " + "at ${DateFormat('hh:mm a').format(selectedTime)}\n" + "${projectName ?? 'Project'}"; } return showModalBottomSheet( @@ -342,30 +360,42 @@ Future _showCommentBottomSheet( } return Padding( - padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).viewInsets.bottom), child: BaseBottomSheet( - title: sheetTitle, // 👈 now showing full sentence as title + title: sheetTitle, + subtitle: projectName, onCancel: () => Navigator.of(context).pop(), onSubmit: submit, isSubmitting: false, submitText: 'Submit', - child: TextField( - controller: commentController, - maxLines: 4, - decoration: InputDecoration( - hintText: 'Type your comment here...', - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + MyText.bodyMedium( + 'Add a comment to proceed', + fontWeight: 500, ), - filled: true, - fillColor: Colors.grey.shade100, - errorText: errorText, - ), - onChanged: (_) { - if (errorText != null) { - setModalState(() => errorText = null); - } - }, + const SizedBox(height: 8), + 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); + } + }, + ), + ], ), ), ); @@ -375,6 +405,5 @@ Future _showCommentBottomSheet( ); } - String capitalizeFirstLetter(String text) => text.isEmpty ? text : text[0].toUpperCase() + text.substring(1);