Sergei a804a82512 Implement Auth, Patients List, and Dashboard
Features:
- Login screen with API integration (credentials endpoint)
- SecureStore for token management
- Patients list with health data display
- Patient dashboard with stats and quick actions
- AI Chat screen (voice_ask API integration)
- Profile screen with logout
- Full error handling and loading states
- WellNuo brand colors and UI components

API Integration:
- Base URL: eluxnetworks.net/function/well-api/api
- Auth: function=credentials
- Chat: function=voice_ask

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-12 12:58:15 -08:00

144 lines
3.3 KiB
TypeScript

import React, { useState } from 'react';
import {
View,
TextInput,
Text,
StyleSheet,
TouchableOpacity,
type TextInputProps,
} from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { AppColors, BorderRadius, FontSizes, Spacing } from '@/constants/theme';
interface InputProps extends TextInputProps {
label?: string;
error?: string;
leftIcon?: keyof typeof Ionicons.glyphMap;
rightIcon?: keyof typeof Ionicons.glyphMap;
onRightIconPress?: () => void;
}
export function Input({
label,
error,
leftIcon,
rightIcon,
onRightIconPress,
secureTextEntry,
style,
...props
}: InputProps) {
const [isPasswordVisible, setIsPasswordVisible] = useState(false);
const isPassword = secureTextEntry !== undefined;
const handleTogglePassword = () => {
setIsPasswordVisible(!isPasswordVisible);
};
return (
<View style={styles.container}>
{label && <Text style={styles.label}>{label}</Text>}
<View style={[styles.inputContainer, error && styles.inputError]}>
{leftIcon && (
<Ionicons
name={leftIcon}
size={20}
color={AppColors.textMuted}
style={styles.leftIcon}
/>
)}
<TextInput
style={[
styles.input,
leftIcon && styles.inputWithLeftIcon,
(isPassword || rightIcon) && styles.inputWithRightIcon,
style,
]}
placeholderTextColor={AppColors.textMuted}
secureTextEntry={isPassword && !isPasswordVisible}
{...props}
/>
{isPassword && (
<TouchableOpacity
onPress={handleTogglePassword}
style={styles.rightIconButton}
>
<Ionicons
name={isPasswordVisible ? 'eye-off-outline' : 'eye-outline'}
size={20}
color={AppColors.textMuted}
/>
</TouchableOpacity>
)}
{!isPassword && rightIcon && (
<TouchableOpacity
onPress={onRightIconPress}
style={styles.rightIconButton}
disabled={!onRightIconPress}
>
<Ionicons
name={rightIcon}
size={20}
color={AppColors.textMuted}
/>
</TouchableOpacity>
)}
</View>
{error && <Text style={styles.errorText}>{error}</Text>}
</View>
);
}
const styles = StyleSheet.create({
container: {
marginBottom: Spacing.md,
},
label: {
fontSize: FontSizes.sm,
fontWeight: '500',
color: AppColors.textPrimary,
marginBottom: Spacing.xs,
},
inputContainer: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: AppColors.surface,
borderRadius: BorderRadius.lg,
borderWidth: 1,
borderColor: AppColors.border,
},
inputError: {
borderColor: AppColors.error,
},
input: {
flex: 1,
paddingVertical: Spacing.sm + 4,
paddingHorizontal: Spacing.md,
fontSize: FontSizes.base,
color: AppColors.textPrimary,
},
inputWithLeftIcon: {
paddingLeft: 0,
},
inputWithRightIcon: {
paddingRight: 0,
},
leftIcon: {
marginLeft: Spacing.md,
marginRight: Spacing.sm,
},
rightIconButton: {
padding: Spacing.md,
},
errorText: {
fontSize: FontSizes.xs,
color: AppColors.error,
marginTop: Spacing.xs,
},
});