From fe4a64e4d2b5c59c77b81fe00061392449994c5b Mon Sep 17 00:00:00 2001 From: Manish Date: Wed, 12 Nov 2025 17:17:01 +0530 Subject: [PATCH] implementation of manage reporting inside employee profile --- .../manage_reporting_bottom_sheet.dart | 134 ++++++++++++++---- 1 file changed, 105 insertions(+), 29 deletions(-) diff --git a/lib/view/employees/manage_reporting_bottom_sheet.dart b/lib/view/employees/manage_reporting_bottom_sheet.dart index 3eb5dd4..ba035c6 100644 --- a/lib/view/employees/manage_reporting_bottom_sheet.dart +++ b/lib/view/employees/manage_reporting_bottom_sheet.dart @@ -13,14 +13,16 @@ class ManageReportingBottomSheet extends StatefulWidget { final EmployeeModel? initialEmployee; final bool hideMainSelector; final bool renderAsCard; - final bool hideLoggedUserFromSelection; // ✅ new + final bool hideLoggedUserFromSelection; + final String? loggedUserId; const ManageReportingBottomSheet({ super.key, this.initialEmployee, this.hideMainSelector = false, this.renderAsCard = false, - this.hideLoggedUserFromSelection = false, // default false + this.hideLoggedUserFromSelection = false, + this.loggedUserId, }); @override @@ -211,21 +213,76 @@ class _ManageReportingBottomSheetState Future _handleSubmit() async { if (_selectedEmployee == null) { showAppSnackbar( - title: 'Error', - message: 'Please select the employee.', - type: SnackbarType.error); + title: 'Error', + message: 'Please select the employee.', + type: SnackbarType.error, + ); return; } + if (_selectedPrimary.isEmpty) { showAppSnackbar( - title: 'Error', - message: 'Please select at least one primary employee.', - type: SnackbarType.error); + title: 'Error', + message: 'Please select at least one primary employee.', + type: SnackbarType.error, + ); return; } + final employeeId = _selectedEmployee!.id; + + // === BUILD PAYLOAD (updated logic) === + + // fetch current assignments so we can deactivate old ones + List? currentAssignments; + try { + currentAssignments = + await ApiService.getOrganizationHierarchyList(employeeId); + } catch (_) { + currentAssignments = null; + } + + // helper sets of newly selected ids + final newPrimaryIds = _selectedPrimary.map((e) => e.id).toSet(); + final newSecondaryIds = _selectedSecondary.map((e) => e.id).toSet(); + final List> payload = []; + // 1) For current active assignments: if they are not in new selections -> add isActive:false + if (currentAssignments != null && currentAssignments.isNotEmpty) { + for (final item in currentAssignments) { + try { + final reportTo = item['reportTo']; + if (reportTo == null) continue; + final reportToId = reportTo['id'] as String?; + if (reportToId == null) continue; + final isPrimary = item['isPrimary'] == true; + final currentlyActive = + item['isActive'] == true || item['isActive'] == null; // be safe + + // if currently active and not included in new selection -> mark false + if (currentlyActive) { + if (isPrimary && !newPrimaryIds.contains(reportToId)) { + payload.add({ + "reportToId": reportToId, + "isPrimary": true, + "isActive": false, + }); + } else if (!isPrimary && !newSecondaryIds.contains(reportToId)) { + payload.add({ + "reportToId": reportToId, + "isPrimary": false, + "isActive": false, + }); + } + } + } catch (_) { + // ignore malformed items (same behavior as before) + } + } + } + + // 2) Add new primary (active) for (final emp in _selectedPrimary) { payload.add({ "reportToId": emp.id, @@ -233,6 +290,8 @@ class _ManageReportingBottomSheetState "isActive": true, }); } + + // 3) Add new secondary (active) for (final emp in _selectedSecondary) { payload.add({ "reportToId": emp.id, @@ -242,11 +301,13 @@ class _ManageReportingBottomSheetState } setState(() => _isSubmitting = true); - // show loader - Get.dialog(const Center(child: CircularProgressIndicator()), - barrierDismissible: false); - final employeeId = _selectedEmployee!.id; + // show loader + Get.dialog( + const Center(child: CircularProgressIndicator()), + barrierDismissible: false, + ); + final success = await ApiService.manageOrganizationHierarchy( employeeId: employeeId, payload: payload, @@ -254,25 +315,36 @@ class _ManageReportingBottomSheetState // hide loader if (Get.isDialogOpen == true) Get.back(); - setState(() => _isSubmitting = false); if (success) { showAppSnackbar( - title: 'Success', - message: 'Reporting assigned successfully', - type: SnackbarType.success); + title: 'Success', + message: 'Reporting assigned successfully', + type: SnackbarType.success, + ); - // Optionally refresh the saved hierarchy (not necessary here) but we can call: + // ✅ Refresh both the organization hierarchy and employee details so UI shows updated managers + try { + final empId = employeeId; + final EmployeesScreenController controller = Get.find(); + await controller.fetchReportingManagers(empId); + await controller.fetchEmployeeDetails(empId); + } catch (_) { + // ignore if controller not found — not critical + } + + // Optional: re-fetch the organization hierarchy list (if needed elsewhere) await ApiService.getOrganizationHierarchyList(employeeId); - // Keep sheet open and reset reporter selections for next assignment + // Keep sheet open and reset selections _resetForm(); } else { showAppSnackbar( - title: 'Error', - message: 'Failed to assign reporting. Please try again.', - type: SnackbarType.error); + title: 'Error', + message: 'Failed to assign reporting. Please try again.', + type: SnackbarType.error, + ); } } @@ -354,11 +426,6 @@ class _ManageReportingBottomSheetState backgroundColor: Colors.indigo.shade50, labelStyle: TextStyle(color: AppTheme.primaryColor), deleteIcon: const Icon(Icons.close, size: 16), - onDeleted: () => setState(() { - _selectedEmployee = null; - _selectEmployeeController.clear(); - _resetReportersOnly(); - }), ), ) else @@ -460,12 +527,21 @@ class _ManageReportingBottomSheetState spacing: 6, runSpacing: 6, children: selectedList.map((emp) { + final isProfileEmployee = widget.initialEmployee != null && + emp.id == widget.initialEmployee!.id; + return Chip( label: Text(emp.name, style: const TextStyle(fontSize: 12)), - backgroundColor: Colors.indigo.shade50, + backgroundColor: isProfileEmployee + ? Colors.indigo.shade50 + : Colors.indigo.shade50, labelStyle: TextStyle(color: AppTheme.primaryColor), - deleteIcon: const Icon(Icons.close, size: 16), - onDeleted: () => selectedList.remove(emp), + // Only show delete icon / action for non-profile employees + deleteIcon: isProfileEmployee + ? null + : const Icon(Icons.close, size: 16), + onDeleted: + isProfileEmployee ? null : () => selectedList.remove(emp), ); }).toList(), );