Added: - forgot-password.tsx, register.tsx auth screens - Discussion_Points.md and Discussion_Points.txt Updated: - login, chat, index, beneficiary detail screens - profile/help and profile/support - API service - Scheme files (Discussion, AppStore) All assets/images are tracked and safe. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
645 lines
20 KiB
TypeScript
645 lines
20 KiB
TypeScript
import React, { useState, useEffect, useCallback } from 'react';
|
|
import {
|
|
View,
|
|
Text,
|
|
StyleSheet,
|
|
ScrollView,
|
|
TouchableOpacity,
|
|
RefreshControl,
|
|
Image,
|
|
Modal,
|
|
TextInput,
|
|
Alert,
|
|
KeyboardAvoidingView,
|
|
Platform,
|
|
} from 'react-native';
|
|
import { useLocalSearchParams, router } from 'expo-router';
|
|
import { Ionicons } from '@expo/vector-icons';
|
|
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
import { api } from '@/services/api';
|
|
import { useBeneficiary } from '@/contexts/BeneficiaryContext';
|
|
import { LoadingSpinner } from '@/components/ui/LoadingSpinner';
|
|
import { FullScreenError } from '@/components/ui/ErrorMessage';
|
|
import { Button } from '@/components/ui/Button';
|
|
import { AppColors, BorderRadius, FontSizes, Spacing } from '@/constants/theme';
|
|
import type { Beneficiary } from '@/types';
|
|
|
|
export default function BeneficiaryDetailScreen() {
|
|
const { id } = useLocalSearchParams<{ id: string }>();
|
|
const { setCurrentBeneficiary } = useBeneficiary();
|
|
const [beneficiary, setBeneficiary] = useState<Beneficiary | null>(null);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [isRefreshing, setIsRefreshing] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
// Edit modal state
|
|
const [isEditModalVisible, setIsEditModalVisible] = useState(false);
|
|
const [editForm, setEditForm] = useState({
|
|
name: '',
|
|
address: '',
|
|
});
|
|
|
|
const loadBeneficiary = useCallback(async (showLoading = true) => {
|
|
if (!id) return;
|
|
|
|
if (showLoading) setIsLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
const response = await api.getBeneficiary(parseInt(id, 10));
|
|
|
|
if (response.ok && response.data) {
|
|
setBeneficiary(response.data);
|
|
} else {
|
|
setError(response.error?.message || 'Failed to load beneficiary');
|
|
}
|
|
} catch (err) {
|
|
setError(err instanceof Error ? err.message : 'An error occurred');
|
|
} finally {
|
|
setIsLoading(false);
|
|
setIsRefreshing(false);
|
|
}
|
|
}, [id]);
|
|
|
|
useEffect(() => {
|
|
loadBeneficiary();
|
|
}, [loadBeneficiary]);
|
|
|
|
const handleRefresh = useCallback(() => {
|
|
setIsRefreshing(true);
|
|
loadBeneficiary(false);
|
|
}, [loadBeneficiary]);
|
|
|
|
const handleChatPress = () => {
|
|
// Set current beneficiary in context before navigating to chat
|
|
// This allows the chat to include beneficiary context in AI questions
|
|
if (beneficiary) {
|
|
setCurrentBeneficiary(beneficiary);
|
|
}
|
|
router.push('/(tabs)/chat');
|
|
};
|
|
|
|
const handleEditPress = () => {
|
|
if (beneficiary) {
|
|
setEditForm({
|
|
name: beneficiary.name || '',
|
|
address: beneficiary.address || '',
|
|
});
|
|
setIsEditModalVisible(true);
|
|
}
|
|
};
|
|
|
|
const handleSaveEdit = () => {
|
|
Alert.alert(
|
|
'Demo Mode',
|
|
'This is a mockup feature. Saving beneficiary data requires backend API endpoints that are not yet implemented.\n\nContact your administrator to enable this feature.',
|
|
[{ text: 'OK', onPress: () => setIsEditModalVisible(false) }]
|
|
);
|
|
};
|
|
|
|
const showComingSoon = (featureName: string) => {
|
|
Alert.alert(
|
|
'Coming Soon',
|
|
`${featureName} is currently in development.\n\nThis feature will be available in a future update.`,
|
|
[{ text: 'OK' }]
|
|
);
|
|
};
|
|
|
|
if (isLoading) {
|
|
return <LoadingSpinner fullScreen message="Loading beneficiary data..." />;
|
|
}
|
|
|
|
if (error || !beneficiary) {
|
|
return (
|
|
<FullScreenError
|
|
message={error || 'Beneficiary not found'}
|
|
onRetry={() => loadBeneficiary()}
|
|
/>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<SafeAreaView style={styles.container} edges={['top']}>
|
|
{/* Header */}
|
|
<View style={styles.header}>
|
|
<TouchableOpacity
|
|
style={styles.backButton}
|
|
onPress={() => router.back()}
|
|
>
|
|
<Ionicons name="arrow-back" size={24} color={AppColors.textPrimary} />
|
|
</TouchableOpacity>
|
|
<Text style={styles.headerTitle}>{beneficiary.name}</Text>
|
|
<TouchableOpacity style={styles.editButton} onPress={handleEditPress}>
|
|
<Ionicons name="create-outline" size={24} color={AppColors.primary} />
|
|
</TouchableOpacity>
|
|
</View>
|
|
|
|
<ScrollView
|
|
style={styles.content}
|
|
showsVerticalScrollIndicator={false}
|
|
refreshControl={
|
|
<RefreshControl
|
|
refreshing={isRefreshing}
|
|
onRefresh={handleRefresh}
|
|
tintColor={AppColors.primary}
|
|
/>
|
|
}
|
|
>
|
|
{/* Beneficiary Info Card */}
|
|
<View style={styles.infoCard}>
|
|
<View style={styles.avatarContainer}>
|
|
{beneficiary.avatar ? (
|
|
<Image source={{ uri: beneficiary.avatar }} style={styles.avatarImage} />
|
|
) : (
|
|
<Text style={styles.avatarText}>
|
|
{beneficiary.name.charAt(0).toUpperCase()}
|
|
</Text>
|
|
)}
|
|
<View
|
|
style={[
|
|
styles.statusBadge,
|
|
beneficiary.status === 'online' ? styles.onlineBadge : styles.offlineBadge,
|
|
]}
|
|
>
|
|
<Text style={styles.statusText}>
|
|
{beneficiary.status === 'online' ? 'Online' : 'Offline'}
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
|
|
<Text style={styles.beneficiaryName}>{beneficiary.name}</Text>
|
|
{beneficiary.address && (
|
|
<Text style={styles.relationship}>{beneficiary.address}</Text>
|
|
)}
|
|
<Text style={styles.lastSeen}>
|
|
{beneficiary.last_location ? `📍 ${beneficiary.last_location}` : ''} • {beneficiary.last_activity || 'No recent activity'}
|
|
</Text>
|
|
</View>
|
|
|
|
{/* Sensor Stats - using real API data */}
|
|
<View style={styles.section}>
|
|
<Text style={styles.sectionTitle}>Sensor Overview</Text>
|
|
|
|
<View style={styles.statsGrid}>
|
|
<View style={styles.statCard}>
|
|
<View style={[styles.statIcon, { backgroundColor: beneficiary.status === 'online' ? '#D1FAE5' : '#F3F4F6' }]}>
|
|
<Ionicons
|
|
name={beneficiary.status === 'online' ? "walk" : "walk-outline"}
|
|
size={24}
|
|
color={beneficiary.status === 'online' ? AppColors.success : AppColors.textMuted}
|
|
/>
|
|
</View>
|
|
<Text style={styles.statValue}>
|
|
{beneficiary.status === 'online' ? 'Active' : 'Inactive'}
|
|
</Text>
|
|
<Text style={styles.statLabel}>Status</Text>
|
|
<Text style={styles.statUnit}>{beneficiary.last_activity || '--'}</Text>
|
|
</View>
|
|
|
|
<View style={styles.statCard}>
|
|
<View style={[styles.statIcon, { backgroundColor: '#DBEAFE' }]}>
|
|
<Ionicons
|
|
name="location-outline"
|
|
size={24}
|
|
color={AppColors.primary}
|
|
/>
|
|
</View>
|
|
<Text style={styles.statValue} numberOfLines={1}>
|
|
{beneficiary.last_location || '--'}
|
|
</Text>
|
|
<Text style={styles.statLabel}>Location</Text>
|
|
<Text style={styles.statUnit} numberOfLines={1}>{beneficiary.before_last_location || '--'}</Text>
|
|
</View>
|
|
|
|
<View style={styles.statCard}>
|
|
<View style={[styles.statIcon, { backgroundColor: '#E0E7FF' }]}>
|
|
<Ionicons name="thermometer-outline" size={24} color={AppColors.primaryDark} />
|
|
</View>
|
|
<Text style={styles.statValue}>
|
|
{beneficiary.temperature?.toFixed(1) || '--'}{beneficiary.units || '°F'}
|
|
</Text>
|
|
<Text style={styles.statLabel}>Temperature</Text>
|
|
<Text style={styles.statUnit}>Bedroom: {beneficiary.bedroom_temperature?.toFixed(1) || '--'}{beneficiary.units || '°F'}</Text>
|
|
</View>
|
|
</View>
|
|
|
|
{/* Additional stats row */}
|
|
<View style={[styles.statsGrid, { marginTop: Spacing.md }]}>
|
|
<View style={styles.statCard}>
|
|
<View style={[styles.statIcon, { backgroundColor: '#F3E8FF' }]}>
|
|
<Ionicons name="moon-outline" size={24} color="#9333EA" />
|
|
</View>
|
|
<Text style={styles.statValue}>
|
|
{beneficiary.sleep_hours?.toFixed(1) || '--'}h
|
|
</Text>
|
|
<Text style={styles.statLabel}>Sleep</Text>
|
|
</View>
|
|
|
|
<View style={styles.statCard}>
|
|
<View style={[styles.statIcon, { backgroundColor: beneficiary.wellness_score && beneficiary.wellness_score >= 70 ? '#D1FAE5' : '#FEF3C7' }]}>
|
|
<Ionicons
|
|
name="heart-outline"
|
|
size={24}
|
|
color={beneficiary.wellness_score && beneficiary.wellness_score >= 70 ? AppColors.success : AppColors.warning}
|
|
/>
|
|
</View>
|
|
<Text style={styles.statValue}>
|
|
{beneficiary.wellness_score || '--'}%
|
|
</Text>
|
|
<Text style={styles.statLabel}>Wellness</Text>
|
|
<Text style={styles.statUnit}>{beneficiary.wellness_descriptor || '--'}</Text>
|
|
</View>
|
|
|
|
<View style={styles.statCard}>
|
|
<View style={[styles.statIcon, { backgroundColor: '#DBEAFE' }]}>
|
|
<Ionicons name="home-outline" size={24} color={AppColors.primary} />
|
|
</View>
|
|
<Text style={styles.statValue} numberOfLines={1}>
|
|
{beneficiary.address?.split(',')[0] || '--'}
|
|
</Text>
|
|
<Text style={styles.statLabel}>Address</Text>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
|
|
{/* Quick Actions */}
|
|
<View style={styles.section}>
|
|
<Text style={styles.sectionTitle}>Quick Actions</Text>
|
|
|
|
<View style={styles.actionsGrid}>
|
|
<TouchableOpacity style={styles.actionCard} onPress={handleChatPress}>
|
|
<View style={[styles.actionIcon, { backgroundColor: '#D1FAE5' }]}>
|
|
<Ionicons name="chatbubble-ellipses" size={24} color={AppColors.success} />
|
|
</View>
|
|
<Text style={styles.actionLabel}>Chat with Julia</Text>
|
|
</TouchableOpacity>
|
|
|
|
<TouchableOpacity style={styles.actionCard} onPress={() => showComingSoon('Set Reminder')}>
|
|
<View style={[styles.actionIcon, { backgroundColor: '#FEF3C7' }]}>
|
|
<Ionicons name="notifications" size={24} color={AppColors.warning} />
|
|
</View>
|
|
<Text style={styles.actionLabel}>Set Reminder</Text>
|
|
</TouchableOpacity>
|
|
|
|
<TouchableOpacity style={styles.actionCard} onPress={() => showComingSoon('Video Call')}>
|
|
<View style={[styles.actionIcon, { backgroundColor: '#DBEAFE' }]}>
|
|
<Ionicons name="call" size={24} color={AppColors.primary} />
|
|
</View>
|
|
<Text style={styles.actionLabel}>Video Call</Text>
|
|
</TouchableOpacity>
|
|
|
|
<TouchableOpacity style={styles.actionCard} onPress={() => showComingSoon('Activity Report')}>
|
|
<View style={[styles.actionIcon, { backgroundColor: '#F3E8FF' }]}>
|
|
<Ionicons name="analytics" size={24} color="#9333EA" />
|
|
</View>
|
|
<Text style={styles.actionLabel}>Activity Report</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
</View>
|
|
|
|
{/* Chat with Julia Button */}
|
|
<View style={styles.chatButtonContainer}>
|
|
<Button
|
|
title="Chat with Julia AI"
|
|
onPress={handleChatPress}
|
|
fullWidth
|
|
size="lg"
|
|
/>
|
|
</View>
|
|
</ScrollView>
|
|
|
|
{/* Edit Modal */}
|
|
<Modal
|
|
visible={isEditModalVisible}
|
|
animationType="slide"
|
|
transparent={true}
|
|
onRequestClose={() => setIsEditModalVisible(false)}
|
|
>
|
|
<KeyboardAvoidingView
|
|
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
|
style={styles.modalOverlay}
|
|
>
|
|
<View style={styles.modalContent}>
|
|
<View style={styles.modalHeader}>
|
|
<Text style={styles.modalTitle}>Edit Beneficiary</Text>
|
|
<TouchableOpacity
|
|
onPress={() => setIsEditModalVisible(false)}
|
|
style={styles.modalCloseButton}
|
|
>
|
|
<Ionicons name="close" size={24} color={AppColors.textPrimary} />
|
|
</TouchableOpacity>
|
|
</View>
|
|
|
|
<ScrollView style={styles.modalForm}>
|
|
<Text style={styles.inputLabel}>Name</Text>
|
|
<TextInput
|
|
style={styles.textInput}
|
|
value={editForm.name}
|
|
onChangeText={(text) => setEditForm({ ...editForm, name: text })}
|
|
placeholder="Enter name"
|
|
placeholderTextColor={AppColors.textMuted}
|
|
/>
|
|
|
|
<Text style={styles.inputLabel}>Address</Text>
|
|
<TextInput
|
|
style={[styles.textInput, styles.textAreaInput]}
|
|
value={editForm.address}
|
|
onChangeText={(text) => setEditForm({ ...editForm, address: text })}
|
|
placeholder="Enter address"
|
|
placeholderTextColor={AppColors.textMuted}
|
|
multiline
|
|
numberOfLines={3}
|
|
/>
|
|
|
|
<View style={styles.infoBox}>
|
|
<Ionicons name="information-circle-outline" size={20} color={AppColors.primary} />
|
|
<Text style={styles.infoBoxText}>
|
|
Other data (wellness score, temperature, sleep hours) is collected automatically from sensors.
|
|
</Text>
|
|
</View>
|
|
</ScrollView>
|
|
|
|
<View style={styles.modalFooter}>
|
|
<TouchableOpacity
|
|
style={styles.cancelButton}
|
|
onPress={() => setIsEditModalVisible(false)}
|
|
>
|
|
<Text style={styles.cancelButtonText}>Cancel</Text>
|
|
</TouchableOpacity>
|
|
<TouchableOpacity
|
|
style={styles.saveButton}
|
|
onPress={handleSaveEdit}
|
|
>
|
|
<Ionicons name="checkmark" size={20} color={AppColors.white} />
|
|
<Text style={styles.saveButtonText}>Save Changes</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
</View>
|
|
</KeyboardAvoidingView>
|
|
</Modal>
|
|
</SafeAreaView>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flex: 1,
|
|
backgroundColor: AppColors.surface,
|
|
},
|
|
header: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
paddingHorizontal: Spacing.md,
|
|
paddingVertical: Spacing.sm,
|
|
backgroundColor: AppColors.background,
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: AppColors.border,
|
|
},
|
|
backButton: {
|
|
padding: Spacing.xs,
|
|
},
|
|
headerTitle: {
|
|
fontSize: FontSizes.lg,
|
|
fontWeight: '600',
|
|
color: AppColors.textPrimary,
|
|
},
|
|
editButton: {
|
|
padding: Spacing.xs,
|
|
},
|
|
content: {
|
|
flex: 1,
|
|
},
|
|
infoCard: {
|
|
backgroundColor: AppColors.background,
|
|
padding: Spacing.xl,
|
|
alignItems: 'center',
|
|
marginBottom: Spacing.md,
|
|
},
|
|
avatarContainer: {
|
|
width: 100,
|
|
height: 100,
|
|
borderRadius: BorderRadius.full,
|
|
backgroundColor: AppColors.primaryLight,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
marginBottom: Spacing.md,
|
|
},
|
|
avatarText: {
|
|
fontSize: FontSizes['3xl'],
|
|
fontWeight: '600',
|
|
color: AppColors.white,
|
|
},
|
|
avatarImage: {
|
|
width: 100,
|
|
height: 100,
|
|
borderRadius: BorderRadius.full,
|
|
},
|
|
statusBadge: {
|
|
position: 'absolute',
|
|
bottom: 0,
|
|
paddingHorizontal: Spacing.sm,
|
|
paddingVertical: Spacing.xs,
|
|
borderRadius: BorderRadius.full,
|
|
},
|
|
onlineBadge: {
|
|
backgroundColor: AppColors.success,
|
|
},
|
|
offlineBadge: {
|
|
backgroundColor: AppColors.offline,
|
|
},
|
|
statusText: {
|
|
fontSize: FontSizes.xs,
|
|
fontWeight: '500',
|
|
color: AppColors.white,
|
|
},
|
|
beneficiaryName: {
|
|
fontSize: FontSizes['2xl'],
|
|
fontWeight: '700',
|
|
color: AppColors.textPrimary,
|
|
},
|
|
relationship: {
|
|
fontSize: FontSizes.base,
|
|
color: AppColors.textSecondary,
|
|
marginTop: Spacing.xs,
|
|
},
|
|
lastSeen: {
|
|
fontSize: FontSizes.sm,
|
|
color: AppColors.textMuted,
|
|
marginTop: Spacing.sm,
|
|
},
|
|
section: {
|
|
backgroundColor: AppColors.background,
|
|
padding: Spacing.lg,
|
|
marginBottom: Spacing.md,
|
|
},
|
|
sectionTitle: {
|
|
fontSize: FontSizes.lg,
|
|
fontWeight: '600',
|
|
color: AppColors.textPrimary,
|
|
marginBottom: Spacing.md,
|
|
},
|
|
statsGrid: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
},
|
|
statCard: {
|
|
flex: 1,
|
|
alignItems: 'center',
|
|
padding: Spacing.sm,
|
|
},
|
|
statIcon: {
|
|
width: 48,
|
|
height: 48,
|
|
borderRadius: BorderRadius.lg,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
marginBottom: Spacing.sm,
|
|
},
|
|
statValue: {
|
|
fontSize: FontSizes.xl,
|
|
fontWeight: '700',
|
|
color: AppColors.textPrimary,
|
|
},
|
|
statLabel: {
|
|
fontSize: FontSizes.xs,
|
|
color: AppColors.textSecondary,
|
|
marginTop: Spacing.xs,
|
|
},
|
|
statUnit: {
|
|
fontSize: FontSizes.xs,
|
|
color: AppColors.textMuted,
|
|
},
|
|
actionsGrid: {
|
|
flexDirection: 'row',
|
|
flexWrap: 'wrap',
|
|
justifyContent: 'space-between',
|
|
},
|
|
actionCard: {
|
|
width: '48%',
|
|
backgroundColor: AppColors.surface,
|
|
borderRadius: BorderRadius.lg,
|
|
padding: Spacing.md,
|
|
alignItems: 'center',
|
|
marginBottom: Spacing.md,
|
|
},
|
|
actionIcon: {
|
|
width: 48,
|
|
height: 48,
|
|
borderRadius: BorderRadius.lg,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
marginBottom: Spacing.sm,
|
|
},
|
|
actionLabel: {
|
|
fontSize: FontSizes.sm,
|
|
fontWeight: '500',
|
|
color: AppColors.textPrimary,
|
|
textAlign: 'center',
|
|
},
|
|
chatButtonContainer: {
|
|
padding: Spacing.lg,
|
|
paddingBottom: Spacing.xxl,
|
|
},
|
|
// Modal styles
|
|
modalOverlay: {
|
|
flex: 1,
|
|
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
justifyContent: 'flex-end',
|
|
},
|
|
modalContent: {
|
|
backgroundColor: AppColors.background,
|
|
borderTopLeftRadius: BorderRadius.xl,
|
|
borderTopRightRadius: BorderRadius.xl,
|
|
maxHeight: '80%',
|
|
},
|
|
modalHeader: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
padding: Spacing.lg,
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: AppColors.border,
|
|
},
|
|
modalTitle: {
|
|
fontSize: FontSizes.xl,
|
|
fontWeight: '600',
|
|
color: AppColors.textPrimary,
|
|
},
|
|
modalCloseButton: {
|
|
padding: Spacing.xs,
|
|
},
|
|
modalForm: {
|
|
padding: Spacing.lg,
|
|
},
|
|
inputLabel: {
|
|
fontSize: FontSizes.sm,
|
|
fontWeight: '500',
|
|
color: AppColors.textSecondary,
|
|
marginBottom: Spacing.xs,
|
|
marginTop: Spacing.md,
|
|
},
|
|
textInput: {
|
|
backgroundColor: AppColors.surface,
|
|
borderRadius: BorderRadius.md,
|
|
padding: Spacing.md,
|
|
fontSize: FontSizes.base,
|
|
color: AppColors.textPrimary,
|
|
borderWidth: 1,
|
|
borderColor: AppColors.border,
|
|
},
|
|
textAreaInput: {
|
|
minHeight: 80,
|
|
textAlignVertical: 'top',
|
|
},
|
|
infoBox: {
|
|
flexDirection: 'row',
|
|
backgroundColor: '#EFF6FF',
|
|
borderRadius: BorderRadius.md,
|
|
padding: Spacing.md,
|
|
marginTop: Spacing.lg,
|
|
gap: Spacing.sm,
|
|
},
|
|
infoBoxText: {
|
|
flex: 1,
|
|
fontSize: FontSizes.sm,
|
|
color: AppColors.primary,
|
|
lineHeight: 20,
|
|
},
|
|
modalFooter: {
|
|
flexDirection: 'row',
|
|
padding: Spacing.lg,
|
|
gap: Spacing.md,
|
|
borderTopWidth: 1,
|
|
borderTopColor: AppColors.border,
|
|
},
|
|
cancelButton: {
|
|
flex: 1,
|
|
paddingVertical: Spacing.md,
|
|
borderRadius: BorderRadius.md,
|
|
backgroundColor: AppColors.surface,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
},
|
|
cancelButtonText: {
|
|
fontSize: FontSizes.base,
|
|
fontWeight: '500',
|
|
color: AppColors.textSecondary,
|
|
},
|
|
saveButton: {
|
|
flex: 2,
|
|
flexDirection: 'row',
|
|
paddingVertical: Spacing.md,
|
|
borderRadius: BorderRadius.md,
|
|
backgroundColor: AppColors.primary,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
gap: Spacing.xs,
|
|
},
|
|
saveButtonText: {
|
|
fontSize: FontSizes.base,
|
|
fontWeight: '600',
|
|
color: AppColors.white,
|
|
},
|
|
});
|