diff --git a/frontend/App.js b/frontend/App.js index 1a653cf..6f16309 100644 --- a/frontend/App.js +++ b/frontend/App.js @@ -1,29 +1,249 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { - value: true -}); -Object.defineProperty(exports, "AppFragmentNode", { - enumerable: true, - get: function() { - return AppFragmentNode; - } -}); -function _core() { - const data = require("@urql/core"); - _core = function() { - return data; - }; - return data; -} -const AppFragmentNode = (0, _core().gql)` - fragment AppFragment on App { - id - scopeKey - ownerAccount { - id - name - } - } -`; +import { StatusBar } from 'expo-status-bar'; +import { StyleSheet, Text, View, TextInput, TouchableOpacity, ActivityIndicator, ScrollView, Alert } from 'react-native'; +import { useState } from 'react'; -//# sourceMappingURL=App.js.map \ No newline at end of file +const API_URL = 'http://10.137.112.65:8000'; + +export default function App() { + const [screen, setScreen] = useState('login'); + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [token, setToken] = useState(null); + const [domicilioId, setDomicilioId] = useState(null); + const [eta, setEta] = useState(null); + const [loading, setLoading] = useState(false); + const [colonia, setColonia] = useState(''); + const [direccion, setDireccion] = useState(''); + + const register = async () => { + setLoading(true); + try { + const res = await fetch(`${API_URL}/auth/register`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email, password }), + }); + const data = await res.json(); + if (data.access_token) { + setToken(data.access_token); + setScreen('domicilio'); + } else { + Alert.alert('Error', data.detail || 'Error al registrar'); + } + } catch (e) { + Alert.alert('Error', 'No se pudo conectar al servidor'); + } + setLoading(false); + }; + + const login = async () => { + setLoading(true); + try { + const res = await fetch(`${API_URL}/auth/login`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email, password }), + }); + const data = await res.json(); + if (data.access_token) { + setToken(data.access_token); + setScreen('domicilio'); + } else { + Alert.alert('Error', 'Credenciales incorrectas'); + } + } catch (e) { + Alert.alert('Error', 'No se pudo conectar al servidor'); + } + setLoading(false); + }; + + const guardarDomicilio = async () => { + setLoading(true); + try { + const res = await fetch(`${API_URL}/domicilios`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}`, + }, + body: JSON.stringify({ + direccion, + colonia, + lat: 20.5185, + lng: -100.8450, + }), + }); + const data = await res.json(); + if (data.id) { + setDomicilioId(data.id); + setScreen('eta'); + consultarETA(data.id); + } else { + Alert.alert('Error', data.detail || 'Colonia no encontrada'); + } + } catch (e) { + Alert.alert('Error', 'No se pudo guardar el domicilio'); + } + setLoading(false); + }; + + const consultarETA = async (id) => { + setLoading(true); + try { + const res = await fetch(`${API_URL}/eta/${id}`, { + headers: { 'Authorization': `Bearer ${token}` }, + }); + const data = await res.json(); + setEta(data); + } catch (e) { + Alert.alert('Error', 'No se pudo obtener el ETA'); + } + setLoading(false); + }; + + if (screen === 'login') return ( + + + 🚛 BasuraApp + Ingresa a tu cuenta + + + {loading ? : <> + + Iniciar sesión + + + Registrarme + + } + + ); + + if (screen === 'domicilio') return ( + + 📍 Mi domicilio + ¿En qué colonia vives? + + + Colonias disponibles: Zona Centro, Las Arboledas, Trojes, San Juanico, Los Olivos, Rancho Seco, Las Insurgentes + {loading ? : + + Guardar y ver horario + } + + ); + + if (screen === 'eta') return ( + + 🕐 Horario de recolección + {loading ? : eta ? <> + + {eta.evento === 'TRUCK_PROXIMITY' ? '🚨 ¡Camión cercano!' : + eta.evento === 'ROUTE_START' ? '🟢 Ruta iniciada' : + eta.evento === 'ROUTE_COMPLETED' ? '✅ Servicio finalizado' : '🚛 En camino'} + {eta.mensaje} + + Ventana de llegada + {eta.ventana_inicio} – {eta.ventana_fin} + + 📍 {eta.colonia} · {eta.route_id} + + + 🔒 Solo ves la información de tu zona. No se muestra la ruta completa del camión. + + consultarETA(domicilioId)}> + Actualizar + + setScreen('separacion')}> + 📚 Guía de separación + + setScreen('reporte')}> + 📋 Reportar incidencia + + : Sin datos} + + ); + + if (screen === 'separacion') return ( + + ♻️ Guía de separación + {[ + { emoji: '🟢', tipo: 'Orgánicos', ejemplos: 'Cáscaras, restos de comida, café, frutas' }, + { emoji: '🔵', tipo: 'Reciclables', ejemplos: 'Papel, cartón, plástico, vidrio, metal' }, + { emoji: '🔴', tipo: 'Sanitarios', ejemplos: 'Pañales, papel higiénico, gasas, algodón' }, + { emoji: '⚠️', tipo: 'Especiales', ejemplos: 'Pilas, medicamentos, electrónicos, pinturas' }, + ].map(item => ( + + {item.emoji} + + {item.tipo} + {item.ejemplos} + + + ))} + setScreen('eta')}> + ← Volver al horario + + + ); + + if (screen === 'reporte') return ( + + 📋 Reportar incidencia + ¿Qué problema tuviste? + {['El camión no pasó', 'Pasó fuera de horario', 'No recogió mis residuos', 'Otro'].map(tipo => ( + { + await fetch(`${API_URL}/reportes?domicilio_id=${domicilioId}&tipo=${tipo}&descripcion=${tipo}`, { + method: 'POST', + headers: { 'Authorization': `Bearer ${token}` } + }); + Alert.alert('¡Gracias!', 'Tu reporte fue enviado correctamente.'); + setScreen('eta'); + }}> + {tipo} + + ))} + setScreen('eta')}> + ← Cancelar + + + ); +} + +const styles = StyleSheet.create({ + container: { flexGrow: 1, backgroundColor: '#f0f4f8', alignItems: 'center', + justifyContent: 'center', padding: 24 }, + title: { fontSize: 28, fontWeight: 'bold', color: '#1a7a4a', marginBottom: 6, textAlign: 'center' }, + subtitle: { fontSize: 15, color: '#555', marginBottom: 24, textAlign: 'center' }, + input: { width: '100%', backgroundColor: '#fff', borderRadius: 10, padding: 14, + fontSize: 15, marginBottom: 12, borderWidth: 1, borderColor: '#ddd' }, + btn: { width: '100%', backgroundColor: '#1a7a4a', borderRadius: 10, + padding: 16, alignItems: 'center', marginTop: 8 }, + btnText: { color: '#fff', fontWeight: 'bold', fontSize: 16 }, + btnSecondary: { width: '100%', borderRadius: 10, padding: 16, + alignItems: 'center', marginTop: 8, borderWidth: 1, borderColor: '#1a7a4a' }, + btnSecondaryText: { color: '#1a7a4a', fontWeight: 'bold', fontSize: 15 }, + hint: { fontSize: 11, color: '#888', marginBottom: 16, textAlign: 'center' }, + etaCard: { width: '100%', backgroundColor: '#fff', borderRadius: 16, + padding: 20, marginBottom: 16, borderWidth: 1, borderColor: '#d0e8d8' }, + etaEvento: { fontSize: 18, fontWeight: 'bold', color: '#1a7a4a', marginBottom: 8 }, + etaMensaje: { fontSize: 15, color: '#333', marginBottom: 16, lineHeight: 22 }, + ventanaBox: { backgroundColor: '#e8f5ee', borderRadius: 10, padding: 14, marginBottom: 12 }, + ventanaLabel: { fontSize: 12, color: '#555', marginBottom: 4 }, + ventanaHora: { fontSize: 24, fontWeight: 'bold', color: '#1a7a4a' }, + coloniaText: { fontSize: 12, color: '#888' }, + privacyBox: { width: '100%', backgroundColor: '#fff8e1', borderRadius: 10, + padding: 14, marginBottom: 16, borderWidth: 1, borderColor: '#ffe082' }, + privacyText: { fontSize: 12, color: '#795548', textAlign: 'center' }, + separacionCard: { width: '100%', backgroundColor: '#fff', borderRadius: 12, + padding: 16, marginBottom: 12, flexDirection: 'row', alignItems: 'center', + gap: 14, borderWidth: 1, borderColor: '#ddd' }, + separacionEmoji: { fontSize: 32 }, + separacionTipo: { fontSize: 16, fontWeight: 'bold', color: '#333' }, + separacionEjemplos: { fontSize: 12, color: '#666', marginTop: 2 }, +}); \ No newline at end of file