import 'package:flutter/material.dart'; import 'package:marco/helpers/widgets/my_form_validator.dart'; import 'package:marco/helpers/services/auth_service.dart'; import 'package:marco/helpers/widgets/my_snackbar.dart'; import 'package:marco/helpers/utils/mixins/ui_mixin.dart'; import 'package:marco/helpers/widgets/my_text.dart'; class OrganizationFormBottomSheet { static void show(BuildContext context) { showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, builder: (_) => DraggableScrollableSheet( expand: false, initialChildSize: 0.85, minChildSize: 0.5, maxChildSize: 0.95, builder: (context, scrollController) => _OrganizationForm(scrollController: scrollController), ), ); } } class _OrganizationForm extends StatefulWidget { final ScrollController scrollController; const _OrganizationForm({required this.scrollController}); @override State<_OrganizationForm> createState() => _OrganizationFormState(); } class _OrganizationFormState extends State<_OrganizationForm> with UIMixin { final MyFormValidator validator = MyFormValidator(); bool _loading = false; bool _agreed = false; List> _industries = []; String? _selectedIndustryId; String? _selectedSize; final List _sizes = ['1-10', '11-50', '51-200', '201-1000', '1000+']; final Map _sizeApiMap = { '1-10': 'less than 10', '11-50': '11 to 50', '51-200': '51 to 200', '201-1000': 'more than 200', '1000+': 'more than 1000', }; @override void initState() { super.initState(); _loadIndustries(); _registerFields(); } void _registerFields() { validator.addField('organizationName', required: true, controller: TextEditingController()); validator.addField('email', required: true, controller: TextEditingController()); validator.addField('contactPerson', required: true, controller: TextEditingController()); validator.addField('contactNumber', required: true, controller: TextEditingController()); validator.addField('about', controller: TextEditingController()); validator.addField('address', required: true, controller: TextEditingController()); } Future _loadIndustries() async { final industries = await AuthService.getIndustries(); if (industries != null) { setState(() { _industries = industries; }); } } @override Widget build(BuildContext context) { return Container( decoration: const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.vertical(top: Radius.circular(20)), ), padding: const EdgeInsets.fromLTRB(20, 20, 20, 40), child: SingleChildScrollView( controller: widget.scrollController, child: Form( key: validator.formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Center( child: Container( width: 40, height: 5, margin: const EdgeInsets.only(bottom: 20), decoration: BoxDecoration( color: Colors.grey[300], borderRadius: BorderRadius.circular(10), ), ), ), Center( child: Column( children: [ MyText.titleLarge( 'Adventure starts here 🚀', fontWeight: 600, color: Colors.black87, ), const SizedBox(height: 4), MyText.bodySmall( "Make your app management easy and fun!", color: Colors.grey, ), ], ), ), const SizedBox(height: 20), _sectionHeader('Organization Info'), _buildTextField('organizationName', 'Organization Name'), _buildTextField('email', 'Email', keyboardType: TextInputType.emailAddress), _buildTextField('about', 'About Organization'), _sectionHeader('Contact Details'), _buildTextField('contactPerson', 'Contact Person'), _buildTextField('contactNumber', 'Contact Number', keyboardType: TextInputType.phone), _buildTextField('address', 'Current Address'), _sectionHeader('Additional Details'), _buildPopupMenuField( 'Organization Size', _sizes, _selectedSize, (val) => setState(() => _selectedSize = val), 'Please select organization size', ), _buildPopupMenuField( 'Industry', _industries.map((e) => e['name'] as String).toList(), _selectedIndustryId != null ? _industries.firstWhere( (e) => e['id'] == _selectedIndustryId)['name'] : null, (val) { setState(() { final selectedIndustry = _industries.firstWhere( (element) => element['name'] == val, orElse: () => {}, ); _selectedIndustryId = selectedIndustry['id']; }); }, 'Please select industry', ), const SizedBox(height: 12), Row( children: [ Checkbox( value: _agreed, onChanged: (val) => setState(() => _agreed = val ?? false), fillColor: MaterialStateProperty.resolveWith((states) => states.contains(MaterialState.selected) ? contentTheme.primary : Colors.white), checkColor: Colors.white, ), Row( children: [ MyText( 'I agree to the ', color: Colors.black87, ), MyText( 'privacy policy & terms', color: contentTheme.primary, fontWeight: 600, ), ], ) ], ), const SizedBox(height: 20), Row( children: [ Expanded( child: ElevatedButton.icon( onPressed: () => Navigator.pop(context), icon: const Icon(Icons.close, color: Colors.white), label: MyText.bodyMedium( "Cancel", color: Colors.white, fontWeight: 600, ), style: ElevatedButton.styleFrom( backgroundColor: Colors.grey, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), padding: const EdgeInsets.symmetric(vertical: 8), ), ), ), const SizedBox(width: 12), Expanded( child: ElevatedButton.icon( onPressed: _loading ? null : _submitForm, icon: Icon(Icons.check_circle_outline, color: Colors.white), label: MyText.bodyMedium( _loading ? "Submitting..." : "Submit", color: Colors.white, fontWeight: 600, ), style: ElevatedButton.styleFrom( backgroundColor: contentTheme .primary, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), padding: const EdgeInsets.symmetric(vertical: 8), ), ), ), ], ), const SizedBox(height: 8), Center( child: TextButton.icon( onPressed: () => Navigator.pop(context), icon: Icon(Icons.arrow_back, size: 18, color: contentTheme.primary), label: MyText.bodySmall( 'Back to log in', fontWeight: 600, color: contentTheme.primary, ), ), ), ], ), ), ), ); } Widget _sectionHeader(String title) { return Padding( padding: const EdgeInsets.only(top: 20, bottom: 8), child: MyText.titleSmall( title, fontWeight: 600, color: Colors.grey[800], ), ); } Widget _buildTextField(String fieldName, String label, {TextInputType keyboardType = TextInputType.text}) { final controller = validator.getController(fieldName); final defaultValidator = validator.getValidation(fieldName); String? Function(String?)? validatorFunc = defaultValidator; if (fieldName == 'contactNumber') { validatorFunc = (value) { if (value == null || value.isEmpty) return 'Contact number is required'; if (!RegExp(r'^\d{10}$').hasMatch(value)) return 'Enter a valid 10-digit contact number'; return null; }; } return Padding( padding: const EdgeInsets.symmetric(vertical: 6), child: TextFormField( controller: controller, keyboardType: keyboardType, validator: validatorFunc, style: const TextStyle(fontSize: 15, color: Colors.black87), decoration: InputDecoration( labelText: label, labelStyle: TextStyle(color: Colors.grey[700], fontSize: 14), filled: true, fillColor: Colors.grey[100], contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), border: OutlineInputBorder( borderRadius: BorderRadius.circular(5), borderSide: BorderSide(color: Colors.grey[400]!), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(5), borderSide: BorderSide(color: Colors.grey[300]!), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(5), borderSide: BorderSide(color: contentTheme.brandRed, width: 1.5), ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(5), borderSide: const BorderSide(color: Colors.red), ), ), ), ); } Widget _buildPopupMenuField( String label, List items, String? selectedValue, ValueChanged onSelected, String errorText, ) { final bool hasError = selectedValue == null; final GlobalKey _key = GlobalKey(); return Padding( padding: const EdgeInsets.symmetric(vertical: 6), child: FormField( validator: (value) => hasError ? errorText : null, builder: (fieldState) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ MyText.bodySmall(label, color: Colors.grey[700]), const SizedBox(height: 6), GestureDetector( key: _key, onTap: () async { final renderBox = _key.currentContext!.findRenderObject() as RenderBox; final offset = renderBox.localToGlobal(Offset.zero); final size = renderBox.size; final selected = await showMenu( context: fieldState.context, position: RelativeRect.fromLTRB( offset.dx, offset.dy + size.height, offset.dx + size.width, offset.dy, ), items: items .map((item) => PopupMenuItem( value: item, child: MyText.bodyMedium(item), )) .toList(), ); if (selected != null) { onSelected(selected); fieldState.didChange(selected); } }, child: InputDecorator( decoration: InputDecoration( filled: true, fillColor: Colors.grey[100], contentPadding: const EdgeInsets.symmetric( horizontal: 16, vertical: 16), border: OutlineInputBorder( borderRadius: BorderRadius.circular(5), borderSide: BorderSide(color: Colors.grey[400]!), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(5), borderSide: BorderSide(color: Colors.grey[300]!), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(5), borderSide: BorderSide(color: contentTheme.brandRed, width: 1.5), ), errorText: fieldState.errorText, ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ MyText.bodyMedium( selectedValue ?? 'Select $label', color: selectedValue == null ? Colors.grey : Colors.black, ), const Icon(Icons.arrow_drop_down, color: Colors.grey), ], ), ), ), ], ); }, ), ); } void _submitForm() async { bool isValid = validator.validateForm(); if (_selectedSize == null || _selectedIndustryId == null) { isValid = false; setState(() {}); } if (!_agreed) { isValid = false; showAppSnackbar( title: "Agreement Required", message: "Please agree to the privacy policy & terms", type: SnackbarType.warning, ); } if (!isValid) return; setState(() => _loading = true); final formData = validator.getData(); final requestBody = { 'organizatioinName': formData['organizationName'], 'email': formData['email'], 'about': formData['about'], 'contactNumber': formData['contactNumber'], 'contactPerson': formData['contactPerson'], 'industryId': _selectedIndustryId ?? '', 'oragnizationSize': _sizeApiMap[_selectedSize] ?? '', 'terms': _agreed, 'address': formData['address'], }; final error = await AuthService.requestDemo(requestBody); setState(() => _loading = false); if (error == null) { showAppSnackbar( title: "Success", message: "Demo request submitted successfully!", type: SnackbarType.success, ); Navigator.pop(context); } else { showAppSnackbar( title: "Error", message: error['error'] ?? 'Unknown error occurred', type: SnackbarType.error, ); } } }