feat: implement payment profile management and caregiver authentication services with theme and UI components
This commit is contained in:
@@ -10,9 +10,10 @@ interface HeaderProps {
|
||||
title: string;
|
||||
showBack?: boolean;
|
||||
rightComponent?: React.ReactNode;
|
||||
onBack?: () => void;
|
||||
}
|
||||
|
||||
export const Header: React.FC<HeaderProps> = ({ title, showBack = false, rightComponent }) => {
|
||||
export const Header: React.FC<HeaderProps> = ({ title, showBack = false, rightComponent, onBack }) => {
|
||||
const navigation = useNavigation();
|
||||
const { screenPadding } = useResponsiveLayout();
|
||||
|
||||
@@ -21,7 +22,7 @@ export const Header: React.FC<HeaderProps> = ({ title, showBack = false, rightCo
|
||||
<View style={styles.leftContainer}>
|
||||
{showBack && (
|
||||
<TouchableOpacity
|
||||
onPress={() => navigation.goBack()}
|
||||
onPress={() => onBack ? onBack() : navigation.goBack()}
|
||||
style={styles.backButton}
|
||||
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
|
||||
>
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
import React, { useState, useRef } from 'react';
|
||||
import { View, TextInput, StyleSheet, Pressable } from 'react-native';
|
||||
|
||||
interface OtpInputProps {
|
||||
length?: number;
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
error?: boolean;
|
||||
}
|
||||
|
||||
export const OtpInput: React.FC<OtpInputProps> = ({ length = 6, value, onChange, error }) => {
|
||||
const [isFocused, setIsFocused] = useState(false);
|
||||
const inputRef = useRef<TextInput>(null);
|
||||
|
||||
const handlePress = () => {
|
||||
inputRef.current?.focus();
|
||||
};
|
||||
|
||||
const renderBoxes = () => {
|
||||
const boxes = [];
|
||||
for (let i = 0; i < length; i++) {
|
||||
const char = value[i] || '';
|
||||
const isCurrentDigit = i === value.length;
|
||||
const isLastDigit = i === length - 1;
|
||||
const isBoxFocused = isFocused && (isCurrentDigit || (value.length === length && isLastDigit));
|
||||
|
||||
boxes.push(
|
||||
<View
|
||||
key={i}
|
||||
style={[
|
||||
styles.box,
|
||||
isBoxFocused && styles.boxFocused,
|
||||
error && styles.boxError,
|
||||
]}
|
||||
>
|
||||
<TextInput
|
||||
style={styles.text}
|
||||
value={char}
|
||||
editable={false}
|
||||
selectTextOnFocus={false}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
return boxes;
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Pressable style={styles.boxesContainer} onPress={handlePress}>
|
||||
{renderBoxes()}
|
||||
</Pressable>
|
||||
<TextInput
|
||||
ref={inputRef}
|
||||
value={value}
|
||||
onChangeText={(text) => {
|
||||
// Allow only numbers and up to `length`
|
||||
const cleanedText = text.replace(/[^0-9]/g, '');
|
||||
if (cleanedText.length <= length) {
|
||||
onChange(cleanedText);
|
||||
}
|
||||
}}
|
||||
keyboardType="number-pad"
|
||||
textContentType="oneTimeCode"
|
||||
maxLength={length}
|
||||
style={styles.hiddenInput}
|
||||
onFocus={() => setIsFocused(true)}
|
||||
onBlur={() => setIsFocused(false)}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
marginVertical: 8,
|
||||
alignItems: 'center',
|
||||
width: '100%',
|
||||
},
|
||||
boxesContainer: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
width: '100%',
|
||||
},
|
||||
box: {
|
||||
width: 45,
|
||||
height: 55,
|
||||
borderWidth: 1,
|
||||
borderColor: '#D1D5DB', // gray-300
|
||||
borderRadius: 12,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '#F9FAFB', // gray-50
|
||||
},
|
||||
boxFocused: {
|
||||
borderColor: '#4F46E5', // indigo-600
|
||||
borderWidth: 2,
|
||||
backgroundColor: '#EEF2FF', // indigo-50
|
||||
},
|
||||
boxError: {
|
||||
borderColor: '#DC2626', // red-600
|
||||
backgroundColor: '#FEF2F2', // red-50
|
||||
},
|
||||
text: {
|
||||
fontSize: 24,
|
||||
fontWeight: 'bold',
|
||||
color: '#111827', // gray-900
|
||||
textAlign: 'center',
|
||||
},
|
||||
hiddenInput: {
|
||||
position: 'absolute',
|
||||
width: 1,
|
||||
height: 1,
|
||||
opacity: 0,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user