diff --git a/frontend-mobile/app/(tabs)/profile.tsx b/frontend-mobile/app/(tabs)/profile.tsx
index d9b088f..f471c2e 100644
--- a/frontend-mobile/app/(tabs)/profile.tsx
+++ b/frontend-mobile/app/(tabs)/profile.tsx
@@ -1,23 +1,219 @@
-import { View, Text, StyleSheet } from 'react-native';
+import React from 'react';
+import { View, Text, StyleSheet, TouchableOpacity, Alert } from 'react-native';
+import { useRouter } from 'expo-router';
+import { Ionicons } from '@expo/vector-icons';
+import { useAuth } from '../../hooks/useAuth';
+import { colors, spacing, borderRadius } from '../../constants/theme';
export default function ProfileScreen() {
+ const router = useRouter();
+ const { user, isAuthenticated, isLoading, logout, isAdmin } = useAuth();
+
+ 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 (
- Perfil
+
+
+
+
+ {user?.username}
+ {isAdmin && Administrador}
+
+
+
+ {isAdmin && (
+
+
+ Panel Admin
+
+
+ )}
+
+
+
+ Favoritos
+
+
+
+
+
+ Notificaciones
+
+
+
+
+
+ Ayuda
+
+
+
+
+
+
+ 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',
- backgroundColor: '#F2F2F7',
+ padding: spacing.xl,
},
- title: {
+ authTitle: {
fontSize: 24,
fontWeight: 'bold',
- color: '#1C1C1E',
+ color: colors.text,
+ marginTop: spacing.lg,
+ },
+ authSubtitle: {
+ fontSize: 16,
+ color: colors.textSecondary,
+ textAlign: 'center',
+ marginTop: spacing.sm,
+ marginBottom: spacing.xl,
+ },
+ header: {
+ alignItems: 'center',
+ padding: spacing.xl,
+ backgroundColor: colors.card,
+ },
+ avatar: {
+ width: 80,
+ height: 80,
+ borderRadius: 40,
+ backgroundColor: colors.background,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ username: {
+ fontSize: 20,
+ fontWeight: 'bold',
+ color: colors.text,
+ marginTop: spacing.md,
+ },
+ adminBadge: {
+ fontSize: 12,
+ color: colors.primary,
+ backgroundColor: '#E3F2FD',
+ paddingHorizontal: spacing.sm,
+ paddingVertical: spacing.xs,
+ borderRadius: borderRadius.sm,
+ marginTop: spacing.sm,
+ },
+ 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,
+ },
+ 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',
},
});
diff --git a/frontend-mobile/app/auth/login.tsx b/frontend-mobile/app/auth/login.tsx
new file mode 100644
index 0000000..dd24add
--- /dev/null
+++ b/frontend-mobile/app/auth/login.tsx
@@ -0,0 +1,155 @@
+import React, { useState } from 'react';
+import {
+ View,
+ Text,
+ TextInput,
+ TouchableOpacity,
+ StyleSheet,
+ KeyboardAvoidingView,
+ Platform,
+ Alert
+} from 'react-native';
+import { useRouter } from 'expo-router';
+import { useAuthStore } from '../../store/authStore';
+import { colors, spacing, borderRadius } from '../../constants/theme';
+
+export default function LoginScreen() {
+ const router = useRouter();
+ const { login } = useAuthStore();
+ const [username, setUsername] = useState('');
+ const [password, setPassword] = useState('');
+ const [isLoading, setIsLoading] = useState(false);
+
+ const handleLogin = async () => {
+ if (!username || !password) {
+ Alert.alert('Error', 'Por favor ingresa usuario y contraseña');
+ return;
+ }
+
+ setIsLoading(true);
+ try {
+ await login(username, password);
+ router.replace('/(tabs)');
+ } catch (error) {
+ Alert.alert('Error', 'Credenciales incorrectas');
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ return (
+
+
+ Iniciar Sesión
+ Ingresa tus credenciales para continuar
+
+
+ Usuario
+
+
+
+
+ Contraseña
+
+
+
+
+
+ {isLoading ? 'Ingresando...' : 'Iniciar Sesión'}
+
+
+
+ router.push('/auth/register')}
+ >
+ ¿No tienes cuenta? Regístrate
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.background,
+ },
+ form: {
+ flex: 1,
+ justifyContent: 'center',
+ padding: spacing.xl,
+ },
+ title: {
+ fontSize: 28,
+ fontWeight: 'bold',
+ color: colors.text,
+ marginBottom: spacing.sm,
+ },
+ subtitle: {
+ fontSize: 16,
+ color: colors.textSecondary,
+ marginBottom: spacing.xl,
+ },
+ inputContainer: {
+ marginBottom: spacing.md,
+ },
+ label: {
+ fontSize: 14,
+ fontWeight: '500',
+ color: colors.text,
+ marginBottom: spacing.xs,
+ },
+ input: {
+ backgroundColor: colors.card,
+ borderRadius: borderRadius.md,
+ padding: spacing.md,
+ fontSize: 16,
+ color: colors.text,
+ },
+ button: {
+ backgroundColor: colors.primary,
+ borderRadius: borderRadius.md,
+ padding: spacing.md,
+ alignItems: 'center',
+ marginTop: spacing.md,
+ },
+ buttonDisabled: {
+ opacity: 0.6,
+ },
+ buttonText: {
+ color: colors.textInverse,
+ fontSize: 16,
+ fontWeight: '600',
+ },
+ linkButton: {
+ marginTop: spacing.lg,
+ alignItems: 'center',
+ },
+ linkText: {
+ color: colors.primary,
+ fontSize: 14,
+ },
+});
diff --git a/frontend-mobile/app/auth/register.tsx b/frontend-mobile/app/auth/register.tsx
new file mode 100644
index 0000000..eeffd21
--- /dev/null
+++ b/frontend-mobile/app/auth/register.tsx
@@ -0,0 +1,172 @@
+import React, { useState } from 'react';
+import {
+ View,
+ Text,
+ TextInput,
+ TouchableOpacity,
+ StyleSheet,
+ KeyboardAvoidingView,
+ Platform,
+ Alert
+} from 'react-native';
+import { useRouter } from 'expo-router';
+import { register } from '../../services/auth';
+import { colors, spacing, borderRadius } from '../../constants/theme';
+
+export default function RegisterScreen() {
+ const router = useRouter();
+ const [username, setUsername] = useState('');
+ const [password, setPassword] = useState('');
+ const [confirmPassword, setConfirmPassword] = useState('');
+ const [isLoading, setIsLoading] = useState(false);
+
+ const handleRegister = async () => {
+ if (!username || !password || !confirmPassword) {
+ Alert.alert('Error', 'Por favor completa todos los campos');
+ return;
+ }
+
+ if (password !== confirmPassword) {
+ Alert.alert('Error', 'Las contraseñas no coinciden');
+ return;
+ }
+
+ setIsLoading(true);
+ try {
+ await register(username, password);
+ router.replace('/(tabs)');
+ } catch (error) {
+ Alert.alert('Error', 'No se pudo crear la cuenta');
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ return (
+
+
+ Crear Cuenta
+ Regístrate para empezar
+
+
+ Usuario
+
+
+
+
+ Contraseña
+
+
+
+
+ Confirmar Contraseña
+
+
+
+
+
+ {isLoading ? 'Creando cuenta...' : 'Crear Cuenta'}
+
+
+
+ router.back()}
+ >
+ ¿Ya tienes cuenta? Inicia sesión
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.background,
+ },
+ form: {
+ flex: 1,
+ justifyContent: 'center',
+ padding: spacing.xl,
+ },
+ title: {
+ fontSize: 28,
+ fontWeight: 'bold',
+ color: colors.text,
+ marginBottom: spacing.sm,
+ },
+ subtitle: {
+ fontSize: 16,
+ color: colors.textSecondary,
+ marginBottom: spacing.xl,
+ },
+ inputContainer: {
+ marginBottom: spacing.md,
+ },
+ label: {
+ fontSize: 14,
+ fontWeight: '500',
+ color: colors.text,
+ marginBottom: spacing.xs,
+ },
+ input: {
+ backgroundColor: colors.card,
+ borderRadius: borderRadius.md,
+ padding: spacing.md,
+ fontSize: 16,
+ color: colors.text,
+ },
+ button: {
+ backgroundColor: colors.primary,
+ borderRadius: borderRadius.md,
+ padding: spacing.md,
+ alignItems: 'center',
+ marginTop: spacing.md,
+ },
+ buttonDisabled: {
+ opacity: 0.6,
+ },
+ buttonText: {
+ color: colors.textInverse,
+ fontSize: 16,
+ fontWeight: '600',
+ },
+ linkButton: {
+ marginTop: spacing.lg,
+ alignItems: 'center',
+ },
+ linkText: {
+ color: colors.primary,
+ fontSize: 14,
+ },
+});