import 'package:fbla_ui/api_logic.dart'; import 'package:fbla_ui/main.dart'; import 'package:fbla_ui/shared.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class CreateEditBusiness extends StatefulWidget { final Business? inputBusiness; const CreateEditBusiness({super.key, this.inputBusiness}); @override State createState() => _CreateEditBusinessState(); } class _CreateEditBusinessState extends State { late TextEditingController _nameController; late TextEditingController _websiteController; late TextEditingController _descriptionController; late TextEditingController _contactNameController; late TextEditingController _contactPhoneController; late TextEditingController _contactEmailController; late TextEditingController _notesController; late TextEditingController _locationNameController; late TextEditingController _locationAddressController; Business business = Business( id: 0, name: 'Business', description: 'Add details about the business below.', type: BusinessType.other, website: '', contactName: '', contactEmail: '', contactPhone: '', notes: '', locationName: '', locationAddress: '', ); bool _isLoading = false; @override void initState() { super.initState(); if (widget.inputBusiness != null) { business = Business.copy(widget.inputBusiness!); _nameController = TextEditingController(text: business.name); _descriptionController = TextEditingController(text: business.description); } else { _nameController = TextEditingController(); _descriptionController = TextEditingController(); } _websiteController = TextEditingController(text: business.website); _contactNameController = TextEditingController(text: business.contactName); _contactPhoneController = TextEditingController(text: business.contactPhone); _contactEmailController = TextEditingController(text: business.contactEmail); _notesController = TextEditingController(text: business.notes); _locationNameController = TextEditingController(text: business.locationName); _locationAddressController = TextEditingController(text: business.locationAddress); } final formKey = GlobalKey(); final TextEditingController businessTypeController = TextEditingController(); @override Widget build(BuildContext context) { return PopScope( canPop: !_isLoading, onPopInvoked: _handlePop, child: Form( key: formKey, child: Scaffold( appBar: AppBar( title: (widget.inputBusiness != null) ? Text('Edit ${widget.inputBusiness?.name}', maxLines: 1) : const Text('Add New Business'), ), floatingActionButton: FloatingActionButton( child: _isLoading ? const Padding( padding: EdgeInsets.all(16.0), child: CircularProgressIndicator( color: Colors.white, strokeWidth: 3.0, ), ) : const Icon(Icons.save), onPressed: () async { if (formKey.currentState!.validate()) { formKey.currentState?.save(); setState(() { _isLoading = true; }); String? result; // if (business.contactName == '') { // business.contactName = 'Contact ${business.name}'; // } if (widget.inputBusiness != null) { result = await editBusiness(business, jwt); } else { result = await createBusiness(business, jwt); } setState(() { _isLoading = false; }); if (result != null) { ScaffoldMessenger.of(context).showSnackBar(SnackBar( width: 400, behavior: SnackBarBehavior.floating, content: Text(result))); } else { Navigator.pushReplacement( context, MaterialPageRoute( builder: (context) => const MainApp())); } } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: const Text('Check field inputs!'), width: 200, behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10)), ), ); } }, ), body: ListView( children: [ Center( child: SizedBox( width: 1000, child: Column( children: [ ListTile( title: Text(business.name, textAlign: TextAlign.left, style: const TextStyle( fontSize: 24, fontWeight: FontWeight.bold)), subtitle: Text( business.description, textAlign: TextAlign.left, ), leading: ClipRRect( borderRadius: BorderRadius.circular(6.0), child: Image.network( width: 48, height: 48, 'https://logo.clearbit.com/${business.website}', errorBuilder: (BuildContext context, Object exception, StackTrace? stackTrace) { return getIconFromType(business.type, 48, Theme.of(context).colorScheme.onBackground); }), ), ), Card( child: Column( children: [ Padding( padding: const EdgeInsets.only( left: 8.0, right: 8.0), child: TextFormField( controller: _nameController, autovalidateMode: AutovalidateMode.onUserInteraction, maxLength: 30, onChanged: (inputName) { setState(() { business.name = inputName; }); }, onTapOutside: (PointerDownEvent event) { FocusScope.of(context).unfocus(); }, decoration: const InputDecoration( labelText: 'Business Name (required)', ), validator: (value) { if (value != null && value.isEmpty) { return 'Name is required'; } return null; }, ), ), Padding( padding: const EdgeInsets.only( left: 8.0, right: 8.0, bottom: 8.0), child: TextFormField( controller: _websiteController, autovalidateMode: AutovalidateMode.onUserInteraction, keyboardType: TextInputType.url, onChanged: (inputUrl) { setState(() { business.website = Uri.encodeFull(inputUrl .toLowerCase() .replaceAll('https://', '') .replaceAll('http://', '') .replaceAll('www.', '')); }); }, onTapOutside: (PointerDownEvent event) { FocusScope.of(context).unfocus(); }, decoration: const InputDecoration( labelText: 'Website (required)', ), validator: (value) { if (value != null && value.isEmpty) { return 'Website is required'; } return null; }, ), ), Padding( padding: const EdgeInsets.only( left: 8.0, right: 8.0), child: TextFormField( controller: _descriptionController, autovalidateMode: AutovalidateMode.onUserInteraction, maxLength: 500, maxLines: null, onChanged: (inputDesc) { setState(() { business.description = inputDesc; }); }, onTapOutside: (PointerDownEvent event) { FocusScope.of(context).unfocus(); }, decoration: const InputDecoration( labelText: 'Business Description (required)', ), validator: (value) { if (value != null && value.isEmpty) { return 'Description is required'; } return null; }, ), ), // Padding( // padding: const EdgeInsets.only( // left: 8.0, right: 8.0, bottom: 16.0), // child: Row( // children: [ // ElevatedButton( // style: ButtonStyle( // backgroundColor: // MaterialStateProperty.all( // Theme.of(context) // .colorScheme // .background)), // child: const Row( // children: [ // Icon(Icons.search), // Text('Search For Location'), // ], // ), // onPressed: () {}, // ), // const Padding( // padding: EdgeInsets.only( // left: 32.0, right: 32.0), // child: Text( // 'OR', // style: TextStyle(fontSize: 24), // ), // ), // Expanded( // child: Column( // children: [ // TextFormField( // controller: _locationNameController, // onChanged: (inputName) { // setState(() { // business.locationName = // inputName; // }); // }, // onTapOutside: // (PointerDownEvent event) { // FocusScope.of(context).unfocus(); // }, // decoration: const InputDecoration( // labelText: // 'Location Name (optional)', // ), // ), // TextFormField( // controller: // _locationAddressController, // onChanged: (inputAddr) { // setState(() { // business.locationAddress = // inputAddr; // }); // }, // onTapOutside: // (PointerDownEvent event) { // FocusScope.of(context).unfocus(); // }, // decoration: const InputDecoration( // labelText: // 'Location Address (optional)', // ), // ), // ], // ), // ), // ], // ), // ), Padding( padding: const EdgeInsets.only( left: 8.0, right: 8.0, bottom: 8.0), child: TextFormField( controller: _locationNameController, onChanged: (inputName) { setState(() { business.locationName = inputName; }); }, onTapOutside: (PointerDownEvent event) { FocusScope.of(context).unfocus(); }, decoration: const InputDecoration( labelText: 'Location Name', ), ), ), Padding( padding: const EdgeInsets.only( left: 8.0, right: 8.0, bottom: 16.0), child: TextFormField( controller: _locationAddressController, onChanged: (inputAddr) { setState(() { business.locationAddress = inputAddr; }); }, onTapOutside: (PointerDownEvent event) { FocusScope.of(context).unfocus(); }, decoration: const InputDecoration( labelText: 'Location Address', ), ), ), Padding( padding: const EdgeInsets.only( left: 8.0, right: 8.0, bottom: 8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text('Type of Business', style: TextStyle(fontSize: 16)), DropdownMenu( initialSelection: business.type, controller: businessTypeController, label: const Text('Business Type'), dropdownMenuEntries: const [ DropdownMenuEntry( value: BusinessType.food, label: 'Food Related'), DropdownMenuEntry( value: BusinessType.shop, label: 'Shop'), DropdownMenuEntry( value: BusinessType.outdoors, label: 'Outdoors'), DropdownMenuEntry( value: BusinessType.manufacturing, label: 'Manufacturing'), DropdownMenuEntry( value: BusinessType.entertainment, label: 'Entertainment'), DropdownMenuEntry( value: BusinessType.other, label: 'Other'), ], onSelected: (inputType) { business.type = inputType!; }, ), ], ), ), Padding( padding: const EdgeInsets.only( left: 8.0, right: 8.0, bottom: 8.0), child: TextFormField( controller: _contactNameController, onSaved: (inputText) { business.contactName = inputText!; }, onTapOutside: (PointerDownEvent event) { FocusScope.of(context).unfocus(); }, decoration: const InputDecoration( labelText: 'Contact Information Name', ), ), ), Padding( padding: const EdgeInsets.only( left: 8.0, right: 8.0, bottom: 8.0), child: TextFormField( controller: _contactPhoneController, inputFormatters: [PhoneFormatter()], keyboardType: TextInputType.phone, onSaved: (inputText) { business.contactPhone = inputText!; }, onTapOutside: (PointerDownEvent event) { FocusScope.of(context).unfocus(); }, decoration: const InputDecoration( labelText: 'Contact Phone # (optional)', ), ), ), Padding( padding: const EdgeInsets.only( left: 8.0, right: 8.0, bottom: 8.0), child: TextFormField( controller: _contactEmailController, keyboardType: TextInputType.emailAddress, onSaved: (inputText) { business.contactEmail = inputText!; }, onTapOutside: (PointerDownEvent event) { FocusScope.of(context).unfocus(); }, decoration: const InputDecoration( labelText: 'Contact Email', ), validator: (value) { if (value != null) { if (value.isEmpty) { return null; } else if (!RegExp( r'^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$') .hasMatch(value)) { return 'Enter a valid Email'; } else if (value.characters.length > 50) { return 'Contact Email cannot be longer than 50 characters'; } } return null; }, ), ), Padding( padding: const EdgeInsets.only( left: 8.0, right: 8.0, bottom: 8.0), child: TextFormField( controller: _notesController, maxLength: 300, maxLines: null, onSaved: (inputText) { business.notes = inputText!; }, onTapOutside: (PointerDownEvent event) { FocusScope.of(context).unfocus(); }, decoration: const InputDecoration( labelText: 'Other Notes', ), ), ), ], ), ), SizedBox( height: 75, ) ], ), ), ), ], ), )), ); } void _handlePop(bool didPop) { if (!didPop) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( width: 400, behavior: SnackBarBehavior.floating, content: Text('Please wait for it to save.'), ), ); } } } class PhoneFormatter extends TextInputFormatter { String phoneFormat(value) { String input = value.replaceAll(RegExp(r'[\D]'), ''); String phoneFormatted = input.isNotEmpty ? '(${input.substring(0, input.length >= 3 ? 3 : null)}${input.length >= 4 ? ') ' : ''}${input.length > 3 ? input.substring(3, input.length >= 5 ? 6 : null) + (input.length >= 7 ? '-${input.substring(6, input.length >= 10 ? 10 : null)}' : '') : ''}' : input; return phoneFormatted; } @override TextEditingValue formatEditUpdate( TextEditingValue oldValue, TextEditingValue newValue) { String text = newValue.text; if (newValue.selection.baseOffset == 0) { return newValue; } return newValue.copyWith( text: phoneFormat(text), selection: TextSelection.collapsed(offset: phoneFormat(text).length)); } }