From 6d29d444fa290f4399f40a9bf55bc942af3899a6 Mon Sep 17 00:00:00 2001 From: Vaibhav Surve Date: Thu, 31 Jul 2025 18:17:48 +0530 Subject: [PATCH] feat: refactor AddEmployeeBottomSheet and AssignProjectBottomSheet for improved UI and functionality --- .../employees/add_employee_bottom_sheet.dart | 611 ++++++++---------- .../assign_employee_bottom_sheet.dart | 348 ++++------ 2 files changed, 386 insertions(+), 573 deletions(-) diff --git a/lib/model/employees/add_employee_bottom_sheet.dart b/lib/model/employees/add_employee_bottom_sheet.dart index f111c4f..91fee4e 100644 --- a/lib/model/employees/add_employee_bottom_sheet.dart +++ b/lib/model/employees/add_employee_bottom_sheet.dart @@ -1,13 +1,13 @@ import 'package:flutter/material.dart'; -import 'package:get/get.dart'; import 'package:flutter/services.dart'; - +import 'package:get/get.dart'; import 'package:marco/controller/dashboard/add_employee_controller.dart'; import 'package:marco/controller/dashboard/employees_screen_controller.dart'; import 'package:marco/helpers/utils/mixins/ui_mixin.dart'; import 'package:marco/helpers/widgets/my_spacing.dart'; import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/helpers/widgets/my_text_style.dart'; +import 'package:marco/helpers/utils/base_bottom_sheet.dart'; class AddEmployeeBottomSheet extends StatefulWidget { @override @@ -18,69 +18,110 @@ class _AddEmployeeBottomSheetState extends State with UIMixin { final AddEmployeeController _controller = Get.put(AddEmployeeController()); - late TextEditingController genderController; - late TextEditingController roleController; - @override - void initState() { - super.initState(); - genderController = TextEditingController(); - roleController = TextEditingController(); - } - - RelativeRect _popupMenuPosition(BuildContext context) { - final RenderBox overlay = - Overlay.of(context).context.findRenderObject() as RenderBox; - return RelativeRect.fromLTRB(100, 300, overlay.size.width - 100, 0); - } - - void _showGenderPopup(BuildContext context) async { - final selected = await showMenu( - context: context, - position: _popupMenuPosition(context), - items: Gender.values.map((gender) { - return PopupMenuItem( - value: gender, - child: Text(gender.name.capitalizeFirst!), + Widget build(BuildContext context) { + return GetBuilder( + init: _controller, + builder: (_) { + return BaseBottomSheet( + title: "Add Employee", + onCancel: () => Navigator.pop(context), + onSubmit: _handleSubmit, + child: Form( + key: _controller.basicValidator.formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _sectionLabel("Personal Info"), + MySpacing.height(16), + _inputWithIcon( + label: "First Name", + hint: "e.g., John", + icon: Icons.person, + controller: + _controller.basicValidator.getController('first_name')!, + validator: + _controller.basicValidator.getValidation('first_name'), + ), + MySpacing.height(16), + _inputWithIcon( + label: "Last Name", + hint: "e.g., Doe", + icon: Icons.person_outline, + controller: + _controller.basicValidator.getController('last_name')!, + validator: + _controller.basicValidator.getValidation('last_name'), + ), + MySpacing.height(16), + _sectionLabel("Contact Details"), + MySpacing.height(16), + _buildPhoneInput(context), + MySpacing.height(24), + _sectionLabel("Other Details"), + MySpacing.height(16), + _buildDropdownField( + label: "Gender", + value: _controller.selectedGender?.name.capitalizeFirst ?? '', + hint: "Select Gender", + onTap: () => _showGenderPopup(context), + ), + MySpacing.height(16), + _buildDropdownField( + label: "Role", + value: _controller.roles.firstWhereOrNull((role) => + role['id'] == _controller.selectedRoleId)?['name'] ?? + "", + hint: "Select Role", + onTap: () => _showRolePopup(context), + ), + ], + ), + ), ); - }).toList(), + }, ); + } - if (selected != null) { - _controller.onGenderSelected(selected); + // Submit logic + Future _handleSubmit() async { + final result = await _controller.createEmployees(); + + if (result != null && result['success'] == true) { + final employeeData = result['data']; // ✅ Safe now + final employeeController = Get.find(); + final projectId = employeeController.selectedProjectId; + + if (projectId == null) { + await employeeController.fetchAllEmployees(); + } else { + await employeeController.fetchEmployeesByProject(projectId); + } + + employeeController.update(['employee_screen_controller']); + + _controller.basicValidator.getController("first_name")?.clear(); + _controller.basicValidator.getController("last_name")?.clear(); + _controller.basicValidator.getController("phone_number")?.clear(); + _controller.selectedGender = null; + _controller.selectedRoleId = null; _controller.update(); + + Navigator.pop(context, employeeData); } } - void _showRolePopup(BuildContext context) async { - final selected = await showMenu( - context: context, - position: _popupMenuPosition(context), - items: _controller.roles.map((role) { - return PopupMenuItem( - value: role['id'], - child: Text(role['name']), - ); - }).toList(), - ); - - if (selected != null) { - _controller.onRoleSelected(selected); - _controller.update(); - } - } - - Widget _sectionLabel(String title) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - MyText.labelLarge(title, fontWeight: 600), - MySpacing.height(4), - Divider(thickness: 1, color: Colors.grey.shade200), - ], - ); - } + // Section label widget + Widget _sectionLabel(String title) => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + MyText.labelLarge(title, fontWeight: 600), + MySpacing.height(4), + Divider(thickness: 1, color: Colors.grey.shade200), + ], + ); + // Input field with icon Widget _inputWithIcon({ required String label, required String hint, @@ -104,6 +145,124 @@ class _AddEmployeeBottomSheetState extends State ); } + // Phone input with country code selector + Widget _buildPhoneInput(BuildContext context) { + return Row( + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 14), + decoration: BoxDecoration( + border: Border.all(color: Colors.grey.shade300), + borderRadius: BorderRadius.circular(12), + color: Colors.grey.shade100, + ), + child: PopupMenuButton>( + onSelected: (country) { + _controller.selectedCountryCode = country['code']!; + _controller.update(); + }, + itemBuilder: (context) => [ + PopupMenuItem( + enabled: false, + padding: EdgeInsets.zero, + child: SizedBox( + height: 200, + width: 100, + child: ListView( + children: _controller.countries.map((country) { + return ListTile( + dense: true, + title: Text("${country['name']} (${country['code']})"), + onTap: () => Navigator.pop(context, country), + ); + }).toList(), + ), + ), + ), + ], + child: Row( + children: [ + Text(_controller.selectedCountryCode), + const Icon(Icons.arrow_drop_down), + ], + ), + ), + ), + MySpacing.width(12), + Expanded( + child: TextFormField( + controller: + _controller.basicValidator.getController('phone_number'), + validator: (value) { + if (value == null || value.trim().isEmpty) { + return "Phone number is required"; + } + + final digitsOnly = value.trim(); + final minLength = _controller + .minDigitsPerCountry[_controller.selectedCountryCode] ?? + 7; + final maxLength = _controller + .maxDigitsPerCountry[_controller.selectedCountryCode] ?? + 15; + + if (!RegExp(r'^[0-9]+$').hasMatch(digitsOnly)) { + return "Only digits allowed"; + } + + if (digitsOnly.length < minLength || + digitsOnly.length > maxLength) { + return "Between $minLength–$maxLength digits"; + } + + return null; + }, + keyboardType: TextInputType.phone, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + LengthLimitingTextInputFormatter(15), + ], + decoration: _inputDecoration("e.g., 9876543210").copyWith( + suffixIcon: IconButton( + icon: const Icon(Icons.contacts), + onPressed: () => _controller.pickContact(context), + ), + ), + ), + ), + ], + ); + } + + // Gender/Role field (read-only dropdown) + Widget _buildDropdownField({ + required String label, + required String value, + required String hint, + required VoidCallback onTap, + }) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + MyText.labelMedium(label), + MySpacing.height(8), + GestureDetector( + onTap: onTap, + child: AbsorbPointer( + child: TextFormField( + readOnly: true, + controller: TextEditingController(text: value), + decoration: _inputDecoration(hint).copyWith( + suffixIcon: const Icon(Icons.expand_more), + ), + ), + ), + ), + ], + ); + } + + // Common input decoration InputDecoration _inputDecoration(String hint) { return InputDecoration( hintText: hint, @@ -120,311 +279,53 @@ class _AddEmployeeBottomSheetState extends State ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), - borderSide: BorderSide(color: Colors.blueAccent, width: 1.5), + borderSide: const BorderSide(color: Colors.blueAccent, width: 1.5), ), contentPadding: MySpacing.all(16), ); } - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - - return GetBuilder( - init: _controller, - builder: (_) { - return SingleChildScrollView( - padding: MediaQuery.of(context).viewInsets, - child: Container( - decoration: BoxDecoration( - color: theme.cardColor, - borderRadius: - const BorderRadius.vertical(top: Radius.circular(24)), - boxShadow: const [ - BoxShadow( - color: Colors.black12, - blurRadius: 12, - offset: Offset(0, -2)) - ], - ), - child: Padding( - padding: const EdgeInsets.fromLTRB(20, 16, 20, 32), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - // Drag Handle - Container( - width: 40, - height: 5, - decoration: BoxDecoration( - color: Colors.grey.shade300, - borderRadius: BorderRadius.circular(10), - ), - ), - MySpacing.height(12), - Text("Add Employee", - style: MyTextStyle.titleLarge(fontWeight: 700)), - MySpacing.height(24), - Form( - key: _controller.basicValidator.formKey, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _sectionLabel("Personal Info"), - MySpacing.height(16), - _inputWithIcon( - label: "First Name", - hint: "e.g., John", - icon: Icons.person, - controller: _controller.basicValidator - .getController('first_name')!, - validator: _controller.basicValidator - .getValidation('first_name'), - ), - MySpacing.height(16), - _inputWithIcon( - label: "Last Name", - hint: "e.g., Doe", - icon: Icons.person_outline, - controller: _controller.basicValidator - .getController('last_name')!, - validator: _controller.basicValidator - .getValidation('last_name'), - ), - MySpacing.height(16), - _sectionLabel("Contact Details"), - MySpacing.height(16), - MyText.labelMedium("Phone Number"), - MySpacing.height(8), - Row( - children: [ - Container( - padding: const EdgeInsets.symmetric( - horizontal: 12, vertical: 14), - decoration: BoxDecoration( - border: Border.all(color: Colors.grey.shade300), - borderRadius: BorderRadius.circular(12), - color: Colors.grey.shade100, - ), - child: PopupMenuButton>( - onSelected: (country) { - _controller.selectedCountryCode = - country['code']!; - _controller.update(); - }, - itemBuilder: (context) => [ - PopupMenuItem( - enabled: false, - padding: EdgeInsets.zero, - child: SizedBox( - height: 200, - width: 100, - child: ListView( - children: _controller.countries - .map((country) { - return ListTile( - dense: true, - title: Text( - "${country['name']} (${country['code']})"), - onTap: () => - Navigator.pop(context, country), - ); - }).toList(), - ), - ), - ), - ], - child: Row( - children: [ - Text(_controller.selectedCountryCode), - const Icon(Icons.arrow_drop_down), - ], - ), - ), - ), - MySpacing.width(12), - Expanded( - child: TextFormField( - controller: _controller.basicValidator - .getController('phone_number'), - validator: (value) { - if (value == null || value.trim().isEmpty) { - return "Phone number is required"; - } - - final digitsOnly = value.trim(); - final minLength = _controller - .minDigitsPerCountry[ - _controller.selectedCountryCode] ?? - 7; - final maxLength = _controller - .maxDigitsPerCountry[ - _controller.selectedCountryCode] ?? - 15; - - if (!RegExp(r'^[0-9]+$') - .hasMatch(digitsOnly)) { - return "Only digits allowed"; - } - - if (digitsOnly.length < minLength || - digitsOnly.length > maxLength) { - return "Between $minLength–$maxLength digits"; - } - - return null; - }, - keyboardType: TextInputType.phone, - inputFormatters: [ - // Allow only digits - FilteringTextInputFormatter.digitsOnly, - // Limit to 10 digits - LengthLimitingTextInputFormatter(10), - ], - decoration: _inputDecoration("e.g., 9876543210") - .copyWith( - suffixIcon: IconButton( - icon: const Icon(Icons.contacts), - onPressed: () => - _controller.pickContact(context), - ), - ), - ), - ), - ], - ), - MySpacing.height(24), - _sectionLabel("Other Details"), - MySpacing.height(16), - MyText.labelMedium("Gender"), - MySpacing.height(8), - GestureDetector( - onTap: () => _showGenderPopup(context), - child: AbsorbPointer( - child: TextFormField( - readOnly: true, - controller: TextEditingController( - text: _controller - .selectedGender?.name.capitalizeFirst, - ), - decoration: - _inputDecoration("Select Gender").copyWith( - suffixIcon: const Icon(Icons.expand_more), - ), - ), - ), - ), - MySpacing.height(16), - MyText.labelMedium("Role"), - MySpacing.height(8), - GestureDetector( - onTap: () => _showRolePopup(context), - child: AbsorbPointer( - child: TextFormField( - readOnly: true, - controller: TextEditingController( - text: _controller.roles.firstWhereOrNull( - (role) => - role['id'] == - _controller.selectedRoleId, - )?['name'] ?? - "", - ), - decoration: - _inputDecoration("Select Role").copyWith( - suffixIcon: const Icon(Icons.expand_more), - ), - ), - ), - ), - MySpacing.height(16), - Row( - children: [ - Expanded( - child: OutlinedButton.icon( - onPressed: () => Navigator.pop(context), - icon: - const Icon(Icons.close, color: Colors.red), - label: MyText.bodyMedium("Cancel", - color: Colors.red, fontWeight: 600), - style: OutlinedButton.styleFrom( - side: const BorderSide(color: Colors.red), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12)), - padding: const EdgeInsets.symmetric( - horizontal: 20, vertical: 14), - ), - ), - ), - const SizedBox(width: 12), - Expanded( - child: ElevatedButton.icon( - onPressed: () async { - if (_controller.basicValidator - .validateForm()) { - final result = - await _controller.createEmployees(); - - if (result != null && - result['success'] == true) { - final employeeData = result['data']; - final employeeController = - Get.find(); - final projectId = - employeeController.selectedProjectId; - - if (projectId == null) { - await employeeController - .fetchAllEmployees(); - } else { - await employeeController - .fetchEmployeesByProject(projectId); - } - - employeeController.update( - ['employee_screen_controller']); - - _controller.basicValidator - .getController("first_name") - ?.clear(); - _controller.basicValidator - .getController("last_name") - ?.clear(); - _controller.basicValidator - .getController("phone_number") - ?.clear(); - _controller.selectedGender = null; - _controller.selectedRoleId = null; - _controller.update(); - - Navigator.pop(context, employeeData); - } - } - }, - icon: const Icon(Icons.check_circle_outline, - color: Colors.white), - label: MyText.bodyMedium("Save", - color: Colors.white, fontWeight: 600), - style: ElevatedButton.styleFrom( - backgroundColor: Colors.indigo, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12)), - padding: const EdgeInsets.symmetric( - horizontal: 28, vertical: 14), - ), - ), - ), - ], - ), - ], - ), - ), - ], - ), - ), - ), + // Gender popup menu + void _showGenderPopup(BuildContext context) async { + final selected = await showMenu( + context: context, + position: _popupMenuPosition(context), + items: Gender.values.map((gender) { + return PopupMenuItem( + value: gender, + child: Text(gender.name.capitalizeFirst!), ); - }, + }).toList(), ); + + if (selected != null) { + _controller.onGenderSelected(selected); + _controller.update(); + } + } + + // Role popup menu + void _showRolePopup(BuildContext context) async { + final selected = await showMenu( + context: context, + position: _popupMenuPosition(context), + items: _controller.roles.map((role) { + return PopupMenuItem( + value: role['id'], + child: Text(role['name']), + ); + }).toList(), + ); + + if (selected != null) { + _controller.onRoleSelected(selected); + _controller.update(); + } + } + + RelativeRect _popupMenuPosition(BuildContext context) { + final RenderBox overlay = + Overlay.of(context).context.findRenderObject() as RenderBox; + return RelativeRect.fromLTRB(100, 300, overlay.size.width - 100, 0); } } diff --git a/lib/view/employees/assign_employee_bottom_sheet.dart b/lib/view/employees/assign_employee_bottom_sheet.dart index a689024..61440ea 100644 --- a/lib/view/employees/assign_employee_bottom_sheet.dart +++ b/lib/view/employees/assign_employee_bottom_sheet.dart @@ -2,9 +2,10 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:marco/helpers/widgets/my_spacing.dart'; import 'package:marco/helpers/widgets/my_text.dart'; +import 'package:marco/helpers/widgets/my_snackbar.dart'; import 'package:marco/controller/employee/assign_projects_controller.dart'; import 'package:marco/model/global_project_model.dart'; -import 'package:marco/helpers/widgets/my_snackbar.dart'; +import 'package:marco/helpers/utils/base_bottom_sheet.dart'; class AssignProjectBottomSheet extends StatefulWidget { final String employeeId; @@ -23,6 +24,7 @@ class AssignProjectBottomSheet extends StatefulWidget { class _AssignProjectBottomSheetState extends State { late final AssignProjectController assignController; + final ScrollController _scrollController = ScrollController(); @override void initState() { @@ -38,229 +40,139 @@ class _AssignProjectBottomSheetState extends State { @override Widget build(BuildContext context) { - final theme = Theme.of(context); + return GetBuilder( + tag: '${widget.employeeId}_${widget.jobRoleId}', + builder: (_) { + return BaseBottomSheet( + title: "Assign to Project", + onCancel: () => Navigator.pop(context), + onSubmit: _handleAssign, + submitText: "Assign", + child: Obx(() { + if (assignController.isLoading.value) { + return const Center(child: CircularProgressIndicator()); + } - return SafeArea( - top: false, - child: DraggableScrollableSheet( - expand: false, - maxChildSize: 0.9, - minChildSize: 0.4, - initialChildSize: 0.7, - builder: (_, scrollController) { - return Container( - decoration: BoxDecoration( - color: theme.cardColor, - borderRadius: - const BorderRadius.vertical(top: Radius.circular(24)), - boxShadow: const [ - BoxShadow( - color: Colors.black12, - blurRadius: 12, - offset: Offset(0, -2), + final projects = assignController.allProjects; + if (projects.isEmpty) { + return const Center(child: Text('No projects available.')); + } + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + MyText.bodySmall( + 'Select the projects to assign this employee.', + color: Colors.grey[600], + ), + MySpacing.height(8), + + // Select All + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + 'Projects (${projects.length})', + style: const TextStyle( + fontWeight: FontWeight.w600, + fontSize: 16, + ), + ), + TextButton( + onPressed: () { + assignController.toggleSelectAll(); + }, + child: Obx(() { + return Text( + assignController.areAllSelected() + ? 'Deselect All' + : 'Select All', + style: const TextStyle( + color: Colors.blueAccent, + fontWeight: FontWeight.w600, + ), + ); + }), + ), + ], + ), + + // List of Projects + SizedBox( + height: 300, + child: ListView.builder( + controller: _scrollController, + itemCount: projects.length, + itemBuilder: (context, index) { + final GlobalProjectModel project = projects[index]; + return Obx(() { + final bool isSelected = + assignController.isProjectSelected( + project.id.toString(), + ); + return Theme( + data: Theme.of(context).copyWith( + checkboxTheme: CheckboxThemeData( + fillColor: WidgetStateProperty.resolveWith( + (states) => states.contains(WidgetState.selected) + ? Colors.blueAccent + : Colors.white, + ), + side: const BorderSide( + color: Colors.black, + width: 2, + ), + checkColor: + WidgetStateProperty.all(Colors.white), + ), + ), + child: CheckboxListTile( + dense: true, + value: isSelected, + title: Text( + project.name, + style: const TextStyle( + fontWeight: FontWeight.w500, + fontSize: 14, + ), + ), + onChanged: (checked) { + assignController.toggleProjectSelection( + project.id.toString(), + checked ?? false, + ); + }, + activeColor: Colors.blueAccent, + controlAffinity: ListTileControlAffinity.leading, + contentPadding: EdgeInsets.zero, + visualDensity: const VisualDensity( + horizontal: -4, + vertical: -4, + ), + ), + ); + }); + }, + ), ), ], - ), - padding: MySpacing.all(16), - child: Obx(() { - if (assignController.isLoading.value) { - return const Center(child: CircularProgressIndicator()); - } - - final projects = assignController.allProjects; - if (projects.isEmpty) { - return const Center(child: Text('No projects available.')); - } - - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Drag Handle - Center( - child: Container( - width: 40, - height: 5, - decoration: BoxDecoration( - color: Colors.grey.shade300, - borderRadius: BorderRadius.circular(10), - ), - ), - ), - MySpacing.height(12), - - // Header - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - MyText.titleMedium('Assign to Project', fontWeight: 700), - ], - ), - MySpacing.height(4), - - // Sub Info - MyText.bodySmall( - 'Select the projects to assign this employee.', - color: Colors.grey[600], - ), - MySpacing.height(8), - - // Select All Toggle - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - 'Projects (${projects.length})', - style: const TextStyle( - fontWeight: FontWeight.w600, - fontSize: 16, - ), - ), - TextButton( - onPressed: () { - assignController.toggleSelectAll(); - }, - child: Obx(() { - return Text( - assignController.areAllSelected() - ? 'Deselect All' - : 'Select All', - style: const TextStyle( - color: Colors.blueAccent, - fontWeight: FontWeight.w600, - ), - ); - }), - ), - ], - ), - - // Project List - Expanded( - child: ListView.builder( - controller: scrollController, - itemCount: projects.length, - padding: EdgeInsets.zero, - itemBuilder: (context, index) { - final GlobalProjectModel project = projects[index]; - return Obx(() { - final bool isSelected = - assignController.isProjectSelected( - project.id.toString(), - ); - return Theme( - data: Theme.of(context).copyWith( - checkboxTheme: CheckboxThemeData( - fillColor: - WidgetStateProperty.resolveWith( - (states) { - if (states.contains(WidgetState.selected)) { - return Colors.blueAccent; - } - return Colors.white; - }, - ), - side: const BorderSide( - color: Colors.black, - width: 2, - ), - checkColor: - WidgetStateProperty.all(Colors.white), - ), - ), - child: CheckboxListTile( - dense: true, - value: isSelected, - title: Text( - project.name, - style: const TextStyle( - fontWeight: FontWeight.w500, - fontSize: 14, - ), - ), - onChanged: (checked) { - assignController.toggleProjectSelection( - project.id.toString(), - checked ?? false, - ); - }, - activeColor: Colors.blueAccent, - controlAffinity: ListTileControlAffinity.leading, - contentPadding: EdgeInsets.zero, - visualDensity: const VisualDensity( - horizontal: -4, - vertical: -4, - ), - ), - ); - }); - }, - ), - ), - MySpacing.height(16), - - // Cancel & Save Buttons - Row( - children: [ - Expanded( - child: OutlinedButton.icon( - onPressed: () => Navigator.pop(context), - icon: const Icon(Icons.close, color: Colors.red), - label: MyText.bodyMedium("Cancel", - color: Colors.red, fontWeight: 600), - style: OutlinedButton.styleFrom( - side: const BorderSide(color: Colors.red), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - padding: const EdgeInsets.symmetric( - horizontal: 10, - vertical: 7, - ), - ), - ), - ), - const SizedBox(width: 12), - Expanded( - child: ElevatedButton.icon( - onPressed: () async { - if (assignController.selectedProjects.isEmpty) { - showAppSnackbar( - title: "Error", - message: "Please select at least one project.", - type: SnackbarType.error, - ); - return; - } - await _assignProjects(); - }, - icon: const Icon(Icons.check_circle_outline, - color: Colors.white), - label: MyText.bodyMedium("Assign", - color: Colors.white, fontWeight: 600), - style: ElevatedButton.styleFrom( - backgroundColor: Colors.indigo, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - padding: const EdgeInsets.symmetric( - horizontal: 10, - vertical: 7, - ), - ), - ), - ), - ], - ), - ], - ); - }), - ); - }, - ), + ); + }), + ); + }, ); } - Future _assignProjects() async { + Future _handleAssign() async { + if (assignController.selectedProjects.isEmpty) { + showAppSnackbar( + title: "Error", + message: "Please select at least one project.", + type: SnackbarType.error, + ); + return; + } + final success = await assignController.assignProjectsToEmployee(); if (success) { Get.back();