import 'package:flutter/material.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart'; import 'package:intl_phone_field/intl_phone_field.dart'; import '../../../../core/network/dio_client.dart'; import '../../dashboard/presentation/dashboard_screen.dart'; import 'package:url_launcher/url_launcher.dart'; class SetupWizardScreen extends StatefulWidget { const SetupWizardScreen({super.key}); @override State createState() => _SetupWizardScreenState(); } class _SetupWizardScreenState extends State with SingleTickerProviderStateMixin { bool _isLoading = false; int _currentStep = 0; String _accountType = 'INDIVIDUAL'; // Step 1: Personal Info final _nameController = TextEditingController(); final _usernameController = TextEditingController(); String _mobileNumber = ''; bool _isUsernameAvailable = false; bool _checkingUsername = false; // Step 2: Business Info (if BUSINESS) String? _natureOfBusiness; final _businessNameController = TextEditingController(); final _addressController = TextEditingController(); final _emailController = TextEditingController(); final _gstController = TextEditingController(); final _panController = TextEditingController(); final _msmeController = TextEditingController(); // Step 3: Consent bool _termsAccepted = false; bool _privacyAccepted = false; int get _totalSteps => _accountType == 'BUSINESS' ? 4 : 3; Future _checkUsername(String username) async { if (username.length < 3) return; setState(() => _checkingUsername = true); try { final res = await DioClient().dio.get( '/account/username/availability', queryParameters: {'username': username}, ); if (mounted) { setState(() { _isUsernameAvailable = res.data['available'] == true; _checkingUsername = false; }); } } catch (e) { if (mounted) setState(() => _checkingUsername = false); } } Future _submitSetup() async { if (!_termsAccepted || !_privacyAccepted) return; setState(() => _isLoading = true); try { final data = { 'accountType': _accountType, 'name': _nameController.text, 'username': _usernameController.text, 'mobileNumber': _mobileNumber, 'termsAccepted': _termsAccepted, 'privacyAccepted': _privacyAccepted, }; if (_accountType == 'BUSINESS') { data.addAll({ 'businessName': _businessNameController.text.isNotEmpty ? _businessNameController.text : _nameController.text, 'natureOfBusiness': _natureOfBusiness!, 'address': _addressController.text, 'emailId': _emailController.text, 'gstin': _gstController.text, 'panNumber': _panController.text, 'msmeNumber': _msmeController.text, }); } await DioClient().dio.post('/account/setup', data: data); if (mounted) { Navigator.pushReplacement( context, MaterialPageRoute(builder: (_) => const DashboardScreen()), ); } } catch (e) { if (mounted) setState(() => _isLoading = false); if (mounted) { ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text('Setup failed: $e'))); } } } Future _launchUrl(String path) async { final url = Uri.parse('http://192.168.0.104:8080/public/legal/$path'); if (await canLaunchUrl(url)) { await launchUrl(url); } } void _nextStep() { if (_currentStep == 1) { if (_nameController.text.trim().isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Please enter your Full Name')), ); return; } if (_usernameController.text.trim().length < 3) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Username must be at least 3 characters'), ), ); return; } if (!_isUsernameAvailable) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Username is unavailable or invalid')), ); return; } } if (_accountType == 'BUSINESS' && _currentStep == 2) { if (_natureOfBusiness == null) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Please select Nature of Business')), ); return; } } if (_currentStep < _totalSteps - 1) { setState(() => _currentStep++); } else { _submitSetup(); } } void _prevStep() { if (_currentStep > 0) { setState(() => _currentStep--); } } InputDecoration _buildInputDecoration(String label) { return InputDecoration( labelText: label, filled: true, fillColor: Colors.white, border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade300), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade300), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Theme.of(context).primaryColor, width: 2), ), contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 18), ); } Widget _buildProgressIndicator() { return Padding( padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Step ${_currentStep + 1} of $_totalSteps', style: TextStyle( color: Theme.of(context).primaryColor, fontWeight: FontWeight.bold, fontSize: 14, ), ), const SizedBox(height: 8), ClipRRect( borderRadius: BorderRadius.circular(4), child: LinearProgressIndicator( value: (_currentStep + 1) / _totalSteps, minHeight: 6, backgroundColor: Theme.of(context).primaryColor.withOpacity(0.1), valueColor: AlwaysStoppedAnimation( Theme.of(context).primaryColor, ), ), ), ], ), ); } Widget _buildStep0AccountType() { return Column( key: const ValueKey(0), crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const Text( 'Welcome to Kifi!', style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold), ), const SizedBox(height: 8), const Text( 'How will you be using the application?', style: TextStyle(fontSize: 16, color: Colors.grey), ), const SizedBox(height: 32), _buildAccountTypeCard( 'INDIVIDUAL', 'Individual', 'For personal use and expenses', LucideIcons.user, ), const SizedBox(height: 16), _buildAccountTypeCard( 'BUSINESS', 'Business', 'For managing business and inventory', LucideIcons.building2, ), ], ); } Widget _buildAccountTypeCard( String type, String title, String subtitle, IconData icon, ) { final isSelected = _accountType == type; return InkWell( onTap: () => setState(() => _accountType = type), borderRadius: BorderRadius.circular(16), child: AnimatedContainer( duration: const Duration(milliseconds: 200), padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: isSelected ? Theme.of(context).primaryColor.withOpacity(0.08) : Colors.white, borderRadius: BorderRadius.circular(16), border: Border.all( color: isSelected ? Theme.of(context).primaryColor : Colors.grey.shade300, width: isSelected ? 2 : 1, ), boxShadow: isSelected ? [] : [ BoxShadow( color: Colors.black.withOpacity(0.03), blurRadius: 10, offset: const Offset(0, 4), ), ], ), child: Row( children: [ Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: isSelected ? Theme.of(context).primaryColor : Colors.grey.shade100, shape: BoxShape.circle, ), child: Icon( icon, color: isSelected ? Colors.white : Colors.grey[600], size: 28, ), ), const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 4), Text( subtitle, style: TextStyle(color: Colors.grey[600], fontSize: 14), ), ], ), ), Icon( isSelected ? LucideIcons.checkCircle2 : LucideIcons.circle, color: isSelected ? Theme.of(context).primaryColor : Colors.grey[300], size: 28, ), ], ), ), ); } Widget _buildStep1Personal() { return Column( key: const ValueKey(1), crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const Text( 'Personal Details', style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold), ), const SizedBox(height: 8), const Text( 'Tell us a bit about yourself.', style: TextStyle(fontSize: 16, color: Colors.grey), ), const SizedBox(height: 32), TextFormField( controller: _nameController, decoration: _buildInputDecoration('Full Name *'), textInputAction: TextInputAction.next, ), const SizedBox(height: 20), TextFormField( controller: _usernameController, decoration: _buildInputDecoration('Username *').copyWith( suffixIcon: SizedBox( width: 48, height: 48, child: Center( child: AnimatedSwitcher( duration: const Duration(milliseconds: 200), child: _checkingUsername ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2), ) : Icon( _isUsernameAvailable ? LucideIcons.checkCircle2 : LucideIcons.xCircle, key: ValueKey(_isUsernameAvailable), color: _usernameController.text.isEmpty ? Colors.transparent : (_isUsernameAvailable ? Colors.green : Colors.red), ), ), ), ), ), onChanged: _checkUsername, textInputAction: TextInputAction.next, ), const SizedBox(height: 20), IntlPhoneField( decoration: _buildInputDecoration('Mobile Number'), initialCountryCode: 'IN', onChanged: (phone) => _mobileNumber = phone.completeNumber, ), ], ); } Widget _buildStep2Business() { return Column( key: const ValueKey(2), crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const Text( 'Business Profile', style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold), ), const SizedBox(height: 8), const Text( 'Set up your company details for invoicing and tracking.', style: TextStyle(fontSize: 16, color: Colors.grey), ), const SizedBox(height: 32), DropdownButtonFormField( decoration: _buildInputDecoration('Nature of Business *'), dropdownColor: Colors.white, value: _natureOfBusiness, items: const [ DropdownMenuItem( value: 'JEWELLERY', child: Text('Jewellery', style: TextStyle(color: Colors.black87)), ), DropdownMenuItem( value: 'PROJECT_MANAGEMENT', child: Text( 'Project Management', style: TextStyle(color: Colors.black87), ), ), DropdownMenuItem( value: 'INVENTORY_MANAGEMENT', child: Text( 'Inventory Management', style: TextStyle(color: Colors.black87), ), ), ], onChanged: (val) => setState(() => _natureOfBusiness = val), ), const SizedBox(height: 20), TextFormField( controller: _businessNameController, decoration: _buildInputDecoration('Business Name (Optional)'), textInputAction: TextInputAction.next, ), const SizedBox(height: 20), TextFormField( controller: _addressController, decoration: _buildInputDecoration('Business Address (Optional)'), maxLines: 3, textInputAction: TextInputAction.newline, ), const SizedBox(height: 20), TextFormField( controller: _emailController, decoration: _buildInputDecoration('Official Email (Optional)'), keyboardType: TextInputType.emailAddress, textInputAction: TextInputAction.next, ), const SizedBox(height: 20), TextFormField( controller: _gstController, decoration: _buildInputDecoration('GST Number (Optional)'), textCapitalization: TextCapitalization.characters, textInputAction: TextInputAction.next, ), const SizedBox(height: 20), TextFormField( controller: _panController, decoration: _buildInputDecoration('PAN (Optional)'), textCapitalization: TextCapitalization.characters, textInputAction: TextInputAction.next, ), const SizedBox(height: 20), TextFormField( controller: _msmeController, decoration: _buildInputDecoration('MSME/Udyam Number (Optional)'), textCapitalization: TextCapitalization.characters, textInputAction: TextInputAction.done, ), ], ); } Widget _buildStep3Consent() { return Column( key: const ValueKey(3), crossAxisAlignment: CrossAxisAlignment.stretch, children: [ const Text( 'Final Step', style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold), ), const SizedBox(height: 8), const Text( 'Please review and accept our policies to continue.', style: TextStyle(fontSize: 16, color: Colors.grey), ), const SizedBox(height: 32), Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), border: Border.all(color: Colors.grey.shade300), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.02), blurRadius: 10, offset: const Offset(0, 4), ), ], ), child: Column( children: [ CheckboxListTile( title: InkWell( onTap: () => _launchUrl('terms'), child: const Text( 'I accept the Terms & Conditions', style: TextStyle( color: Colors.blue, decoration: TextDecoration.underline, fontSize: 15, fontWeight: FontWeight.w500, ), ), ), value: _termsAccepted, controlAffinity: ListTileControlAffinity.leading, activeColor: Theme.of(context).primaryColor, onChanged: (val) => setState(() => _termsAccepted = val ?? false), contentPadding: const EdgeInsets.symmetric( horizontal: 16, vertical: 8, ), ), Divider(height: 1, indent: 56, color: Colors.grey.shade200), CheckboxListTile( title: InkWell( onTap: () => _launchUrl('privacy'), child: const Text( 'I accept the Privacy Policy', style: TextStyle( color: Colors.blue, decoration: TextDecoration.underline, fontSize: 15, fontWeight: FontWeight.w500, ), ), ), value: _privacyAccepted, controlAffinity: ListTileControlAffinity.leading, activeColor: Theme.of(context).primaryColor, onChanged: (val) => setState(() => _privacyAccepted = val ?? false), contentPadding: const EdgeInsets.symmetric( horizontal: 16, vertical: 8, ), ), ], ), ), ], ); } Widget _buildCurrentStep() { if (_currentStep == 0) return _buildStep0AccountType(); if (_currentStep == 1) return _buildStep1Personal(); if (_accountType == 'BUSINESS' && _currentStep == 2) return _buildStep2Business(); return _buildStep3Consent(); // Step 2 (Individual) or Step 3 (Business) } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.grey.shade50, appBar: AppBar( elevation: 0, backgroundColor: Colors.transparent, leading: _currentStep > 0 ? IconButton( icon: const Icon(LucideIcons.chevronLeft), onPressed: _prevStep, ) : null, ), body: SafeArea( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _buildProgressIndicator(), Expanded( child: Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 600), child: AnimatedSwitcher( duration: const Duration(milliseconds: 300), switchInCurve: Curves.easeOutCubic, switchOutCurve: Curves.easeInCubic, transitionBuilder: (child, animation) { return SlideTransition( position: Tween( begin: const Offset(0.05, 0), end: Offset.zero, ).animate(animation), child: FadeTransition(opacity: animation, child: child), ); }, child: SingleChildScrollView( key: ValueKey(_currentStep), physics: const BouncingScrollPhysics(), padding: const EdgeInsets.symmetric( horizontal: 24.0, vertical: 16.0, ), child: _buildCurrentStep(), ), ), ), ), ), Container( width: double.infinity, padding: const EdgeInsets.symmetric( horizontal: 24.0, vertical: 24.0, ), decoration: BoxDecoration( color: Colors.white, border: Border(top: BorderSide(color: Colors.grey.shade200)), ), child: Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 600), child: SizedBox( width: double.infinity, child: ElevatedButton( onPressed: _isLoading ? null : _nextStep, style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 20), backgroundColor: Theme.of(context).primaryColor, foregroundColor: Colors.white, elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), ), child: _isLoading ? const SizedBox( height: 24, width: 24, child: CircularProgressIndicator( color: Colors.white, strokeWidth: 2, ), ) : Text( _currentStep == _totalSteps - 1 ? 'Complete Setup' : 'Continue', style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, letterSpacing: 0.5, ), ), ), ), ), ), ), ], ), ), ); } }