diff --git a/lib/controllers/doctor_controller.dart b/lib/controllers/doctor_controller.dart index 90366d7..1d0dca8 100644 --- a/lib/controllers/doctor_controller.dart +++ b/lib/controllers/doctor_controller.dart @@ -97,11 +97,11 @@ class DoctorController { } void addQualification(String qualification) { - model.qualifications!.add(qualification); + model.qualifications!.add(qualification.trim()); } void removeQualification(String qualification) { - model.qualifications!.remove(qualification); + model.qualifications!.remove(qualification.trim()); } void updateFloorBuilding(String floorBuilding) { diff --git a/lib/screens/authentication/sign_up_screen.dart b/lib/screens/authentication/sign_up_screen.dart index a464803..b8cf0d1 100644 --- a/lib/screens/authentication/sign_up_screen.dart +++ b/lib/screens/authentication/sign_up_screen.dart @@ -1,5 +1,3 @@ -import 'dart:math'; - import 'package:flutter/material.dart'; import 'package:intl_phone_field/intl_phone_field.dart'; import 'package:medora/data/services/data_service.dart'; @@ -26,123 +24,6 @@ class _SignUpScreenState extends State { bool _isLoading = false; bool _obscurePassword = true; - // Add focus nodes to handle keyboard navigation - final _emailFocusNode = FocusNode(); - final _passwordFocusNode = FocusNode(); - - // Email validation patterns - final RegExp _emailRegex = RegExp( - r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$', - caseSensitive: false, - ); - - // Common email domain validation - final List _commonEmailDomains = [ - 'gmail.com', - 'yahoo.com', - 'hotmail.com', - 'outlook.com' - ]; - - String? _validateEmail(String? value) { - if (value == null || value.isEmpty) { - return 'Email address is required'; - } - - value = value.trim(); - - // Basic format validation - if (!_emailRegex.hasMatch(value)) { - return 'Please enter a valid email address'; - } - - // Check email length - if (value.length > 254) { - return 'Email address is too long'; - } - - // Split email into local and domain parts - final parts = value.split('@'); - if (parts.length != 2) { - return 'Invalid email format'; - } - - final localPart = parts[0]; - final domainPart = parts[1].toLowerCase(); - - // Validate local part - if (localPart.isEmpty || localPart.length > 64) { - return 'Invalid email username'; - } - - // Check for common typos in popular domains - for (final domain in _commonEmailDomains) { - if (domainPart.length > 3 && - domainPart != domain && - _calculateLevenshteinDistance(domainPart, domain) <= 2) { - return 'Did you mean @$domain?'; - } - } - - // Additional domain validation - if (!domainPart.contains('.')) { - return 'Invalid email domain'; - } - - return null; - } - - String? _validatePassword(String? value) { - if (value == null || value.isEmpty) { - return 'Password is required'; - } - - if (value.length < 6) { - return 'Password must be at least 6 characters'; - } - - if (!value.contains(RegExp(r'[A-Z]'))) { - return 'Password must contain at least one uppercase letter'; - } - - if (!value.contains(RegExp(r'[0-9]'))) { - return 'Password must contain at least one number'; - } - - if (!value.contains(RegExp(r'[!@#$%^&*(),.?":{}|<>]'))) { - return 'Password must contain at least one special character'; - } - - return null; - } - - // Calculate Levenshtein distance for typo detection - int _calculateLevenshteinDistance(String a, String b) { - if (a.isEmpty) return b.length; - if (b.isEmpty) return a.length; - - List previousRow = List.generate(b.length + 1, (i) => i); - List currentRow = List.filled(b.length + 1, 0); - - for (int i = 0; i < a.length; i++) { - currentRow[0] = i + 1; - - for (int j = 0; j < b.length; j++) { - int insertCost = previousRow[j + 1] + 1; - int deleteCost = currentRow[j] + 1; - int replaceCost = previousRow[j] + (a[i] != b[j] ? 1 : 0); - - currentRow[j + 1] = [insertCost, deleteCost, replaceCost].reduce(min); - } - - List temp = previousRow; - previousRow = currentRow; - currentRow = temp; - } - - return previousRow[b.length]; - } - @override Widget build(BuildContext context) { return Scaffold( @@ -170,7 +51,6 @@ class _SignUpScreenState extends State { children: [ TextFormField( controller: _emailController, - focusNode: _emailFocusNode, decoration: InputDecoration( labelText: 'Email', border: OutlineInputBorder( @@ -185,25 +65,22 @@ class _SignUpScreenState extends State { ), prefixIcon: const Icon(Icons.email_outlined, color: Colors.blue), - errorMaxLines: 2, ), keyboardType: TextInputType.emailAddress, - textInputAction: TextInputAction.next, - onFieldSubmitted: (_) { - _passwordFocusNode.requestFocus(); - }, - validator: _validateEmail, - onChanged: (value) { - // Trigger validation on change if the field was previously invalid - if (_formKey.currentState?.validate() ?? false) { - setState(() {}); + validator: (value) { + if (value?.isEmpty ?? true) { + return 'Please enter your email'; } + if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$') + .hasMatch(value!)) { + return 'Please enter a valid email'; + } + return null; }, ), const SizedBox(height: 16), TextFormField( controller: _passwordController, - focusNode: _passwordFocusNode, decoration: InputDecoration( labelText: 'Password', border: OutlineInputBorder( @@ -229,15 +106,16 @@ class _SignUpScreenState extends State { onPressed: () => setState( () => _obscurePassword = !_obscurePassword), ), - errorMaxLines: 2, ), obscureText: _obscurePassword, - validator: _validatePassword, - onChanged: (value) { - // Trigger validation on change if the field was previously invalid - if (_formKey.currentState?.validate() ?? false) { - setState(() {}); + validator: (value) { + if (value?.isEmpty ?? true) { + return 'Please enter your password'; } + if ((value?.length ?? 0) < 6) { + return 'Password must be at least 6 characters'; + } + return null; }, ), const SizedBox(height: 16), @@ -262,7 +140,7 @@ class _SignUpScreenState extends State { }, validator: (phone) { if (phone?.completeNumber.isEmpty ?? true) { - return 'Phone number is required'; + return 'Please enter your phone number'; } return null; }, @@ -287,7 +165,6 @@ class _SignUpScreenState extends State { Future _handleSignUp() async { if (_formKey.currentState == null || !_formKey.currentState!.validate()) { - _showErrorSnackBar('Please fix the errors in the form'); return; } @@ -345,8 +222,6 @@ class _SignUpScreenState extends State { void dispose() { _emailController.dispose(); _passwordController.dispose(); - _emailFocusNode.dispose(); - _passwordFocusNode.dispose(); super.dispose(); } } diff --git a/lib/screens/doctor_screen/doctor_dashboard/doctor_services_menu_screen.dart b/lib/screens/doctor_screen/doctor_dashboard/doctor_services_menu_screen.dart index e372ca6..af37272 100644 --- a/lib/screens/doctor_screen/doctor_dashboard/doctor_services_menu_screen.dart +++ b/lib/screens/doctor_screen/doctor_dashboard/doctor_services_menu_screen.dart @@ -88,11 +88,11 @@ class _DoctorServicesMenuScreen extends State { ], ), ), - // const Icon( - // Icons.chevron_right, - // color: Colors.white, - // size: 30, - // ), + const Icon( + Icons.chevron_right, + color: Colors.white, + size: 30, + ), ], ), ); diff --git a/lib/screens/doctor_screen/doctor_profile_screens/address_screen.dart b/lib/screens/doctor_screen/doctor_profile_screens/address_screen.dart index e61743c..2131156 100644 --- a/lib/screens/doctor_screen/doctor_profile_screens/address_screen.dart +++ b/lib/screens/doctor_screen/doctor_profile_screens/address_screen.dart @@ -60,19 +60,39 @@ class _DoctorAddressScreenState extends State { _addressTypeController = TextEditingController(text: doctor.addressType ?? ''); selectedAddressType = widget.controller?.model.addressType; - if (widget.controller?.model.addressType != null) { - final existingType = widget.controller!.model.addressType!; - selectedAddressType = existingType; - - // If it's a custom type (not in predefined list) - if (!addressTypes.contains(existingType)) { - showCustomTypeField = true; - _addressTypeController.text = existingType; - } + if (selectedAddressType != null && + !addressTypes.contains(selectedAddressType)) { + showCustomTypeField = true; } } + // bool _validateAndProceed() {if (_formKey.currentState!.validate()) { + // // Update the address model + // _controller.updateFloorBuilding(_floorBuildingController.text); + // _controller.updateStreet(_streetController.text); + // _controller.updateCity(_cityController.text); + // _controller.updateState(_stateController.text); + // _controller.updateCountry(_countryController.text); + // _controller.updatePostalCode(_postalCodeController.text); + + // // Validate the address fields + // if (_areFieldsValid()) { + // return true; + // } + + // ScaffoldMessenger.of(context).showSnackBar( + // const SnackBar(content: Text('Please fill in all required fields')), + // ); + // return false; + // } bool _validateAndProceed() { + // if (!_formKey.currentState!.validate()) return false; + // if (selectedAddressType == null) { + // ScaffoldMessenger.of(context).showSnackBar(const SnackBar( + // content: Text('Please select an address type'), + // backgroundColor: Colors.red, + // )); + // } if (_formKey.currentState!.validate()) { _controller.updateFloorBuilding(_floorBuildingController.text); _controller.updateStreet(_streetController.text); @@ -97,6 +117,20 @@ class _DoctorAddressScreenState extends State { _addressTypeController.text.isNotEmpty; } + // bool _validateAndProceed() { + // if (!_formKey.currentState!.validate()) return false; + // if (selectedAddressType == null) { + // ScaffoldMessenger.of(context).showSnackBar( + // const SnackBar( + // content: Text('Please select an address type'), + // backgroundColor: Colors.red, + // ), + // ); + // return false; + // } + // return true; + // } + void _handleAddressTypeSelection(String type) { setState(() { if (type == 'Others') { @@ -104,15 +138,11 @@ class _DoctorAddressScreenState extends State { if (!showCustomTypeField) { _addressTypeController.clear(); selectedAddressType = null; - widget.controller?.updateAddressType(''); } } else { showCustomTypeField = false; - _addressTypeController - .clear(); // Clear the custom field if it was active selectedAddressType = type; - widget.controller - ?.updateAddressType(type); // Update controller with selected type + widget.controller?.updateAddressType(type); } }); } @@ -404,7 +434,7 @@ class _DoctorAddressScreenState extends State { if (isMandatory && (value == null || value.isEmpty)) { return '$label is required'; } - if (value != null && !RegExp(r'^(?!0{6})\d{6}$').hasMatch(value)) { + if (value != null && !RegExp(r'^[0-9]+$').hasMatch(value)) { return 'Please enter numbers only'; } return null; diff --git a/lib/screens/doctor_screen/doctor_profile_screens/doctor_profile_screen.dart b/lib/screens/doctor_screen/doctor_profile_screens/doctor_profile_screen.dart index 299c057..d676952 100644 --- a/lib/screens/doctor_screen/doctor_profile_screens/doctor_profile_screen.dart +++ b/lib/screens/doctor_screen/doctor_profile_screens/doctor_profile_screen.dart @@ -242,6 +242,7 @@ class _ProfileUploadPageState extends State { children: [ _buildUniformField( label: 'Name', + icon: Icons.person, child: Container(), // The child parameter is not used in this implementation