calendi/app/app/(tabs)/profile.js
2025-05-01 20:30:35 +02:00

322 lines
7.7 KiB
JavaScript

import React, { useEffect, useState } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, ActivityIndicator, Image, TextInput, ScrollView, Alert } from 'react-native';
import { Stack } from 'expo-router';
import apiService from '../../lib/api/apiService';
import Colors from '../../constants';
export default function ProfileScreen() {
const [profile, setProfile] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [isEditing, setIsEditing] = useState(false);
const [formData, setFormData] = useState({
name: '',
email: '',
bio: '',
});
useEffect(() => {
loadProfile();
}, []);
const loadProfile = async () => {
try {
setLoading(true);
setError(null);
const userData = await apiService.getUserProfile();
setProfile(userData);
setFormData({
name: userData.name || '',
email: userData.email || '',
bio: userData.bio || '',
});
} catch (err) {
setError('Failed to load profile data. Please try again.');
console.error(err);
} finally {
setLoading(false);
}
};
const handleInputChange = (field, value) => {
setFormData({
...formData,
[field]: value
});
};
const handleSave = async () => {
try {
setLoading(true);
await apiService.updateUserProfile(formData);
setProfile({
...profile,
...formData
});
setIsEditing(false);
Alert.alert('Success', 'Profile updated successfully!');
} catch (err) {
Alert.alert('Error', 'Failed to update profile. Please try again.');
console.error(err);
} finally {
setLoading(false);
}
};
const handleCancel = () => {
setFormData({
name: profile.name || '',
email: profile.email || '',
bio: profile.bio || '',
});
setIsEditing(false);
};
if (loading && !profile) {
return (
<View style={styles.centeredContainer}>
<ActivityIndicator size="large" color={Colors.primary} />
</View>
);
}
if (error && !profile) {
return (
<View style={styles.centeredContainer}>
<Text style={styles.errorText}>{error}</Text>
<TouchableOpacity style={styles.retryButton} onPress={loadProfile}>
<Text style={styles.retryButtonText}>Retry</Text>
</TouchableOpacity>
</View>
);
}
return (
<ScrollView style={styles.container}>
<Stack.Screen
options={{
title: 'Profile',
headerShown: true,
headerRight: () => (
<TouchableOpacity
onPress={isEditing ? handleSave : () => setIsEditing(true)}
disabled={loading}
>
<Text style={styles.headerButton}>
{isEditing ? 'Save' : 'Edit'}
</Text>
</TouchableOpacity>
),
}}
/>
{loading && (
<ActivityIndicator
size="small"
color={Colors.primary}
style={styles.loadingIndicator}
/>
)}
<View style={styles.profileHeader}>
<Image
source={{ uri: profile?.avatar || 'https://via.placeholder.com/150' }}
style={styles.avatar}
/>
<View style={styles.profileInfo}>
{isEditing ? (
<TextInput
style={styles.nameInput}
value={formData.name}
onChangeText={(text) => handleInputChange('name', text)}
placeholder="Name"
/>
) : (
<Text style={styles.name}>{profile?.name}</Text>
)}
</View>
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>Email</Text>
{isEditing ? (
<TextInput
style={styles.input}
value={formData.email}
onChangeText={(text) => handleInputChange('email', text)}
keyboardType="email-address"
/>
) : (
<Text style={styles.sectionContent}>{profile?.email}</Text>
)}
</View>
<View style={styles.section}>
<Text style={styles.sectionTitle}>Bio</Text>
{isEditing ? (
<TextInput
style={[styles.input, styles.bioInput]}
value={formData.bio}
onChangeText={(text) => handleInputChange('bio', text)}
multiline
numberOfLines={4}
/>
) : (
<Text style={styles.sectionContent}>{profile?.bio || 'No bio provided'}</Text>
)}
</View>
{isEditing && (
<TouchableOpacity
style={styles.cancelButton}
onPress={handleCancel}
>
<Text style={styles.cancelButtonText}>Cancel</Text>
</TouchableOpacity>
)}
<View style={styles.statsSection}>
<Text style={styles.sectionTitle}>Your Stats</Text>
<View style={styles.statsContainer}>
<View style={styles.statBox}>
<Text style={styles.statNumber}>{profile?.stats?.eventsCreated || 0}</Text>
<Text style={styles.statLabel}>Events Created</Text>
</View>
<View style={styles.statBox}>
<Text style={styles.statNumber}>{profile?.stats?.eventsAttended || 0}</Text>
<Text style={styles.statLabel}>Events Attended</Text>
</View>
</View>
</View>
</ScrollView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: Colors.background,
},
centeredContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
},
loadingIndicator: {
marginTop: 10,
},
headerButton: {
color: Colors.primary,
fontSize: 16,
fontWeight: 'bold',
marginRight: 15,
},
profileHeader: {
flexDirection: 'row',
alignItems: 'center',
padding: 20,
backgroundColor: Colors.card,
borderBottomWidth: 1,
borderBottomColor: Colors.border,
},
avatar: {
width: 80,
height: 80,
borderRadius: 40,
marginRight: 20,
},
profileInfo: {
flex: 1,
},
name: {
fontSize: 22,
fontWeight: 'bold',
},
nameInput: {
fontSize: 22,
fontWeight: 'bold',
padding: 5,
borderBottomWidth: 1,
borderBottomColor: Colors.primary,
},
section: {
padding: 20,
backgroundColor: Colors.card,
marginBottom: 10,
},
sectionTitle: {
fontSize: 16,
color: 'gray',
marginBottom: 8,
},
sectionContent: {
fontSize: 16,
},
input: {
padding: 10,
borderWidth: 1,
borderColor: Colors.border,
borderRadius: 5,
fontSize: 16,
},
bioInput: {
height: 100,
textAlignVertical: 'top',
},
cancelButton: {
backgroundColor: Colors.background,
padding: 15,
alignItems: 'center',
borderWidth: 1,
borderColor: Colors.error,
marginHorizontal: 20,
borderRadius: 5,
marginBottom: 20,
},
cancelButtonText: {
color: Colors.error,
fontWeight: 'bold',
},
errorText: {
color: Colors.error,
textAlign: 'center',
marginBottom: 20,
},
retryButton: {
backgroundColor: Colors.primary,
paddingVertical: 10,
paddingHorizontal: 20,
borderRadius: 8,
},
retryButtonText: {
color: 'white',
fontWeight: 'bold',
},
statsSection: {
padding: 20,
backgroundColor: Colors.card,
marginBottom: 30,
},
statsContainer: {
flexDirection: 'row',
justifyContent: 'space-around',
marginTop: 10,
},
statBox: {
alignItems: 'center',
padding: 15,
backgroundColor: Colors.background,
borderRadius: 10,
width: '45%',
},
statNumber: {
fontSize: 24,
fontWeight: 'bold',
color: Colors.primary,
},
statLabel: {
marginTop: 5,
color: 'gray',
},
});