// App completa en React Native con Expo y React Navigation
// Guarda datos localmente con AsyncStorage y compatible con GitHub/Expo
import React, { useState, useEffect } from 'react';
import { View, Text, Button, TextInput, FlatList, TouchableOpacity, StyleSheet, Alert, ScrollView } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
const Stack = createNativeStackNavigator();
const rosaClaro = '#f8d7e8';
const rosaMedio = '#f06292';
const rosaOscuro = '#880e4f';
function HomeScreen({ navigation }) {
return (
Mi App de Estudio
);
}
function ListaAsignaturas({ navigation, route }) {
const tipo = route.name;
const [asignaturas, setAsignaturas] = useState([]);
const [nombre, setNombre] = useState('');
useEffect(() => {
AsyncStorage.getItem(tipo).then(data => {
if (data) setAsignaturas(JSON.parse(data));
});
}, []);
useEffect(() => {
AsyncStorage.setItem(tipo, JSON.stringify(asignaturas));
}, [asignaturas]);
const agregar = () => {
if (nombre.trim()) {
setAsignaturas([...asignaturas, { nombre, id: Date.now().toString() }]);
setNombre('');
}
};
const eliminar = (id) => {
Alert.alert('Eliminar', '¿Deseas eliminar esta asignatura?', [
{ text: 'Cancelar' },
{ text: 'Eliminar', onPress: () => setAsignaturas(asignaturas.filter(a => a.id !== id)) },
]);
};
return (
{tipo}
item.id}
renderItem={({ item }) => (
{item.nombre}
eliminar(item.id)}>
⋮
)}
/>
);
}
function CalculadoraNotas() {
const [notas, setNotas] = useState([{ nota: '', porcentaje: '' }]);
const [minAprobacion, setMinAprobacion] = useState('4.0');
const [promedio, setPromedio] = useState(null);
const [notaNecesaria, setNotaNecesaria] = useState(null);
const calcular = () => {
let totalPeso = 0;
let acumulado = 0;
notas.forEach(({ nota, porcentaje }) => {
const n = parseFloat(nota);
const p = parseFloat(porcentaje);
if (!isNaN(n) && !isNaN(p)) {
acumulado += (n * p) / 100;
totalPeso += p;
}
});
const restante = 100 - totalPeso;
const min = parseFloat(minAprobacion);
setPromedio(acumulado);
if (restante > 0 && !isNaN(min)) {
const falta = ((min - acumulado) * 100) / restante;
setNotaNecesaria(falta);
} else {
setNotaNecesaria(null);
}
};
const actualizarNota = (index, field, value) => {
const nuevasNotas = [...notas];
nuevasNotas[index][field] = value;
setNotas(nuevasNotas);
};
const agregarFila = () => {
setNotas([...notas, { nota: '', porcentaje: '' }]);
};
return (
Cálculo Rápido
{notas.map((item, index) => (
actualizarNota(index, 'nota', text)}
/>
actualizarNota(index, 'porcentaje', text)}
/>
))}
{promedio !== null && (
Promedio actual: {promedio.toFixed(2)}
)}
{notaNecesaria !== null && (
Nota necesaria para aprobar: {notaNecesaria > 7 ? 'Imposible con lo restante' : notaNecesaria.toFixed(2)}
)}
);
}
function PorHacer() {
const [tareas, setTareas] = useState([]);
const [texto, setTexto] = useState('');
useEffect(() => {
AsyncStorage.getItem('PorHacer').then(data => {
if (data) setTareas(JSON.parse(data));
});
}, []);
useEffect(() => {
AsyncStorage.setItem('PorHacer', JSON.stringify(tareas));
}, [tareas]);
const agregar = () => {
if (texto.trim()) {
setTareas([...tareas, { texto, hecho: false, id: Date.now().toString() }]);
setTexto('');
}
};
const toggle = (id) => {
setTareas(tareas.map(t => t.id === id ? { ...t, hecho: !t.hecho } : t));
};
const eliminar = (id) => {
Alert.alert('Eliminar', '¿Eliminar esta tarea?', [
{ text: 'Cancelar' },
{ text: 'Eliminar', onPress: () => setTareas(tareas.filter(t => t.id !== id)) },
]);
};
return (
Por Hacer
item.id}
renderItem={({ item }) => (
toggle(item.id)}
>
{item.texto}
eliminar(item.id)}>
⋮
)}
/>
);
}
export default function App() {
return (
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: rosaClaro,
padding: 20,
},
title: {
fontSize: 24,
fontWeight: 'bold',
marginBottom: 10,
color: rosaOscuro,
textAlign: 'center'
},
input: {
borderWidth: 1,
borderColor: rosaOscuro,
padding: 10,
marginVertical: 10,
backgroundColor: 'white',
},
item: {
backgroundColor: 'white',
padding: 15,
marginVertical: 5,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
dots: {
fontSize: 18,
color: rosaOscuro,
},
tachado: {
backgroundColor: '#f5cfdc',
textDecorationLine: 'line-through',
},
filaNotas: {
flexDirection: 'row',
justifyContent: 'space-between',
},
resultado: {
marginTop: 15,
fontSize: 16,
fontWeight: 'bold',
color: rosaOscuro,
textAlign: 'center'
}
});