503 lines
14 KiB
TypeScript
503 lines
14 KiB
TypeScript
/*
|
|
* File: SignUpScreen.tsx
|
|
* Description: Multi-step signup screen with validation and Redux integration
|
|
* Design & Developed by Tech4Biz Solutions
|
|
* Copyright (c) Spurrin Innovations. All rights reserved.
|
|
*/
|
|
|
|
import React, { useEffect, useState } from 'react';
|
|
import {
|
|
View,
|
|
StyleSheet,
|
|
StatusBar,
|
|
Alert,
|
|
Text,
|
|
TouchableOpacity,
|
|
ScrollView,
|
|
KeyboardAvoidingView,
|
|
Platform
|
|
} from 'react-native';
|
|
import { theme } from '../../../theme/theme';
|
|
import { useAppDispatch, useAppSelector } from '../../../store/hooks';
|
|
import Icon from 'react-native-vector-icons/Feather';
|
|
|
|
// Import signup step components
|
|
import EmailStep from '../components/signup/EmailStep';
|
|
import PasswordStep from '../components/signup/PasswordStep';
|
|
import NameStep from '../components/signup/NameStep';
|
|
import DocumentUploadStep from '../components/signup/DocumentUploadStep';
|
|
import HospitalSelectionStep from '../components/signup/HospitalSelectionStep';
|
|
import EmailAlreadyRegisteredModal from '../components/signup/EmailAlreadyRegisteredModal';
|
|
|
|
// Import API service
|
|
|
|
|
|
// Import hospital Redux functionality
|
|
import { fetchHospitals } from '../redux/hospitalSlice';
|
|
import { selectHospitalLoading, selectHospitals } from '../redux/hospitalSelectors';
|
|
|
|
// Import types
|
|
import { SignUpData, SignUpStep } from '../types/signup';
|
|
import { authAPI } from '../services/authAPI';
|
|
import { showError, showSuccess } from '../../../shared/utils/toast';
|
|
|
|
// ============================================================================
|
|
// INTERFACES
|
|
// ============================================================================
|
|
|
|
interface SignUpScreenProps {
|
|
navigation: any;
|
|
}
|
|
|
|
// ============================================================================
|
|
// SIGNUP SCREEN COMPONENT
|
|
// ============================================================================
|
|
|
|
/**
|
|
* SignUpScreen Component
|
|
*
|
|
* Purpose: Multi-step signup flow with validation and Redux integration
|
|
*
|
|
* Features:
|
|
* - Step-by-step signup process (email → password → name → document → hospital)
|
|
* - Real-time validation with visual feedback
|
|
* - Email and username availability checks
|
|
* - Hospital selection with search
|
|
* - Document upload with preview
|
|
* - Progress tracking with visual progress bar
|
|
* - Modern UI with icons and proper typography
|
|
* - Keyboard-aware layout for better UX
|
|
* - Redux state management
|
|
* - Loading states and error handling
|
|
*/
|
|
const SignUpScreen: React.FC<SignUpScreenProps> = ({ navigation }) => {
|
|
// ============================================================================
|
|
// STATE MANAGEMENT
|
|
// ============================================================================
|
|
|
|
const [currentStep, setCurrentStep] = useState<SignUpStep>('email');
|
|
const [showEmailRegisteredModal, setShowEmailRegisteredModal] = useState(false);
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const [signUpData, setSignUpData] = useState<Partial<SignUpData>>({
|
|
email: '',
|
|
password: '',
|
|
first_name: '',
|
|
last_name: '',
|
|
username: '',
|
|
id_photo_url: null,
|
|
hospital_id: '',
|
|
});
|
|
|
|
const dispatch = useAppDispatch();
|
|
|
|
// ============================================================================
|
|
// REDUX STATE
|
|
// ============================================================================
|
|
|
|
const hospitals = useAppSelector(selectHospitals);
|
|
const hospitalLoading = useAppSelector(selectHospitalLoading);
|
|
|
|
// ============================================================================
|
|
// STEP CONFIGURATION
|
|
// ============================================================================
|
|
|
|
const steps: SignUpStep[] = ['email', 'password', 'name', 'document', 'hospital'];
|
|
const currentStepIndex = steps.indexOf(currentStep);
|
|
|
|
// ============================================================================
|
|
// EFFECTS
|
|
// ============================================================================
|
|
|
|
useEffect(() => {
|
|
// Fetch hospitals on component mount
|
|
dispatch(fetchHospitals());
|
|
}, [dispatch]);
|
|
|
|
|
|
|
|
// ============================================================================
|
|
// STEP HANDLERS
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Handle Email Step Continue
|
|
*
|
|
* Purpose: Validate email and proceed to next step
|
|
*/
|
|
const handleEmailContinue = async (email: string) => {
|
|
setIsLoading(true);
|
|
|
|
try {
|
|
const response :any = await authAPI.validatemail({email});
|
|
console.log('response', response);
|
|
|
|
if(response.status==409&&response.data.message){
|
|
// Show modal instead of toast for already registered email
|
|
setShowEmailRegisteredModal(true)
|
|
}
|
|
if(response.status==200&&response.data.message){
|
|
setSignUpData(prev => ({ ...prev, email }));
|
|
setCurrentStep('password');
|
|
}
|
|
|
|
} catch (error) {
|
|
Alert.alert('Error', 'Failed to validate email. Please try again.');
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Handle Password Step Continue
|
|
*
|
|
* Purpose: Validate password and proceed to next step
|
|
*/
|
|
const handlePasswordContinue = (password: string) => {
|
|
setSignUpData(prev => ({ ...prev, password }));
|
|
setCurrentStep('name');
|
|
};
|
|
|
|
/**
|
|
* Handle Name Step Continue
|
|
*
|
|
* Purpose: Validate name and username, then proceed to next step
|
|
*/
|
|
const handleNameContinue = async (firstName: string, lastName: string, username: string) => {
|
|
setIsLoading(true);
|
|
|
|
try {
|
|
const response:any = await authAPI.validateusername(username);
|
|
console.log('response', response);
|
|
|
|
if(response.status==409&&response.data.message){
|
|
showError(response.data.message);
|
|
}
|
|
if(response.status==200&&response.data.message){
|
|
setSignUpData(prev => ({
|
|
...prev,
|
|
first_name: firstName,
|
|
last_name: lastName,
|
|
username: username
|
|
}));
|
|
setCurrentStep('document');
|
|
}
|
|
} catch (error) {
|
|
Alert.alert('Error', 'Failed to validate username. Please try again.');
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Handle Document Upload Step Continue
|
|
*
|
|
* Purpose: Save document and proceed to next step
|
|
*/
|
|
const handleDocumentContinue = (documentUri: string) => {
|
|
setSignUpData(prev => ({ ...prev, id_photo_url: documentUri }));
|
|
setCurrentStep('hospital');
|
|
};
|
|
|
|
/**
|
|
* Handle Hospital Selection Step Continue
|
|
*
|
|
* Purpose: Complete signup process
|
|
*/
|
|
const handleHospitalContinue = async (hospitalId: string) => {
|
|
const finalData: SignUpData = {
|
|
...signUpData,
|
|
hospital_id: hospitalId,
|
|
} as SignUpData;
|
|
|
|
setSignUpData(finalData);
|
|
|
|
// Call completion handler
|
|
await onSignUpComplete(finalData);
|
|
};
|
|
|
|
/**
|
|
* Complete Signup Process
|
|
*
|
|
* Purpose: Submit final signup data to API
|
|
*/
|
|
const onSignUpComplete = async (payload: SignUpData) => {
|
|
console.log('final payload', payload);
|
|
setIsLoading(true);
|
|
|
|
try {
|
|
const formData = new FormData();
|
|
let role = 'er_physician';
|
|
|
|
formData.append('email', payload.email);
|
|
formData.append('password', payload.password);
|
|
formData.append('first_name', payload.first_name);
|
|
formData.append('last_name', payload.last_name);
|
|
formData.append('username', payload.username);
|
|
formData.append('dashboard_role', role);
|
|
formData.append('hospital_id', payload.hospital_id);
|
|
|
|
// Attach file if exists
|
|
if (payload.id_photo_url) {
|
|
const filePath = payload.id_photo_url;
|
|
const file = {
|
|
uri: filePath,
|
|
name: 'id_photo',
|
|
type: 'image/jpg',
|
|
};
|
|
formData.append('id_photo_url', file as any);
|
|
}
|
|
|
|
console.log('payload prepared', formData);
|
|
const response :any = await authAPI.signup(formData);
|
|
console.log('signup response', response);
|
|
|
|
if(response.ok && response.data && response.data.success ) {
|
|
//@ts-ignore
|
|
showSuccess('Sign Up Successfully')
|
|
navigation.navigate('Login');
|
|
// dispatch(setHospitals(response.data.data))
|
|
} else {
|
|
showError('error while signup');
|
|
if( response.data && response.data.message ) {
|
|
//@ts-ignore
|
|
showError(response.data.message)
|
|
}
|
|
|
|
}
|
|
} catch (error: any) {
|
|
console.log('error', error);
|
|
Alert.alert('Error', 'Failed to create account. Please try again.');
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
// ============================================================================
|
|
// NAVIGATION HANDLERS
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Handle Back Navigation
|
|
*
|
|
* Purpose: Navigate to previous step or go back to previous screen
|
|
*/
|
|
const handleBack = () => {
|
|
if (currentStepIndex > 0) {
|
|
const previousStep = steps[currentStepIndex - 1];
|
|
setCurrentStep(previousStep);
|
|
} else {
|
|
navigation.goBack();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Handle Modal Close
|
|
*
|
|
* Purpose: Close email already registered modal
|
|
*/
|
|
const handleCloseModal = () => {
|
|
setShowEmailRegisteredModal(false);
|
|
};
|
|
|
|
/**
|
|
* Handle Go To Login
|
|
*
|
|
* Purpose: Navigate to login screen
|
|
*/
|
|
const handleGoToLogin = () => {
|
|
setShowEmailRegisteredModal(false);
|
|
navigation.navigate('Login');
|
|
};
|
|
|
|
// ============================================================================
|
|
// RENDER FUNCTIONS
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Render Current Step
|
|
*
|
|
* Purpose: Render the appropriate step component based on current step
|
|
*/
|
|
const renderCurrentStep = () => {
|
|
console.log('signupdate', signUpData);
|
|
|
|
const commonProps = {
|
|
onBack: handleBack,
|
|
data: signUpData,
|
|
isLoading,
|
|
};
|
|
|
|
switch (currentStep) {
|
|
case 'email':
|
|
return (
|
|
<EmailStep
|
|
{...commonProps}
|
|
onContinue={handleEmailContinue}
|
|
/>
|
|
);
|
|
|
|
case 'password':
|
|
return (
|
|
<PasswordStep
|
|
{...commonProps}
|
|
onContinue={handlePasswordContinue}
|
|
/>
|
|
);
|
|
|
|
case 'name':
|
|
return (
|
|
<NameStep
|
|
{...commonProps}
|
|
onContinue={handleNameContinue}
|
|
/>
|
|
);
|
|
|
|
case 'document':
|
|
return (
|
|
<DocumentUploadStep
|
|
{...commonProps}
|
|
onContinue={handleDocumentContinue}
|
|
/>
|
|
);
|
|
|
|
case 'hospital':
|
|
return (
|
|
<HospitalSelectionStep
|
|
{...commonProps}
|
|
onContinue={handleHospitalContinue}
|
|
hospitals={hospitals}
|
|
hospitalLoading={hospitalLoading}
|
|
/>
|
|
);
|
|
|
|
default:
|
|
return (
|
|
<EmailStep
|
|
{...commonProps}
|
|
onContinue={handleEmailContinue}
|
|
/>
|
|
);
|
|
}
|
|
};
|
|
|
|
// ============================================================================
|
|
// RENDER
|
|
// ============================================================================
|
|
|
|
return (
|
|
<KeyboardAvoidingView
|
|
style={styles.container}
|
|
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
|
>
|
|
<StatusBar
|
|
barStyle="dark-content"
|
|
backgroundColor={theme.colors.background}
|
|
/>
|
|
|
|
{/* Conditional Content Rendering */}
|
|
{currentStep === 'hospital' ? (
|
|
// For hospital step, render without ScrollView to avoid conflicts
|
|
<View style={styles.content}>
|
|
{renderCurrentStep()}
|
|
</View>
|
|
) : (
|
|
// For other steps, use ScrollView for proper scrolling
|
|
<ScrollView
|
|
style={styles.content}
|
|
showsVerticalScrollIndicator={false}
|
|
contentContainerStyle={styles.contentContainer}
|
|
>
|
|
{renderCurrentStep()}
|
|
</ScrollView>
|
|
)}
|
|
|
|
{/* Progress Bar - Bottom */}
|
|
<View style={styles.progressContainer}>
|
|
<View style={styles.progressBar}>
|
|
<View
|
|
style={[
|
|
styles.progressFill,
|
|
{ width: `${((currentStepIndex + 1) / steps.length) * 100}%` }
|
|
]}
|
|
/>
|
|
</View>
|
|
<Text style={styles.progressText}>
|
|
{Math.round(((currentStepIndex + 1) / steps.length) * 100)}% Complete
|
|
</Text>
|
|
</View>
|
|
|
|
<EmailAlreadyRegisteredModal
|
|
visible={showEmailRegisteredModal}
|
|
onClose={handleCloseModal}
|
|
onGoToLogin={handleGoToLogin}
|
|
/>
|
|
</KeyboardAvoidingView>
|
|
);
|
|
};
|
|
|
|
// ============================================================================
|
|
// STYLES
|
|
// ============================================================================
|
|
|
|
const styles = StyleSheet.create({
|
|
// Main container
|
|
container: {
|
|
flex: 1,
|
|
backgroundColor: theme.colors.background,
|
|
},
|
|
|
|
|
|
|
|
// Progress container
|
|
progressContainer: {
|
|
paddingHorizontal: theme.spacing.lg,
|
|
paddingVertical: theme.spacing.md,
|
|
backgroundColor: theme.colors.background,
|
|
borderTopWidth: 1,
|
|
borderTopColor: theme.colors.border,
|
|
},
|
|
|
|
// Progress bar
|
|
progressBar: {
|
|
height: 4,
|
|
backgroundColor: theme.colors.border,
|
|
borderRadius: theme.borderRadius.round,
|
|
marginBottom: theme.spacing.sm,
|
|
overflow: 'hidden',
|
|
},
|
|
|
|
// Progress fill
|
|
progressFill: {
|
|
height: '100%',
|
|
backgroundColor: theme.colors.primary,
|
|
borderRadius: theme.borderRadius.round,
|
|
},
|
|
|
|
// Progress text
|
|
progressText: {
|
|
fontSize: theme.typography.fontSize.bodySmall,
|
|
fontFamily: theme.typography.fontFamily.medium,
|
|
color: theme.colors.textSecondary,
|
|
textAlign: 'center',
|
|
},
|
|
|
|
// Content area
|
|
content: {
|
|
flex: 1,
|
|
},
|
|
|
|
// Content container
|
|
contentContainer: {
|
|
flexGrow: 1,
|
|
padding: theme.spacing.lg,
|
|
paddingBottom: theme.spacing.xl,
|
|
},
|
|
});
|
|
|
|
export default SignUpScreen;
|
|
|
|
/*
|
|
* End of File: SignUpScreen.tsx
|
|
* Design & Developed by Tech4Biz Solutions
|
|
* Copyright (c) Spurrin Innovations. All rights reserved.
|
|
*/
|