import React, { useState, useEffect } from 'react'; import { View, Text, StyleSheet, TouchableOpacity, Alert, TextInput, ScrollView, Image, ActivityIndicator } from 'react-native'; import { useRouter } from 'expo-router'; import { Ionicons } from '@expo/vector-icons'; import * as ImagePicker from 'expo-image-picker'; import { useAuth } from '../../hooks/useAuth'; import { colors, spacing, borderRadius } from '../../constants/theme'; interface SearchHistoryItem { id: number; address: string; latitude: number | null; longitude: number | null; created_at: string; } export default function ProfileScreen() { const router = useRouter(); const { user, isAuthenticated, isLoading, logout, isAdmin } = useAuth(); const [firstName, setFirstName] = useState(user?.first_name || ''); const [lastName, setLastName] = useState(user?.last_name || ''); const [avatarUrl, setAvatarUrl] = useState(user?.avatar_url || ''); const [searchHistory, setSearchHistory] = useState([]); const [saving, setSaving] = useState(false); const [feedback, setFeedback] = useState<{ type: 'ok' | 'err'; text: string } | null>(null); useEffect(() => { if (isAuthenticated) { loadSearchHistory(); } }, [isAuthenticated]); useEffect(() => { setFirstName(user?.first_name || ''); setLastName(user?.last_name || ''); setAvatarUrl(user?.avatar_url || ''); }, [user]); async function loadSearchHistory() { try { const res = await fetch('/api/search-history', { credentials: 'include' }); if (res.ok) { const data = await res.json(); setSearchHistory(data); } } catch (err) { console.error('Error loading search history:', err); } } async function handleSave() { setSaving(true); setFeedback(null); try { const res = await fetch('/api/users/me', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ first_name: firstName.trim() || null, last_name: lastName.trim() || null, avatar_url: avatarUrl || null, }), }); if (!res.ok) { const err = await res.json().catch(() => ({})); throw new Error(err.error || 'Error al guardar'); } setFeedback({ type: 'ok', text: 'Perfil guardado.' }); } catch (err: any) { setFeedback({ type: 'err', text: err.message || 'Error al guardar' }); } finally { setSaving(false); } } async function handlePickImage() { const result = await ImagePicker.launchImageLibraryAsync({ mediaTypes: ImagePicker.MediaTypeOptions.Images, allowsEditing: true, aspect: [1, 1], quality: 0.8, }); if (!result.canceled && result.assets[0]) { setAvatarUrl(result.assets[0].uri); try { const res = await fetch('/api/users/me', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ avatar_url: result.assets[0].uri }), }); if (res.ok) { setFeedback({ type: 'ok', text: 'Foto de perfil actualizada.' }); } } catch (err) { setFeedback({ type: 'err', text: 'Error al guardar la foto' }); } } } async function handleDeleteSearch(id: number) { try { const res = await fetch(`/api/search-history/${id}`, { method: 'DELETE', credentials: 'include', }); if (res.ok) { setSearchHistory(prev => prev.filter(item => item.id !== id)); } } catch (err) { console.error('Error deleting search:', err); } } const handleLogout = async () => { Alert.alert( 'Cerrar Sesión', '¿Estás seguro que deseas cerrar sesión?', [ { text: 'Cancelar', style: 'cancel' }, { text: 'Cerrar Sesión', style: 'destructive', onPress: async () => { await logout(); router.replace('/auth/login'); } }, ] ); }; if (isLoading) { return ( Cargando... ); } if (!isAuthenticated) { return ( Inicia Sesión Inicia sesión para acceder a todas las funcionalidades router.push('/auth/login')} > Iniciar Sesión router.push('/auth/register')} > Crear cuenta nueva ); } return ( {/* Avatar Section */} {avatarUrl ? ( ) : ( )} Toca para cambiar foto {/* Editable Fields */} Nombre Apellidos {feedback && ( {feedback.text} )} {saving ? ( ) : ( Guardar Cambios )} {/* Menu Section */} Mis Direcciones {searchHistory.length > 0 && ( Tus búsquedas recientes: {searchHistory.map((item) => ( {item.address} handleDeleteSearch(item.id)} style={styles.searchDelete} > ))} )} {isAdmin && ( Panel Admin )} {/* Logout Button */} Cerrar Sesión ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: colors.background, }, loadingText: { textAlign: 'center', marginTop: spacing.xxl, color: colors.textSecondary, }, authPrompt: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: spacing.xl, }, authTitle: { fontSize: 24, fontWeight: 'bold', color: colors.text, marginTop: spacing.lg, }, authSubtitle: { fontSize: 16, color: colors.textSecondary, textAlign: 'center', marginTop: spacing.sm, marginBottom: spacing.xl, }, avatarSection: { alignItems: 'center', padding: spacing.xl, backgroundColor: colors.card, }, avatarContainer: { width: 100, height: 100, borderRadius: 50, backgroundColor: colors.background, justifyContent: 'center', alignItems: 'center', overflow: 'hidden', }, avatarImage: { width: 100, height: 100, borderRadius: 50, }, avatarOverlay: { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0, 0, 0, 0.5)', justifyContent: 'center', alignItems: 'center', opacity: 0.8, }, editAvatarText: { marginTop: spacing.sm, color: colors.textSecondary, fontSize: 14, }, formSection: { padding: spacing.xl, backgroundColor: colors.card, marginTop: spacing.md, }, inputGroup: { marginBottom: spacing.md, }, label: { fontSize: 14, fontWeight: '600', color: colors.textSecondary, marginBottom: spacing.xs, }, input: { borderWidth: 1, borderColor: colors.border || '#E0E0E0', borderRadius: borderRadius.md, padding: spacing.md, fontSize: 16, color: colors.text, }, feedback: { padding: spacing.md, borderRadius: borderRadius.md, marginBottom: spacing.md, }, feedbackOk: { backgroundColor: 'rgba(0, 69, 13, 0.1)', borderWidth: 1, borderColor: 'rgba(0, 69, 13, 0.2)', }, feedbackErr: { backgroundColor: 'rgba(186, 26, 26, 0.1)', borderWidth: 1, borderColor: 'rgba(186, 26, 26, 0.2)', }, feedbackText: { fontSize: 14, }, saveButton: { backgroundColor: colors.primary, borderRadius: borderRadius.md, padding: spacing.md, alignItems: 'center', }, saveButtonDisabled: { opacity: 0.6, }, saveButtonText: { color: colors.textInverse, fontSize: 16, fontWeight: '600', }, menuSection: { marginTop: spacing.md, backgroundColor: colors.card, }, menuItem: { flexDirection: 'row', alignItems: 'center', padding: spacing.md, borderBottomWidth: 1, borderBottomColor: colors.separator, }, menuText: { flex: 1, marginLeft: spacing.md, fontSize: 16, color: colors.text, }, searchHistorySection: { padding: spacing.md, backgroundColor: colors.background, borderBottomWidth: 1, borderBottomColor: colors.separator, }, searchHistoryTitle: { fontSize: 14, color: colors.textSecondary, marginBottom: spacing.sm, }, searchItem: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingVertical: spacing.sm, borderBottomWidth: 1, borderBottomColor: colors.separator, }, searchAddress: { flex: 1, fontSize: 14, color: colors.text, }, searchDelete: { padding: spacing.xs, }, button: { backgroundColor: colors.primary, borderRadius: borderRadius.md, paddingVertical: spacing.md, paddingHorizontal: spacing.xl, }, buttonText: { color: colors.textInverse, fontSize: 16, fontWeight: '600', }, linkButton: { marginTop: spacing.md, }, linkText: { color: colors.primary, fontSize: 14, }, logoutButton: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', margin: spacing.xl, padding: spacing.md, backgroundColor: colors.card, borderRadius: borderRadius.md, borderWidth: 1, borderColor: colors.danger, }, logoutText: { marginLeft: spacing.sm, color: colors.danger, fontSize: 16, fontWeight: '500', }, });