feat: implement payment profile management and caregiver authentication services with theme and UI components

This commit is contained in:
2026-08-18 14:16:24 +05:30
parent 8beb0ebbb5
commit 13656d4bb3
9 changed files with 487 additions and 15 deletions
+3 -2
View File
@@ -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 }}
>
+116
View File
@@ -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,
},
});