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/permission_matrix_models.dart'; import '../../../../shared/models/user_management_models.dart'; import '../../../../shared/widgets/app_button.dart'; import '../../../../shared/widgets/app_loading_view.dart'; import '../../../../shared/widgets/app_text_field.dart'; import '../../../../shared/widgets/error_view.dart'; import '../../../../shared/widgets/app_side_panel.dart'; import '../providers/role_form_provider.dart'; import 'rbac_widgets.dart'; import '../../../../shared/widgets/app_toast.dart'; class RoleFormPanel extends ConsumerStatefulWidget { const RoleFormPanel({super.key, this.roleId}); final String? roleId; bool get isEditing => roleId != null; @override ConsumerState createState() => _RoleFormPanelState(); } class _RoleFormPanelState extends ConsumerState { final _formKey = GlobalKey(); final _nameController = TextEditingController(); final _descriptionController = TextEditingController(); final Map _viewPermissions = {}; bool _prefilled = false; bool _permissionsInitialized = false; @override void dispose() { _nameController.dispose(); _descriptionController.dispose(); super.dispose(); } void _prefillFromRole(RoleCardModel role) { if (_prefilled) return; _prefilled = true; _nameController.text = role.name; _descriptionController.text = role.description ?? ''; } Future _save() async { if (!_formKey.currentState!.validate()) return; final formState = ref.read(roleFormProvider(widget.roleId)).valueOrNull; if (formState == null) return; final notifier = ref.read(roleFormProvider(widget.roleId).notifier); final bool success; if (widget.isEditing) { success = await notifier.submitUpdate( widget.roleId!, UpdateRoleRequest( name: _nameController.text.trim(), description: _descriptionController.text.trim(), ), ); } else { success = await notifier.submitCreate( buildCreateRoleRequest( name: _nameController.text.trim(), description: _descriptionController.text.trim(), selectedModules: _viewPermissions, catalog: formState.permissionCatalog, ), ); } if (!mounted) return; if (success) { Navigator.of(context, rootNavigator: true).pop(true); return; } final error = ref.read(roleFormProvider(widget.roleId)).valueOrNull?.errorMessage; showAppToastFromSnackBar(context, SnackBar( content: Text( error ?? (widget.isEditing ? 'Failed to update role' : 'Failed to create role'), ), ), ); } @override Widget build(BuildContext context) { final formAsync = ref.watch(roleFormProvider(widget.roleId)); final isSubmitting = formAsync.valueOrNull?.isSubmitting ?? false; return SidePanelScaffold( title: widget.isEditing ? 'Edit Role' : 'Create New Role', footer: Row( children: [ Expanded( child: OutlinedButton( onPressed: isSubmitting ? null : () => Navigator.of(context, rootNavigator: true).pop(), child: const Text('Cancel'), ), ), const SizedBox(width: 12), Expanded( child: AppButton( label: widget.isEditing ? 'Update Role' : 'Save Role', expand: true, isLoading: isSubmitting, onPressed: isSubmitting ? null : _save, ), ), ], ), child: formAsync.when( loading: () => AppLoadingView( message: widget.isEditing ? 'Loading role...' : 'Loading form...', ), error: (error, _) => ErrorView.fromFailure( error is Failure ? error : Failure.unknown(message: error.toString()), onRetry: () => ref.invalidate(roleFormProvider(widget.roleId)), ), data: (formState) { if (!widget.isEditing && !_permissionsInitialized && formState.selectableModules.isNotEmpty) { WidgetsBinding.instance.addPostFrameCallback((_) { if (!mounted || _permissionsInitialized) return; setState(() { _permissionsInitialized = true; for (final module in formState.selectableModules) { _viewPermissions.putIfAbsent(module.code, () => false); } }); }); } if (formState.editingRole != null && !_prefilled) { WidgetsBinding.instance.addPostFrameCallback((_) { if (!mounted || _prefilled) return; setState(() => _prefillFromRole(formState.editingRole!)); }); } return Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ AppTextField( controller: _nameController, label: 'Role Name *', hint: 'e.g. QC Manager', validator: Validators.roleName, inputFormatters: Validators.roleNameInput, ), const SizedBox(height: 16), AppTextField( controller: _descriptionController, label: 'Description', hint: 'What does this role do?', maxLines: 3, ), if (!widget.isEditing) ...[ const SizedBox(height: 24), Text( 'INITIAL PERMISSIONS', style: Theme.of(context).textTheme.labelSmall?.copyWith( fontWeight: FontWeight.w700, letterSpacing: 0.8, color: Theme.of(context).colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 8), Text( 'You can fine-tune permissions in the Permission Matrix tab after creating the role.', style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Theme.of(context).colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 16), if (formState.selectableModules.isEmpty) Text( 'No view permissions available from the server.', style: Theme.of(context).textTheme.bodySmall?.copyWith( color: Theme.of(context).colorScheme.onSurfaceVariant, ), ) else ...formState.selectableModules.asMap().entries.map((entry) { final module = entry.value; final appearance = permissionModuleAppearance(module.code, entry.key); return ModulePermissionRow( icon: appearance.icon, color: appearance.color, label: module.name, value: _viewPermissions[module.code] ?? false, onChanged: (v) => setState(() => _viewPermissions[module.code] = v), ); }), ], ], ), ); }, ), ); } } /// Backwards-compatible alias. typedef CreateRolePanel = RoleFormPanel;