399 lines
11 KiB
TypeScript
399 lines
11 KiB
TypeScript
/*
|
|
* File: NameStep.tsx
|
|
* Description: Name step component for signup flow
|
|
* Design & Developed by Tech4Biz Solutions
|
|
* Copyright (c) Spurrin Innovations. All rights reserved.
|
|
*/
|
|
|
|
import React, { useState } from 'react';
|
|
import {
|
|
View,
|
|
Text,
|
|
TextInput,
|
|
TouchableOpacity,
|
|
StyleSheet,
|
|
KeyboardAvoidingView,
|
|
Platform,
|
|
ScrollView,
|
|
} from 'react-native';
|
|
import { theme } from '../../../../theme/theme';
|
|
import { NameStepProps } from '../../types/signup';
|
|
import Icon from 'react-native-vector-icons/Feather';
|
|
|
|
// ============================================================================
|
|
// NAME STEP COMPONENT
|
|
// ============================================================================
|
|
|
|
/**
|
|
* NameStep Component
|
|
*
|
|
* Purpose: Third step of signup flow - personal information
|
|
*
|
|
* Features:
|
|
* - First name, last name, and username inputs
|
|
* - Real-time validation with error handling
|
|
* - Continue button with loading state
|
|
* - Back navigation with modern header
|
|
*/
|
|
const NameStep: React.FC<NameStepProps> = ({
|
|
onContinue,
|
|
onBack,
|
|
data,
|
|
isLoading,
|
|
}) => {
|
|
// ============================================================================
|
|
// STATE MANAGEMENT
|
|
// ============================================================================
|
|
|
|
const [firstName, setFirstName] = useState(data.first_name || '');
|
|
const [lastName, setLastName] = useState(data.last_name || '');
|
|
const [username, setUsername] = useState(data.username || '');
|
|
const [errors, setErrors] = useState({
|
|
firstName: '',
|
|
lastName: '',
|
|
username: '',
|
|
});
|
|
|
|
// ============================================================================
|
|
// VALIDATION FUNCTIONS
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Validate Input Fields
|
|
*
|
|
* Purpose: Check if all fields are valid
|
|
*/
|
|
const validateFields = (): boolean => {
|
|
const newErrors = {
|
|
firstName: '',
|
|
lastName: '',
|
|
username: '',
|
|
};
|
|
|
|
if (!firstName.trim()) {
|
|
newErrors.firstName = 'First name is required';
|
|
}
|
|
|
|
if (!lastName.trim()) {
|
|
newErrors.lastName = 'Last name is required';
|
|
}
|
|
|
|
if (!username.trim()) {
|
|
newErrors.username = 'Username is required';
|
|
} else if (username.length < 3) {
|
|
newErrors.username = 'Username must be at least 3 characters';
|
|
}
|
|
|
|
setErrors(newErrors);
|
|
return !Object.values(newErrors).some(error => error !== '');
|
|
};
|
|
|
|
/**
|
|
* Handle Continue
|
|
*
|
|
* Purpose: Validate fields and proceed to next step
|
|
*/
|
|
const handleContinue = () => {
|
|
if (validateFields()) {
|
|
onContinue(firstName.trim(), lastName.trim(), username.trim());
|
|
}
|
|
};
|
|
|
|
// ============================================================================
|
|
// RENDER
|
|
// ============================================================================
|
|
|
|
return (
|
|
<KeyboardAvoidingView
|
|
style={styles.container}
|
|
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
|
>
|
|
<ScrollView
|
|
style={styles.scrollView}
|
|
contentContainerStyle={styles.scrollContent}
|
|
keyboardShouldPersistTaps="handled"
|
|
showsVerticalScrollIndicator={false}
|
|
>
|
|
{/* Header */}
|
|
<View style={styles.header}>
|
|
<TouchableOpacity onPress={onBack} style={styles.backButton}>
|
|
<Icon name="arrow-left" size={24} color={theme.colors.textPrimary} />
|
|
</TouchableOpacity>
|
|
|
|
<View style={styles.headerContent}>
|
|
<Text style={styles.title}>Create Account</Text>
|
|
<Text style={styles.subtitle}>Step 3 of 5</Text>
|
|
</View>
|
|
|
|
<View style={styles.headerSpacer} />
|
|
</View>
|
|
|
|
{/* Content */}
|
|
<View style={styles.content}>
|
|
<Text style={styles.sectionTitle}>Tell us about yourself</Text>
|
|
<Text style={styles.description}>
|
|
Please provide your name and choose a username for your account.
|
|
</Text>
|
|
|
|
{/* First Name Input */}
|
|
<View style={styles.inputContainer}>
|
|
<Text style={styles.inputLabel}>First Name</Text>
|
|
<TextInput
|
|
style={[
|
|
styles.input,
|
|
errors.firstName ? styles.inputError : null,
|
|
]}
|
|
placeholder="Enter your first name"
|
|
placeholderTextColor={theme.colors.textMuted}
|
|
value={firstName}
|
|
onChangeText={(text) => {
|
|
setFirstName(text);
|
|
setErrors(prev => ({ ...prev, firstName: '' }));
|
|
}}
|
|
autoCapitalize="words"
|
|
autoCorrect={false}
|
|
editable={!isLoading}
|
|
/>
|
|
{errors.firstName ? (
|
|
<Text style={styles.errorText}>{errors.firstName}</Text>
|
|
) : null}
|
|
</View>
|
|
|
|
{/* Last Name Input */}
|
|
<View style={styles.inputContainer}>
|
|
<Text style={styles.inputLabel}>Last Name</Text>
|
|
<TextInput
|
|
style={[
|
|
styles.input,
|
|
errors.lastName ? styles.inputError : null,
|
|
]}
|
|
placeholder="Enter your last name"
|
|
placeholderTextColor={theme.colors.textMuted}
|
|
value={lastName}
|
|
onChangeText={(text) => {
|
|
setLastName(text);
|
|
setErrors(prev => ({ ...prev, lastName: '' }));
|
|
}}
|
|
autoCapitalize="words"
|
|
autoCorrect={false}
|
|
editable={!isLoading}
|
|
/>
|
|
{errors.lastName ? (
|
|
<Text style={styles.errorText}>{errors.lastName}</Text>
|
|
) : null}
|
|
</View>
|
|
|
|
{/* Username Input */}
|
|
<View style={styles.inputContainer}>
|
|
<Text style={styles.inputLabel}>Username</Text>
|
|
<TextInput
|
|
style={[
|
|
styles.input,
|
|
errors.username ? styles.inputError : null,
|
|
]}
|
|
placeholder="Choose a username"
|
|
placeholderTextColor={theme.colors.textMuted}
|
|
value={username}
|
|
onChangeText={(text) => {
|
|
setUsername(text);
|
|
setErrors(prev => ({ ...prev, username: '' }));
|
|
}}
|
|
autoCapitalize="none"
|
|
autoCorrect={false}
|
|
editable={!isLoading}
|
|
/>
|
|
{errors.username ? (
|
|
<Text style={styles.errorText}>{errors.username}</Text>
|
|
) : null}
|
|
</View>
|
|
|
|
{/* Continue Button */}
|
|
<TouchableOpacity
|
|
style={[
|
|
styles.continueButton,
|
|
(!firstName.trim() || !lastName.trim() || !username.trim() || isLoading)
|
|
? styles.continueButtonDisabled
|
|
: null,
|
|
]}
|
|
onPress={handleContinue}
|
|
disabled={!firstName.trim() || !lastName.trim() || !username.trim() || isLoading}
|
|
>
|
|
<Text style={[
|
|
styles.continueButtonText,
|
|
(!firstName.trim() || !lastName.trim() || !username.trim() || isLoading)
|
|
? styles.continueButtonTextDisabled
|
|
: null,
|
|
]}>
|
|
{isLoading ? 'Validating...' : 'Continue'}
|
|
</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
</ScrollView>
|
|
</KeyboardAvoidingView>
|
|
);
|
|
};
|
|
|
|
// ============================================================================
|
|
// STYLES
|
|
// ============================================================================
|
|
|
|
const styles = StyleSheet.create({
|
|
// Main container
|
|
container: {
|
|
flex: 1,
|
|
backgroundColor: theme.colors.background,
|
|
},
|
|
|
|
// Scroll view
|
|
scrollView: {
|
|
flex: 1,
|
|
},
|
|
|
|
// Scroll content
|
|
scrollContent: {
|
|
flexGrow: 1,
|
|
paddingHorizontal: theme.spacing.sm,
|
|
},
|
|
|
|
// Header section
|
|
header: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
paddingTop: theme.spacing.xl,
|
|
paddingBottom: theme.spacing.lg,
|
|
marginBottom: theme.spacing.lg,
|
|
},
|
|
|
|
// Back button
|
|
backButton: {
|
|
padding: theme.spacing.sm,
|
|
borderRadius: theme.borderRadius.medium,
|
|
backgroundColor: theme.colors.backgroundAlt,
|
|
},
|
|
|
|
// Header content
|
|
headerContent: {
|
|
flex: 1,
|
|
alignItems: 'center',
|
|
},
|
|
|
|
// Header spacer
|
|
headerSpacer: {
|
|
width: 40,
|
|
},
|
|
|
|
// Title
|
|
title: {
|
|
fontSize: theme.typography.fontSize.displaySmall,
|
|
fontFamily: theme.typography.fontFamily.bold,
|
|
color: theme.colors.textPrimary,
|
|
marginBottom: theme.spacing.xs,
|
|
},
|
|
|
|
// Subtitle
|
|
subtitle: {
|
|
fontSize: theme.typography.fontSize.bodyMedium,
|
|
fontFamily: theme.typography.fontFamily.regular,
|
|
color: theme.colors.textSecondary,
|
|
},
|
|
|
|
// Content section
|
|
content: {
|
|
flex: 1,
|
|
justifyContent: 'center',
|
|
paddingBottom: theme.spacing.xl,
|
|
},
|
|
|
|
// Section title
|
|
sectionTitle: {
|
|
fontSize: theme.typography.fontSize.displaySmall,
|
|
fontFamily: theme.typography.fontFamily.bold,
|
|
color: theme.colors.textPrimary,
|
|
marginBottom: theme.spacing.sm,
|
|
},
|
|
|
|
// Description
|
|
description: {
|
|
fontSize: theme.typography.fontSize.bodyMedium,
|
|
fontFamily: theme.typography.fontFamily.regular,
|
|
color: theme.colors.textSecondary,
|
|
marginBottom: theme.spacing.xl,
|
|
},
|
|
|
|
// Input container
|
|
inputContainer: {
|
|
marginBottom: theme.spacing.xl,
|
|
},
|
|
|
|
// Input label
|
|
inputLabel: {
|
|
fontSize: theme.typography.fontSize.bodyMedium,
|
|
fontFamily: theme.typography.fontFamily.medium,
|
|
color: theme.colors.textPrimary,
|
|
marginBottom: theme.spacing.sm,
|
|
},
|
|
|
|
// Input field
|
|
input: {
|
|
borderWidth: 1,
|
|
borderColor: theme.colors.border,
|
|
borderRadius: theme.borderRadius.medium,
|
|
paddingHorizontal: theme.spacing.md,
|
|
paddingVertical: theme.spacing.md,
|
|
fontSize: theme.typography.fontSize.bodyLarge,
|
|
fontFamily: theme.typography.fontFamily.regular,
|
|
color: theme.colors.textPrimary,
|
|
backgroundColor: theme.colors.background,
|
|
},
|
|
|
|
// Input error state
|
|
inputError: {
|
|
borderColor: theme.colors.error,
|
|
},
|
|
|
|
// Error text
|
|
errorText: {
|
|
fontSize: theme.typography.fontSize.bodySmall,
|
|
fontFamily: theme.typography.fontFamily.regular,
|
|
color: theme.colors.error,
|
|
marginTop: theme.spacing.xs,
|
|
},
|
|
|
|
// Continue button
|
|
continueButton: {
|
|
backgroundColor: theme.colors.primary,
|
|
borderRadius: theme.borderRadius.medium,
|
|
paddingVertical: theme.spacing.md,
|
|
paddingHorizontal: theme.spacing.lg,
|
|
alignItems: 'center',
|
|
marginBottom: theme.spacing.lg,
|
|
...theme.shadows.primary,
|
|
},
|
|
|
|
// Continue button disabled
|
|
continueButtonDisabled: {
|
|
backgroundColor: theme.colors.border,
|
|
opacity: 0.6,
|
|
},
|
|
|
|
// Continue button text
|
|
continueButtonText: {
|
|
fontSize: theme.typography.fontSize.bodyLarge,
|
|
fontFamily: theme.typography.fontFamily.bold,
|
|
color: theme.colors.background,
|
|
},
|
|
|
|
// Continue button text disabled
|
|
continueButtonTextDisabled: {
|
|
color: theme.colors.textMuted,
|
|
},
|
|
});
|
|
|
|
export default NameStep;
|
|
|
|
/*
|
|
* End of File: NameStep.tsx
|
|
* Design & Developed by Tech4Biz Solutions
|
|
* Copyright (c) Spurrin Innovations. All rights reserved.
|
|
*/
|