import React, { useState, useEffect } from 'react'; import { View, Text, StyleSheet, TouchableOpacity, Alert, TextInput, ScrollView, Image, ActivityIndicator, Modal } 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([]); // Config modal state const [showConfig, setShowConfig] = useState(false); const [configFirstName, setConfigFirstName] = useState(''); const [configLastName, setConfigLastName] = useState(''); const [configEmail, setConfigEmail] = useState(''); const [configCity, setConfigCity] = useState(''); const [configAddress, setConfigAddress] = useState(''); const [configSaving, setConfigSaving] = useState(false); const [configFeedback, setConfigFeedback] = 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); } } function openConfig() { setConfigFirstName(user?.first_name || ''); setConfigLastName(user?.last_name || ''); setConfigEmail(user?.email || ''); setConfigCity(user?.city || ''); setConfigAddress(user?.address || ''); setConfigFeedback(null); setShowConfig(true); } async function handleConfigSave() { setConfigSaving(true); setConfigFeedback(null); try { const res = await fetch('/api/users/me', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, credentials: 'include', body: JSON.stringify({ first_name: configFirstName.trim() || null, last_name: configLastName.trim() || null, email: configEmail.trim() || null, city: configCity.trim() || null, address: configAddress.trim() || null, }), }); if (!res.ok) { const err = await res.json().catch(() => ({})); throw new Error(err.error || 'Error al guardar'); } const updated = await res.json(); setFirstName(updated.first_name || ''); setLastName(updated.last_name || ''); setConfigFeedback({ type: 'ok', text: 'Perfil guardado.' }); setTimeout(() => setShowConfig(false), 1200); } catch (err: any) { setConfigFeedback({ type: 'err', text: err.message || 'Error al guardar' }); } finally { setConfigSaving(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) { const updated = await res.json(); // Trigger auth refresh to update user state } } catch (err) { console.error('Error saving avatar:', err); } } } 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 {/* Read-only Name Section */} Nombre {firstName || '—'} Apellidos {lastName || '—'} {/* Menu Section */} Configuración 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 {/* Config Modal */} Configuración setShowConfig(false)}> Nombre Apellidos Correo electrónico Ciudad Dirección {configFeedback && ( {configFeedback.text} )} setShowConfig(false)} disabled={configSaving} > Cancelar {configSaving ? ( ) : ( Guardar )} ); } 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, }, infoSection: { padding: spacing.xl, backgroundColor: colors.card, marginTop: spacing.md, gap: spacing.md, }, infoCard: { backgroundColor: colors.background, padding: spacing.md, borderRadius: borderRadius.md, borderWidth: 1, borderColor: colors.border || '#E0E0E0', }, infoLabel: { fontSize: 12, fontWeight: '600', color: colors.textSecondary, textTransform: 'uppercase', letterSpacing: 0.05, marginBottom: 4, }, infoValue: { fontSize: 16, fontWeight: '500', color: colors.text, }, 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', }, // Modal styles modalBackdrop: { flex: 1, backgroundColor: 'rgba(0, 0, 0, 0.5)', justifyContent: 'flex-end', }, modalContent: { backgroundColor: colors.card, borderTopLeftRadius: 20, borderTopRightRadius: 20, maxHeight: '90%', }, modalHeader: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', padding: spacing.lg, borderBottomWidth: 1, borderBottomColor: colors.separator, }, modalTitle: { fontSize: 18, fontWeight: 'bold', color: colors.text, }, modalBody: { padding: spacing.lg, }, modalField: { marginBottom: spacing.md, }, modalLabel: { fontSize: 13, fontWeight: '600', color: colors.textSecondary, marginBottom: spacing.xs, }, modalInput: { borderWidth: 1, borderColor: colors.border || '#E0E0E0', borderRadius: borderRadius.md, padding: spacing.md, fontSize: 16, color: colors.text, }, modalFeedback: { padding: spacing.md, borderRadius: borderRadius.md, marginBottom: spacing.md, }, modalFeedbackOk: { backgroundColor: 'rgba(0, 69, 13, 0.1)', borderWidth: 1, borderColor: 'rgba(0, 69, 13, 0.2)', }, modalFeedbackErr: { backgroundColor: 'rgba(186, 26, 26, 0.1)', borderWidth: 1, borderColor: 'rgba(186, 26, 26, 0.2)', }, modalFeedbackText: { fontSize: 14, color: colors.text, }, modalActions: { flexDirection: 'row', justifyContent: 'flex-end', gap: spacing.md, marginTop: spacing.md, }, modalCancelBtn: { paddingVertical: spacing.md, paddingHorizontal: spacing.lg, borderRadius: borderRadius.md, borderWidth: 1, borderColor: colors.border || '#E0E0E0', }, modalCancelText: { fontSize: 16, color: colors.text, }, modalSaveBtn: { backgroundColor: colors.primary, paddingVertical: spacing.md, paddingHorizontal: spacing.xl, borderRadius: borderRadius.md, minWidth: 100, alignItems: 'center', }, modalSaveBtnDisabled: { opacity: 0.6, }, modalSaveText: { color: colors.textInverse, fontSize: 16, fontWeight: '600', }, });