import 'package:flutter/material.dart'; import 'dart:async'; import '../services/odoo_service.dart'; import '../services/config.dart'; import '../theme/app_theme.dart'; import '../widgets/agreement_dialog.dart'; class SignupScreen extends StatefulWidget { const SignupScreen({super.key}); @override State createState() => _SignupScreenState(); } class _SignupScreenState extends State { final _nameController = TextEditingController(); final _phoneController = TextEditingController(); final _emailController = TextEditingController(); final _otpController = TextEditingController(); final _passwordController = TextEditingController(); final _confirmPasswordController = TextEditingController(); DateTime? _selectedDate; String? _selectedGender; bool _agreedToTerms = false; bool _isLoading = false; bool _sendingOtp = false; bool _otpSent = false; int _countdown = 0; Timer? _timer; final List _genderOptions = ['Male', 'Female']; @override void dispose() { _nameController.dispose(); _phoneController.dispose(); _emailController.dispose(); _otpController.dispose(); _passwordController.dispose(); _confirmPasswordController.dispose(); _timer?.cancel(); super.dispose(); } void _selectBirthDate() async { final DateTime? picked = await showDatePicker( context: context, initialDate: DateTime(2000, 1, 1), firstDate: DateTime(1920), lastDate: DateTime.now(), builder: (context, child) { return Theme( data: Theme.of(context).copyWith( colorScheme: const ColorScheme.light( primary: AppTheme.secondary, onPrimary: Colors.white, onSurface: AppTheme.onSurface, ), textButtonTheme: TextButtonThemeData( style: TextButton.styleFrom( foregroundColor: AppTheme.secondary, ), ), ), child: child!, ); }, ); if (picked != null && picked != _selectedDate) { setState(() { _selectedDate = picked; }); } } void _sendVerificationCode() async { final phone = _phoneController.text.trim(); final email = _emailController.text.trim(); if (phone.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Please enter your phone number first.')), ); return; } if (email.isEmpty || !email.contains('@')) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Please enter a valid email address.')), ); return; } setState(() => _sendingOtp = true); try { final service = OdooService(); service.connect(AppConfig.odooUrl); final response = await service.sendOtp( email: email, phone: phone, type: 'signup', ); if (response != null && response['status'] == 'success') { setState(() { _otpSent = true; _countdown = 60; }); _startTimer(); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(response['message'] ?? 'Verification code sent!')), ); } } else { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(response?['message'] ?? 'Failed to send verification code.')), ); } } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Error: $e')), ); } } finally { if (mounted) { setState(() => _sendingOtp = false); } } } void _startTimer() { _timer?.cancel(); _timer = Timer.periodic(const Duration(seconds: 1), (timer) { if (_countdown == 0) { timer.cancel(); } else { setState(() { _countdown--; }); } }); } void _signUp() async { final name = _nameController.text.trim(); final phone = _phoneController.text.trim(); final email = _emailController.text.trim(); final otp = _otpController.text.trim(); final password = _passwordController.text; final confirmPassword = _confirmPasswordController.text; if (name.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Please enter your name.')), ); return; } if (phone.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Please enter your phone number.')), ); return; } if (email.isEmpty || !email.contains('@')) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Please enter a valid email address.')), ); return; } if (otp.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Please enter the verification code (OTP).')), ); return; } if (_selectedDate == null) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Please select your birth date.')), ); return; } if (_selectedGender == null) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Please select your gender.')), ); return; } if (password.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Please enter a password.')), ); return; } if (password != confirmPassword) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Passwords do not match.')), ); return; } if (!_agreedToTerms) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('You must agree to the Terms & Conditions and Privacy Policy.')), ); return; } setState(() => _isLoading = true); try { final formattedDate = "${_selectedDate!.year}-${_selectedDate!.month.toString().padLeft(2, '0')}-${_selectedDate!.day.toString().padLeft(2, '0')}"; final service = OdooService(); service.connect(AppConfig.odooUrl); final response = await service.signUpMember( name: name, phone: phone, email: email, birthDate: formattedDate, gender: _selectedGender!, password: password, otp: otp, ); if (response != null && response['status'] == 'success') { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(response['message'] ?? 'Registration successful!')), ); Navigator.of(context).pop(); } } else { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(response?['message'] ?? 'Registration failed.')), ); } } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Error: $e')), ); } } finally { if (mounted) { setState(() => _isLoading = false); } } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Become a Member'), ), body: SafeArea( child: SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 32.0, vertical: 24.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text( 'Join Mie Mapan Loyalty', style: Theme.of(context).textTheme.headlineMedium?.copyWith( color: AppTheme.onSurface, fontSize: 28, ), ), const SizedBox(height: 8), Text( 'Register below to start earning points, unlocking culinary tiers, and tracking your loyalty level.', style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: AppTheme.onSurfaceVariant, ), ), const SizedBox(height: 36), TextField( controller: _nameController, decoration: const InputDecoration( labelText: 'Full Name', ), ), const SizedBox(height: 20), TextField( controller: _phoneController, keyboardType: TextInputType.phone, decoration: const InputDecoration( labelText: 'Phone Number', hintText: 'e.g. 08123456789', ), ), const SizedBox(height: 20), Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ Expanded( child: TextField( controller: _emailController, keyboardType: TextInputType.emailAddress, decoration: const InputDecoration( labelText: 'Email Address', hintText: 'yourname@example.com', ), ), ), const SizedBox(width: 12), _sendingOtp ? const Padding( padding: EdgeInsets.only(bottom: 8.0), child: SizedBox( width: 32, height: 32, child: CircularProgressIndicator(strokeWidth: 2.5), ), ) : Padding( padding: const EdgeInsets.only(bottom: 4.0), child: ElevatedButton( onPressed: _countdown > 0 ? null : _sendVerificationCode, style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), minimumSize: Size.zero, ), child: Text( _countdown > 0 ? '${_countdown}s' : (_otpSent ? 'Resend' : 'Send OTP'), style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold), ), ), ), ], ), if (_otpSent) ...[ const SizedBox(height: 20), TextField( controller: _otpController, keyboardType: TextInputType.number, decoration: const InputDecoration( labelText: 'Verification Code (OTP)', hintText: 'Enter 6-digit code', prefixIcon: Icon(Icons.security), ), ), ], const SizedBox(height: 20), InkWell( onTap: _selectBirthDate, child: Container( color: AppTheme.surfaceContainer, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( _selectedDate == null ? 'Select Birth Date' : 'Birth Date: ${_selectedDate!.day.toString().padLeft(2, '0')}-${_selectedDate!.month.toString().padLeft(2, '0')}-${_selectedDate!.year}', style: TextStyle( color: _selectedDate == null ? AppTheme.onSurfaceVariant : AppTheme.onSurface, fontSize: 16, ), ), const Icon(Icons.calendar_today, color: AppTheme.onSurfaceVariant), ], ), ), ), const SizedBox(height: 20), DropdownButtonFormField( initialValue: _selectedGender, items: _genderOptions.map((String value) { return DropdownMenuItem( value: value, child: Text(value), ); }).toList(), onChanged: (val) { setState(() { _selectedGender = val; }); }, decoration: const InputDecoration( labelText: 'Gender', ), dropdownColor: AppTheme.surfaceContainerLow, style: const TextStyle( color: AppTheme.onSurface, fontSize: 16, ), ), const SizedBox(height: 20), TextField( controller: _passwordController, decoration: const InputDecoration( labelText: 'Password', ), obscureText: true, ), const SizedBox(height: 20), TextField( controller: _confirmPasswordController, decoration: const InputDecoration( labelText: 'Confirm Password', ), obscureText: true, ), const SizedBox(height: 24), Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Checkbox( value: _agreedToTerms, activeColor: AppTheme.secondary, onChanged: (val) { setState(() { _agreedToTerms = val ?? false; }); }, ), Expanded( child: Padding( padding: const EdgeInsets.only(top: 8.0), child: Wrap( children: [ const Text('I agree to the ', style: TextStyle(color: AppTheme.onSurfaceVariant, fontSize: 13)), GestureDetector( onTap: () => AgreementDialog.show( context, 'Terms & Conditions', AgreementTexts.termsAndConditions ), child: const Text( 'Terms & Conditions', style: TextStyle( color: AppTheme.secondary, fontWeight: FontWeight.bold, decoration: TextDecoration.underline, fontSize: 13, ), ), ), const Text(' and ', style: TextStyle(color: AppTheme.onSurfaceVariant, fontSize: 13)), GestureDetector( onTap: () => AgreementDialog.show( context, 'Privacy Policy', AgreementTexts.privacyPolicy ), child: const Text( 'Privacy Policy', style: TextStyle( color: AppTheme.secondary, fontWeight: FontWeight.bold, decoration: TextDecoration.underline, fontSize: 13, ), ), ), ], ), ), ), ], ), const SizedBox(height: 36), SizedBox( height: 56, child: _isLoading ? const Center(child: CircularProgressIndicator()) : ElevatedButton( onPressed: _signUp, child: const Text( 'Sign Up & Get Silver Card', style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), ), ), ], ), ), ), ); } }