import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../../core/errors/failure.dart'; import '../../../../core/utils/validators.dart'; import '../../../../shared/models/user_management_models.dart'; import '../../../../shared/widgets/app_button.dart'; import '../../../../shared/widgets/app_dropdown.dart'; import '../../../../shared/widgets/app_loading_view.dart'; import '../../../../shared/widgets/app_searchable_dropdown.dart'; import '../../../../shared/widgets/app_searchable_multi_select_dropdown.dart'; import '../../../../shared/widgets/app_text_field.dart'; import '../../../../shared/widgets/error_view.dart'; import '../../../../shared/widgets/app_side_panel.dart'; import '../../../master_data/presentation/widgets/master_quick_add.dart'; import '../providers/add_user_form_provider.dart'; import '../../../../shared/widgets/app_toast.dart'; class AddUserPanel extends ConsumerStatefulWidget { const AddUserPanel({super.key, this.userId}); final String? userId; bool get isEditing => userId != null; @override ConsumerState createState() => _AddUserPanelState(); } class _AddUserPanelState extends ConsumerState { final _formKey = GlobalKey(); final _nameController = TextEditingController(); final _employeeCodeController = TextEditingController(); final _emailController = TextEditingController(); final _mobileController = TextEditingController(); final _passwordController = TextEditingController(); List _selectedRoleIds = []; String _selectedStatus = 'Active'; String? _selectedDepartmentId; String? _selectedPlantId; String? _selectedDesignationId; String? _selectedReportingToId; bool _prefilled = false; bool _obscurePassword = true; static const _statusOptions = [ AppDropdownOption(value: 'Active', label: 'Active'), AppDropdownOption(value: 'Inactive', label: 'Inactive'), AppDropdownOption(value: 'Locked', label: 'Locked'), ]; @override void dispose() { _nameController.dispose(); _employeeCodeController.dispose(); _emailController.dispose(); _mobileController.dispose(); _passwordController.dispose(); super.dispose(); } String _statusToApi(String label) => switch (label) { 'Active' => 'active', 'Inactive' => 'inactive', 'Locked' => 'locked', _ => 'active', }; String _statusFromApi(String status) => switch (status.toLowerCase()) { 'active' => 'Active', 'inactive' => 'Inactive', 'locked' => 'Locked', _ => 'Active', }; void _prefillFromUser(ManagedUserModel user) { if (_prefilled) return; _prefilled = true; _nameController.text = user.fullName; _employeeCodeController.text = user.employeeCode; _emailController.text = user.email; _mobileController.text = user.mobile; _selectedRoleIds = user.effectiveRoleIds; _selectedDepartmentId = user.departmentId; _selectedDesignationId = user.designationId; _selectedPlantId = user.plantId; _selectedReportingToId = user.reportingTo; _selectedStatus = _statusLabelFromUser(user); } String _statusLabelFromUser(ManagedUserModel user) { final normalized = user.status.trim().toLowerCase(); if (normalized.isNotEmpty && normalized != 'active') { return _statusFromApi(user.status); } if (!user.isActive) return 'Inactive'; return _statusFromApi(user.status); } List> _toOptions(List items) { return items .map((item) => AppDropdownOption(value: item.id, label: item.name)) .toList(); } Future _save() async { if (!_formKey.currentState!.validate()) return; final roleIds = _selectedRoleIds .map((id) => int.tryParse(id)) .whereType() .toList(); if (roleIds.isEmpty) { showAppToastFromSnackBar(context, const SnackBar(content: Text('Please select at least one role')), ); return; } final notifier = ref.read(addUserFormProvider(widget.userId).notifier); final bool success; if (widget.isEditing) { success = await notifier.submitUpdate( widget.userId!, UpdateUserRequest( employeeCode: _employeeCodeController.text.trim(), fullName: _nameController.text.trim(), email: _emailController.text.trim(), password: _passwordController.text.isEmpty ? null : _passwordController.text, mobile: _mobileController.text.trim().isEmpty ? null : _mobileController.text.trim(), roleId: roleIds.first, roleIds: roleIds, departmentId: int.tryParse(_selectedDepartmentId ?? ''), designationId: int.tryParse(_selectedDesignationId ?? ''), plantId: int.tryParse(_selectedPlantId ?? ''), reportingTo: int.tryParse(_selectedReportingToId ?? ''), status: _statusToApi(_selectedStatus), isActive: _selectedStatus == 'Active', ), ); } else { success = await notifier.submitCreate( CreateUserRequest( employeeCode: _employeeCodeController.text.trim(), fullName: _nameController.text.trim(), email: _emailController.text.trim(), password: _passwordController.text, mobile: _mobileController.text.trim().isEmpty ? null : _mobileController.text.trim(), roleId: roleIds.first, roleIds: roleIds, departmentId: int.tryParse(_selectedDepartmentId ?? ''), designationId: int.tryParse(_selectedDesignationId ?? ''), plantId: int.tryParse(_selectedPlantId ?? ''), reportingTo: int.tryParse(_selectedReportingToId ?? ''), status: _statusToApi(_selectedStatus), isActive: _selectedStatus == 'Active', ), ); } if (!mounted) return; if (success) { Navigator.of(context, rootNavigator: true).pop(true); return; } final error = ref.read(addUserFormProvider(widget.userId)).valueOrNull?.errorMessage; showAppToastFromSnackBar(context, SnackBar( content: Text( error ?? (widget.isEditing ? 'Failed to update user' : 'Failed to create user'), ), ), ); } Widget _buildDropdown({ required String label, required String? value, required List options, required ValueChanged onChanged, String? hint, bool required = false, String? masterId, Map? initialValues, }) { final fieldLabel = required ? '$label *' : label; final fieldHint = hint ?? 'Select ${label.toLowerCase()}'; final fieldEnabled = options.isNotEmpty || masterId != null; final validator = required ? (String? v) => v == null ? 'Please select $label' : null : null; if (masterId == null) { return AppSearchableDropdown( label: fieldLabel, value: value, hint: fieldHint, searchHint: 'Search $label...', enabled: fieldEnabled, options: _toOptions(options), onChanged: onChanged, validator: validator, ); } return MasterQuickAddDropdown( masterId: masterId, label: fieldLabel, value: value, hint: fieldHint, searchHint: 'Search $label...', enabled: fieldEnabled, options: _toOptions(options), initialValues: initialValues, refreshLookups: () => ref.invalidate(addUserFormProvider(widget.userId)), parseCreatedId: (id) => id, onChanged: onChanged, validator: validator, ); } @override Widget build(BuildContext context) { final formAsync = ref.watch(addUserFormProvider(widget.userId)); final isSubmitting = formAsync.valueOrNull?.isSubmitting ?? false; return SidePanelScaffold( title: widget.isEditing ? 'Edit User' : 'Add User', footer: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ OutlinedButton( onPressed: isSubmitting ? null : () => Navigator.of(context, rootNavigator: true).pop(), child: const Text('Cancel'), ), const SizedBox(width: 12), AppButton( label: widget.isEditing ? 'Update User' : 'Save User', expand: false, icon: Icons.check, isLoading: isSubmitting, onPressed: isSubmitting ? null : _save, ), ], ), child: formAsync.when( loading: () => AppLoadingView( message: widget.isEditing ? 'Loading user...' : 'Loading form options...', ), error: (error, _) => ErrorView.fromFailure( error is Failure ? error : Failure.unknown(message: error.toString()), onRetry: () => ref.invalidate(addUserFormProvider(widget.userId)), ), data: (formState) { if (formState.editingUser != null) { _prefillFromUser(formState.editingUser!); } return Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ SidePanelFormRow( left: AppTextField( controller: _nameController, label: 'Full Name *', hint: 'e.g. Ravi Kumar', validator: (v) => Validators.required(v, fieldName: 'Name'), ), right: AppTextField( controller: _employeeCodeController, label: 'Employee Code *', hint: 'e.g. EMP002', validator: (v) => Validators.required(v, fieldName: 'Employee Code'), ), ), SidePanelFormRow( left: AppTextField( controller: _emailController, label: 'Email *', hint: 'ravi@company.com', keyboardType: TextInputType.emailAddress, validator: Validators.email, ), right: AppTextField( controller: _mobileController, label: 'Mobile', hint: '9XXXXXXXXX', keyboardType: TextInputType.phone, validator: Validators.optionalMobile, inputFormatters: Validators.mobileInput, ), ), const SizedBox(height: 12), SidePanelSection( title: 'ROLE & ACCESS', children: [ SidePanelFormRow( left: AppSearchableMultiSelectDropdown( label: 'Role *', values: _selectedRoleIds, hint: formState.roles.isEmpty ? 'No roles available' : 'Select roles', searchHint: 'Search role...', enabled: formState.roles.isNotEmpty, options: _toOptions(formState.roles), onChanged: (ids) => setState(() => _selectedRoleIds = ids), validator: (ids) => ids == null || ids.isEmpty ? 'Please select at least one role' : null, ), right: AppSearchableDropdown( label: 'Status', value: _selectedStatus, options: _statusOptions, searchHint: 'Search status...', onChanged: (v) => setState(() => _selectedStatus = v ?? 'Active'), ), ), ], ), SidePanelSection( title: 'ORGANISATION', children: [ SidePanelFormRow( left: _buildDropdown( label: 'Department', value: _selectedDepartmentId, options: formState.departments, masterId: 'departments', onChanged: (v) => setState(() => _selectedDepartmentId = v), ), right: _buildDropdown( label: 'Designation', value: _selectedDesignationId, options: formState.designations, masterId: 'designations', onChanged: (v) => setState(() => _selectedDesignationId = v), ), ), SidePanelFormRow( left: _buildDropdown( label: 'Plant / Unit', value: _selectedPlantId, options: formState.plants, masterId: 'locations', initialValues: const {'type': 'plant'}, onChanged: (v) => setState(() => _selectedPlantId = v), ), right: _buildDropdown( label: 'Reporting To', value: _selectedReportingToId, options: formState.managers, hint: formState.managers.isEmpty ? 'No managers available' : 'Select manager', onChanged: (v) => setState(() => _selectedReportingToId = v), ), ), ], ), SidePanelSection( title: 'PASSWORD', children: [ AppTextField( controller: _passwordController, label: widget.isEditing ? 'New Password' : 'Temporary Password *', hint: '8+ chars, upper, lower, digit, special', obscureText: _obscurePassword, suffixIcon: IconButton( tooltip: _obscurePassword ? 'Show password' : 'Hide password', icon: Icon( _obscurePassword ? Icons.visibility_outlined : Icons.visibility_off_outlined, size: 20, ), onPressed: () => setState( () => _obscurePassword = !_obscurePassword, ), ), validator: widget.isEditing ? Validators.optionalPassword : Validators.password, ), const SizedBox(height: 4), Text( widget.isEditing ? 'Leave blank to keep the current password.' : 'User will be asked to change this on first login.', style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Theme.of(context).colorScheme.onSurfaceVariant, ), ), ], ), ], ), ); }, ), ); } }