diff --git a/lib/helpers/services/api_endpoints.dart b/lib/helpers/services/api_endpoints.dart index fae25cf..0f67c10 100644 --- a/lib/helpers/services/api_endpoints.dart +++ b/lib/helpers/services/api_endpoints.dart @@ -135,6 +135,7 @@ class ApiEndpoints { static const String manageOrganizationHierarchy = "/organization/hierarchy/manage"; + // Service Project Module API Endpoints static const String getServiceProjectsList = "/serviceproject/list"; static const String getServiceProjectDetail = "/serviceproject/details"; diff --git a/lib/view/employees/employee_detail_screen.dart b/lib/view/employees/employee_detail_screen.dart index 6492674..16af71e 100644 --- a/lib/view/employees/employee_detail_screen.dart +++ b/lib/view/employees/employee_detail_screen.dart @@ -51,18 +51,18 @@ class _EmployeeDetailPageState extends State with UIMixin { } String _getDisplayValue(dynamic value) { - if (value == null || value.toString().trim().isEmpty || value == 'null') { - return 'NA'; + 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'; + if (date == null || date == DateTime(1)) return "NA"; try { return DateFormat('d/M/yyyy').format(date); } catch (_) { - return 'NA'; + return "NA"; } } @@ -77,18 +77,15 @@ class _EmployeeDetailPageState extends State with UIMixin { return Padding( padding: const EdgeInsets.symmetric(vertical: 12), child: InkWell( - onTap: isActionable && value != 'NA' ? onTap : null, - onLongPress: isActionable && value != 'NA' ? onLongPress : null, + 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, - ), + child: Icon(icon, size: 20), ), MySpacing.width(16), Expanded( @@ -103,23 +100,19 @@ class _EmployeeDetailPageState extends State with UIMixin { MySpacing.height(4), MyText( value, - color: isActionable && value != 'NA' + color: isActionable && value != "NA" ? Colors.blueAccent : Colors.black87, fontWeight: 500, - decoration: isActionable && value != 'NA' + decoration: isActionable && value != "NA" ? TextDecoration.underline : TextDecoration.none, ), ], ), ), - if (isActionable && value != 'NA') - Icon( - Icons.chevron_right, - color: Colors.grey[400], - size: 20, - ), + if (isActionable && value != "NA") + Icon(Icons.chevron_right, color: Colors.grey[400], size: 20), ], ), ), @@ -142,10 +135,7 @@ class _EmployeeDetailPageState extends State with UIMixin { children: [ Row( children: [ - Icon( - titleIcon, - size: 20, - ), + Icon(titleIcon, size: 20), MySpacing.width(8), MyText( title, @@ -172,7 +162,7 @@ class _EmployeeDetailPageState extends State with UIMixin { backgroundColor: const Color(0xFFF1F1F1), appBar: showAppBar ? CustomAppBar( - title: 'Employee Details', + title: "Employee Details", onBackPressed: () { if (widget.fromProfile) { Get.back(); @@ -189,7 +179,7 @@ class _EmployeeDetailPageState extends State with UIMixin { final employee = controller.selectedEmployeeDetails.value; if (employee == null) { - return Center(child: MyText('No employee details found.')); + return const Center(child: MyText("No employee details found.")); } return SafeArea( @@ -204,7 +194,7 @@ class _EmployeeDetailPageState extends State with UIMixin { child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ - // Header Section + /// ------------------ HEADER CARD ------------------ Card( elevation: 2, shadowColor: Colors.black12, @@ -257,8 +247,8 @@ class _EmployeeDetailPageState extends State with UIMixin { employee.hasApplicationAccess, 'gender': employee.gender.toLowerCase(), 'job_role_id': employee.jobRoleId, - 'joining_date': - employee.joiningDate?.toIso8601String(), + 'joining_date': employee.joiningDate + ?.toIso8601String(), }, ), ); @@ -273,8 +263,10 @@ class _EmployeeDetailPageState extends State with UIMixin { ), ), ), + MySpacing.height(16), + /// ------------------ MANAGE REPORTING ------------------ _buildSectionCard( title: 'Manage Reporting', titleIcon: Icons.people_outline, @@ -308,7 +300,6 @@ class _EmployeeDetailPageState extends State with UIMixin { ), ); - // 🔄 Refresh reporting managers after editing await controller.fetchReportingManagers(employee.id); }, child: Padding( @@ -355,41 +346,32 @@ class _EmployeeDetailPageState extends State with UIMixin { } return Padding( - padding: const EdgeInsets.only( - top: 8.0, left: 8, right: 8, bottom: 8), + padding: const EdgeInsets.fromLTRB(8, 8, 8, 8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Padding( - padding: - const EdgeInsets.symmetric(vertical: 4.0), - child: Text( - 'Primary → ${_getManagerNames(primary)}', - style: const TextStyle( + Text( + 'Primary → ${_getManagerNames(primary)}', + style: const TextStyle( fontSize: 14, - fontWeight: FontWeight.w600, - ), - ), + fontWeight: FontWeight.w600), ), - Padding( - padding: - const EdgeInsets.symmetric(vertical: 4.0), - child: Text( - 'Secondary → ${_getManagerNames(secondary)}', - style: const TextStyle( + Text( + 'Secondary → ${_getManagerNames(secondary)}', + style: const TextStyle( fontSize: 14, - fontWeight: FontWeight.w600, - ), - ), + fontWeight: FontWeight.w600), ), ], ), ); - }) + }), ], ), - // Contact Information Section + MySpacing.height(16), + + /// ------------------ CONTACT INFO ------------------ _buildSectionCard( title: 'Contact Information', titleIcon: Icons.contact_phone, @@ -408,7 +390,8 @@ class _EmployeeDetailPageState extends State with UIMixin { onLongPress: () { if (employee.email != null && employee.email.toString().trim().isNotEmpty) { - LauncherUtils.copyToClipboard(employee.email!, + LauncherUtils.copyToClipboard( + employee.email!, typeLabel: 'Email'); } }, @@ -434,9 +417,10 @@ class _EmployeeDetailPageState extends State with UIMixin { ), ], ), + MySpacing.height(16), - // Emergency Contact Section + /// ------------------ EMERGENCY CONTACT ------------------ _buildSectionCard( title: 'Emergency Contact', titleIcon: Icons.emergency, @@ -446,17 +430,16 @@ class _EmployeeDetailPageState extends State with UIMixin { label: 'Contact Person', value: _getDisplayValue(employee.emergencyContactPerson), - isActionable: false, ), _buildDetailRow( icon: Icons.phone_in_talk_outlined, label: 'Emergency Phone', - value: _getDisplayValue(employee.emergencyPhoneNumber), + value: + _getDisplayValue(employee.emergencyPhoneNumber), isActionable: true, onTap: () { if (employee.emergencyPhoneNumber != null && - employee.emergencyPhoneNumber - .toString() + employee.emergencyPhoneNumber! .trim() .isNotEmpty) { LauncherUtils.launchPhone( @@ -465,8 +448,7 @@ class _EmployeeDetailPageState extends State with UIMixin { }, onLongPress: () { if (employee.emergencyPhoneNumber != null && - employee.emergencyPhoneNumber - .toString() + employee.emergencyPhoneNumber! .trim() .isNotEmpty) { LauncherUtils.copyToClipboard( @@ -477,9 +459,10 @@ class _EmployeeDetailPageState extends State with UIMixin { ), ], ), + MySpacing.height(16), - // Personal Information Section + /// ------------------ PERSONAL INFO ------------------ _buildSectionCard( title: 'Personal Information', titleIcon: Icons.person, @@ -488,25 +471,23 @@ class _EmployeeDetailPageState extends State with UIMixin { icon: Icons.wc_outlined, label: 'Gender', value: _getDisplayValue(employee.gender), - isActionable: false, ), _buildDetailRow( icon: Icons.cake_outlined, label: 'Birth Date', value: _formatDate(employee.birthDate), - isActionable: false, ), _buildDetailRow( icon: Icons.work_outline, label: 'Joining Date', value: _formatDate(employee.joiningDate), - isActionable: false, ), ], ), + MySpacing.height(16), - // Address Information Section + /// ------------------ ADDRESS INFO ------------------ _buildSectionCard( title: 'Address Information', titleIcon: Icons.location_on, @@ -515,13 +496,11 @@ class _EmployeeDetailPageState extends State with UIMixin { icon: Icons.home_outlined, label: 'Current Address', value: _getDisplayValue(employee.currentAddress), - isActionable: false, ), _buildDetailRow( icon: Icons.home_work_outlined, label: 'Permanent Address', value: _getDisplayValue(employee.permanentAddress), - isActionable: false, ), ], ), @@ -532,7 +511,7 @@ class _EmployeeDetailPageState extends State with UIMixin { ); }), - // Floating “Assign to Project” FAB + /// ------------------ FLOATING BUTTON ------------------ floatingActionButton: Obx(() { final employee = controller.selectedEmployeeDetails.value; if (employee == null) return const SizedBox.shrink(); @@ -550,17 +529,18 @@ class _EmployeeDetailPageState extends State with UIMixin { ); }, backgroundColor: contentTheme.primary, - label: MyText( + icon: const Icon(Icons.add), + label: MyText( 'Assign to Project', fontSize: 14, fontWeight: 500, ), - icon: const Icon(Icons.add), ); }), ); } + /// ------------------ UTIL ------------------ String _getManagerNames(List managers) { if (managers.isEmpty) return '—'; return managers