import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:intl/intl.dart'; import 'package:marco/controller/employee/employees_screen_controller.dart'; import 'package:marco/helpers/widgets/custom_app_bar.dart'; import 'package:marco/helpers/widgets/avatar.dart'; import 'package:marco/helpers/widgets/my_spacing.dart'; import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/helpers/utils/launcher_utils.dart'; import 'package:marco/helpers/widgets/my_refresh_indicator.dart'; import 'package:marco/model/employees/add_employee_bottom_sheet.dart'; import 'package:marco/helpers/utils/mixins/ui_mixin.dart'; import 'package:marco/helpers/widgets/my_custom_skeleton.dart'; import 'package:marco/view/employees/manage_reporting_bottom_sheet.dart'; import 'package:marco/model/employees/employee_model.dart'; import 'package:marco/view/employees/manage_reporting_bottom_sheet.dart'; import 'package:marco/model/employees/employee_model.dart'; import 'package:marco/view/employees/assign_employee_bottom_sheet.dart'; class EmployeeDetailPage extends StatefulWidget { final String employeeId; final bool fromProfile; const EmployeeDetailPage({ super.key, required this.employeeId, this.fromProfile = false, }); @override State createState() => _EmployeeDetailPageState(); } class _EmployeeDetailPageState extends State with UIMixin { final EmployeesScreenController controller = Get.put(EmployeesScreenController()); @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { controller.fetchEmployeeDetails(widget.employeeId); controller.fetchReportingManagers(widget.employeeId); }); } @override void dispose() { controller.selectedEmployeeDetails.value = null; super.dispose(); } String _getDisplayValue(dynamic value) { if (value == null || value.toString().trim().isEmpty || value == "null") { return "NA"; } return value.toString(); } String _formatDate(DateTime? date) { if (date == null || date == DateTime(1)) return "NA"; try { return DateFormat('d/M/yyyy').format(date); } catch (_) { return "NA"; } } Widget _buildDetailRow({ required IconData icon, required String label, required String value, VoidCallback? onTap, VoidCallback? onLongPress, bool isActionable = false, }) { return Padding( padding: const EdgeInsets.symmetric(vertical: 12), child: InkWell( onTap: isActionable && value != "NA" ? onTap : null, onLongPress: isActionable && value != "NA" ? onLongPress : null, borderRadius: BorderRadius.circular(5), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( padding: const EdgeInsets.all(8), child: Icon(icon, size: 20), ), MySpacing.width(16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ MyText( label, color: Colors.grey[600], fontWeight: 500, ), MySpacing.height(4), MyText( value, color: isActionable && value != "NA" ? Colors.blueAccent : Colors.black87, fontWeight: 500, decoration: isActionable && value != "NA" ? TextDecoration.underline : TextDecoration.none, ), ], ), ), if (isActionable && value != "NA") Icon(Icons.chevron_right, color: Colors.grey[400], size: 20), ], ), ), ); } Widget _buildSectionCard({ required String title, required IconData titleIcon, required List children, }) { return Card( elevation: 2, shadowColor: Colors.black12, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5)), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(titleIcon, size: 20), MySpacing.width(8), MyText( title, fontSize: 16, fontWeight: 700, color: Colors.black87, ), ], ), MySpacing.height(8), const Divider(), ...children, ], ), ), ); } @override Widget build(BuildContext context) { final bool showAppBar = !widget.fromProfile; return Scaffold( backgroundColor: const Color(0xFFF1F1F1), appBar: showAppBar ? CustomAppBar( title: "Employee Details", onBackPressed: () { if (widget.fromProfile) { Get.back(); } else { Get.offNamed('/dashboard/employees'); } }, ) : null, body: Obx(() { if (controller.isLoadingEmployeeDetails.value) { return SkeletonLoaders.employeeDetailSkeletonLoader(); } final employee = controller.selectedEmployeeDetails.value; if (employee == null) { return const Center(child: MyText("No employee details found.")); } return SafeArea( child: MyRefreshIndicator( onRefresh: () async { await controller.fetchEmployeeDetails(widget.employeeId); await controller.fetchReportingManagers(employee.id); }, child: SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), padding: const EdgeInsets.fromLTRB(12, 20, 12, 80), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ /// ------------------ HEADER CARD ------------------ Card( elevation: 2, shadowColor: Colors.black12, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(5), ), child: Padding( padding: const EdgeInsets.all(16), child: Row( children: [ Avatar( firstName: employee.firstName, lastName: employee.lastName, size: 35, ), MySpacing.width(16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ MyText.titleMedium( '${employee.firstName} ${employee.lastName}', fontWeight: 700, ), MySpacing.height(6), MyText.bodySmall( _getDisplayValue(employee.jobRole), fontWeight: 500, ), ], ), ), IconButton( icon: Icon(Icons.edit, size: 24, color: contentTheme.primary), onPressed: () async { final result = await showModalBottomSheet< Map>( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, builder: (_) => AddEmployeeBottomSheet( employeeData: { 'id': employee.id, 'first_name': employee.firstName, 'last_name': employee.lastName, 'phone_number': employee.phoneNumber, 'email': employee.email, 'hasApplicationAccess': employee.hasApplicationAccess, 'gender': employee.gender.toLowerCase(), 'job_role_id': employee.jobRoleId, 'joining_date': employee.joiningDate ?.toIso8601String(), }, ), ); if (result != null) { controller .fetchEmployeeDetails(widget.employeeId); } }, ), ], ), ), ), MySpacing.height(16), /// ------------------ MANAGE REPORTING ------------------ _buildSectionCard( title: 'Manage Reporting', titleIcon: Icons.people_outline, children: [ GestureDetector( onTap: () async { await showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, builder: (_) => ManageReportingBottomSheet( initialEmployee: EmployeeModel( id: employee.id, employeeId: employee.id.toString(), firstName: employee.firstName ?? "", lastName: employee.lastName ?? "", name: "${employee.firstName} ${employee.lastName}", email: employee.email ?? "", jobRole: employee.jobRole ?? "", jobRoleID: "0", designation: employee.jobRole ?? "", phoneNumber: employee.phoneNumber ?? "", activity: 0, action: 0, ), hideMainSelector: true, hideLoggedUserFromSelection: true, loggedUserId: controller.selectedEmployeeDetails.value?.id, ), ); await controller.fetchReportingManagers(employee.id); }, child: Padding( padding: const EdgeInsets.symmetric(vertical: 12), child: Row( children: [ const Icon(Icons.manage_accounts_outlined, color: Colors.grey), const SizedBox(width: 16), const Expanded( child: Text( 'View / Update Reporting Managers', style: TextStyle( fontSize: 15, fontWeight: FontWeight.w500, color: Colors.black87, ), ), ), const Icon(Icons.arrow_forward_ios_rounded, size: 16, color: Colors.grey), ], ), ), ), Obx(() { final primary = controller.selectedEmployeePrimaryManagers; final secondary = controller.selectedEmployeeSecondaryManagers; if (primary.isEmpty && secondary.isEmpty) { return const Padding( padding: EdgeInsets.only(top: 8.0), child: Text( 'No reporting managers assigned', style: TextStyle( fontSize: 14, color: Colors.grey, fontStyle: FontStyle.italic, ), ), ); } return Padding( padding: const EdgeInsets.fromLTRB(8, 8, 8, 8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Primary → ${_getManagerNames(primary)}', style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w600), ), Text( 'Secondary → ${_getManagerNames(secondary)}', style: const TextStyle( fontSize: 14, fontWeight: FontWeight.w600), ), ], ), ); }), ], ), MySpacing.height(16), /// ------------------ CONTACT INFO ------------------ _buildSectionCard( title: 'Contact Information', titleIcon: Icons.contact_phone, children: [ _buildDetailRow( icon: Icons.email_outlined, label: 'Email', value: _getDisplayValue(employee.email), isActionable: true, onTap: () { if (employee.email != null && employee.email.toString().trim().isNotEmpty) { LauncherUtils.launchEmail(employee.email!); } }, onLongPress: () { if (employee.email != null && employee.email.toString().trim().isNotEmpty) { LauncherUtils.copyToClipboard( employee.email!, typeLabel: 'Email'); } }, ), _buildDetailRow( icon: Icons.phone_outlined, label: 'Phone Number', value: _getDisplayValue(employee.phoneNumber), isActionable: true, onTap: () { if (employee.phoneNumber.trim().isNotEmpty) { LauncherUtils.launchPhone(employee.phoneNumber); } }, onLongPress: () { if (employee.phoneNumber.trim().isNotEmpty) { LauncherUtils.copyToClipboard( employee.phoneNumber, typeLabel: 'Phone Number', ); } }, ), ], ), MySpacing.height(16), /// ------------------ EMERGENCY CONTACT ------------------ _buildSectionCard( title: 'Emergency Contact', titleIcon: Icons.emergency, children: [ _buildDetailRow( icon: Icons.person_outline, label: 'Contact Person', value: _getDisplayValue(employee.emergencyContactPerson), ), _buildDetailRow( icon: Icons.phone_in_talk_outlined, label: 'Emergency Phone', value: _getDisplayValue(employee.emergencyPhoneNumber), isActionable: true, onTap: () { if (employee.emergencyPhoneNumber != null && employee.emergencyPhoneNumber! .trim() .isNotEmpty) { LauncherUtils.launchPhone( employee.emergencyPhoneNumber!); } }, onLongPress: () { if (employee.emergencyPhoneNumber != null && employee.emergencyPhoneNumber! .trim() .isNotEmpty) { LauncherUtils.copyToClipboard( employee.emergencyPhoneNumber!, typeLabel: 'Emergency Phone'); } }, ), ], ), MySpacing.height(16), /// ------------------ PERSONAL INFO ------------------ _buildSectionCard( title: 'Personal Information', titleIcon: Icons.person, children: [ _buildDetailRow( icon: Icons.wc_outlined, label: 'Gender', value: _getDisplayValue(employee.gender), ), _buildDetailRow( icon: Icons.cake_outlined, label: 'Birth Date', value: _formatDate(employee.birthDate), ), _buildDetailRow( icon: Icons.work_outline, label: 'Joining Date', value: _formatDate(employee.joiningDate), ), ], ), MySpacing.height(16), /// ------------------ ADDRESS INFO ------------------ _buildSectionCard( title: 'Address Information', titleIcon: Icons.location_on, children: [ _buildDetailRow( icon: Icons.home_outlined, label: 'Current Address', value: _getDisplayValue(employee.currentAddress), ), _buildDetailRow( icon: Icons.home_work_outlined, label: 'Permanent Address', value: _getDisplayValue(employee.permanentAddress), ), ], ), ], ), ), ), ); }), /// ------------------ FLOATING BUTTON ------------------ floatingActionButton: Obx(() { final employee = controller.selectedEmployeeDetails.value; if (employee == null) return const SizedBox.shrink(); return FloatingActionButton.extended( onPressed: () { showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, builder: (context) => AssignProjectBottomSheet( employeeId: widget.employeeId, jobRoleId: employee.jobRoleId, ), ); }, backgroundColor: contentTheme.primary, icon: const Icon(Icons.add), label: MyText( 'Assign to Project', fontSize: 14, fontWeight: 500, ), ); }), ); } /// ------------------ UTIL ------------------ String _getManagerNames(List managers) { if (managers.isEmpty) return '—'; return managers .map((m) => '${(m.firstName ?? '').trim()} ${(m.lastName ?? '').trim()}'.trim()) .where((name) => name.isNotEmpty) .join(', '); } }