Skip to content

Commit 84ddf14

Browse files
committed
Fix: aplicar logger, rate limit a peticiones a firestore, corregir useEffect loop
1 parent ad47f04 commit 84ddf14

13 files changed

Lines changed: 392 additions & 43 deletions

File tree

package-lock.json

Lines changed: 108 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"preview": "vite preview"
1111
},
1212
"dependencies": {
13+
"@sentry/react": "^10.22.0",
1314
"clsx": "^2.1.1",
1415
"date-fns": "^4.1.0",
1516
"firebase": "^12.4.0",

src/App.tsx

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import type { GamePhase, Hypothesis } from "./types";
1717
import { Phase1Collider } from "./components/Games/Phase1Collider/Phase1Collider";
1818
import { Phase2Equation } from "./components/Games/Phase2Equation/Phase2Equation";
1919
import { Phase3Synthesis } from "./components/Games/Phase3Synthesis/Phase3Synthesis";
20+
import { logger } from "@/utils/logger";
2021

2122
function App() {
2223
const [currentPhase, setCurrentPhase] = useState<GamePhase>("intro");
@@ -35,9 +36,9 @@ function App() {
3536
useEffect(() => {
3637
if (!hasDraft()) {
3738
initializeDraft();
38-
console.log("🆕 Nuevo draft inicializado");
39+
logger.log("🆕 Nuevo draft inicializado");
3940
} else {
40-
console.log("📋 Draft existente encontrado");
41+
logger.log("📋 Draft existente encontrado");
4142
}
4243
setIsInitialized(true);
4344
}, []);
@@ -49,14 +50,14 @@ function App() {
4950
const handleHypothesisSelect = (hypothesis: Hypothesis) => {
5051
// Sobrescribir hipótesis en localStorage (permite cambiar de opinión)
5152
updateHypothesis(hypothesis);
52-
console.log(`✅ Hipótesis ${hypothesis} guardada en draft`);
53+
logger.log(`✅ Hipótesis ${hypothesis} guardada en draft`);
5354

5455
// Navegar a InputScreen
5556
setCurrentPhase("input");
5657
};
5758

5859
const handleInputSubmit = async (data: InputData) => {
59-
console.log("📝 Datos de input guardados en draft:", data);
60+
logger.log("📝 Datos de input guardados en draft:", data);
6061

6162
// Verificar si el draft está listo para enviar
6263
const draft = getDraft();
@@ -66,7 +67,7 @@ function App() {
6667
// Ir a los juegos primero
6768
setCurrentPhase("collider");
6869

69-
console.log("🎮 Navegando a juegos. Draft completo:", draft);
70+
logger.log("🎮 Navegando a juegos. Draft completo:", draft);
7071
} else {
7172
alert("Faltan datos. Por favor completa el formulario.");
7273
}
@@ -84,13 +85,13 @@ function App() {
8485
try {
8586
const draft = getDraft();
8687

87-
console.log("🚀 Enviando predicción completa a Firebase...");
88-
console.log("📦 Draft final:", draft);
88+
logger.log("🚀 Enviando predicción completa a Firebase...");
89+
logger.log("📦 Draft final:", draft);
8990

9091
// Enviar a Firebase
9192
await submitPrediction(draft);
9293

93-
console.log("✅ Predicción enviada exitosamente");
94+
logger.log("✅ Predicción enviada exitosamente");
9495

9596
// IMPORTANTE: NO limpiar el draft todavía
9697
// SuccessScreen lo necesita para mostrar los datos
@@ -99,7 +100,7 @@ function App() {
99100
// Navegar a pantalla de éxito
100101
setCurrentPhase("submitted");
101102
} catch (error) {
102-
console.error("❌ Error al enviar predicción:", error);
103+
logger.error("❌ Error al enviar predicción:", error);
103104
alert("Error al enviar tu predicción. Por favor intenta de nuevo.");
104105
} finally {
105106
setIsSaving(false);
@@ -108,19 +109,19 @@ function App() {
108109

109110
// Handlers para los juegos
110111
const handleColliderComplete = (score: number) => {
111-
console.log(`🎮 Collider completado: ${score} pts`);
112+
logger.log(`🎮 Collider completado: ${score} pts`);
112113
// updateGameScore("collider", score) ya se llama dentro del juego
113114
setCurrentPhase("equation");
114115
};
115116

116117
const handleEquationComplete = (score: number) => {
117-
console.log(`🎮 Equation completado: ${score} pts`);
118+
logger.log(`🎮 Equation completado: ${score} pts`);
118119
// updateGameScore("equation", score) ya se llama dentro del juego
119120
setCurrentPhase("synthesis");
120121
};
121122

122123
const handleSynthesisComplete = async (score: number) => {
123-
console.log(`🎮 Synthesis completado: ${score} pts`);
124+
logger.log(`🎮 Synthesis completado: ${score} pts`);
124125
// updateGameScore("synthesis", score) ya se llama dentro del juego
125126

126127
// Después del último juego, enviar TODO a Firebase

src/components/Admin/AdminPanel.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { db } from "@/lib/firebase";
44
import { AdminStats } from "./AdminStats";
55
import { AdminWall } from "./AdminWall";
66
import type { PredictionDraft } from "@/services/localStorageService";
7+
import { logger } from "@/utils/logger";
78

89
type Tab = "stats" | "wall";
910

@@ -28,9 +29,9 @@ export const AdminPanel = () => {
2829
})) as PredictionDraft[];
2930

3031
setPredictions(data);
31-
console.log("📊 Predicciones cargadas:", data.length);
32+
logger.log("📊 Predicciones cargadas:", data.length);
3233
} catch (error) {
33-
console.error("❌ Error cargando predicciones:", error);
34+
logger.error("❌ Error cargando predicciones:", error);
3435
} finally {
3536
setIsLoading(false);
3637
}

src/components/Intro/ParticleBackground.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,10 @@ export function ParticleBackground() {
3636
"rgba(168, 85, 247, ", // purple-500 (química)
3737
];
3838

39-
// Crear partículas
40-
const particleCount = 60;
39+
// Detectar móvil y optimizar
40+
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
41+
const particleCount = isMobile ? 20 : 60;
42+
const maxDistance = isMobile ? 80 : 120;
4143
const particles: Particle[] = [];
4244

4345
for (let i = 0; i < particleCount; i++) {
@@ -54,7 +56,7 @@ export function ParticleBackground() {
5456

5557
// Función de animación
5658
const animate = () => {
57-
ctx.clearRect(0, 0, canvas.width, canvas.height); // Limpiar completamente en vez de trail
59+
ctx.clearRect(0, 0, canvas.width, canvas.height);
5860

5961
// Actualizar y dibujar partículas
6062
particles.forEach((particle) => {
@@ -84,12 +86,12 @@ export function ParticleBackground() {
8486
const dy = particles[i].y - particles[j].y;
8587
const distance = Math.sqrt(dx * dx + dy * dy);
8688

87-
if (distance < 120) {
89+
if (distance < maxDistance) {
8890
ctx.beginPath();
8991
ctx.moveTo(particles[i].x, particles[i].y);
9092
ctx.lineTo(particles[j].x, particles[j].y);
9193
ctx.strokeStyle = `rgba(100, 200, 255, ${
92-
0.15 * (1 - distance / 120)
94+
0.15 * (1 - distance / maxDistance)
9395
})`;
9496
ctx.lineWidth = 0.5;
9597
ctx.stroke();

src/components/Layout/SpaceComputer.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { useMemo } from "react";
12
import { useCountdown } from "../../hooks/useCountdown";
23
import { useEventConfig } from "../../hooks/useEventConfig";
34
import type { GamePhase } from "../../types";
@@ -7,8 +8,16 @@ interface SpaceComputerProps {
78
}
89

910
export function SpaceComputer({ currentPhase }: SpaceComputerProps) {
10-
const { config } = useEventConfig(); // Removemos el loading check
11-
const countdown = useCountdown(config?.revealDate || Date.now());
11+
const { config } = useEventConfig();
12+
13+
// ✅ FIX: Memorizar la fecha target para evitar que cambie en cada render
14+
const targetDate = useMemo(() => {
15+
return (
16+
config?.revealDate || new Date("2025-10-26T19:00:00-03:00").getTime()
17+
);
18+
}, [config?.revealDate]);
19+
20+
const countdown = useCountdown(targetDate);
1221

1322
// Calcular progreso de gestación
1423
// FPP: 29 enero 2026

src/hooks/useEventConfig.ts

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,61 @@ import { useState, useEffect } from "react";
22
import { doc, getDoc } from "firebase/firestore";
33
import { db } from "../lib/firebase";
44
import type { EventConfig } from "../types";
5+
import { logger } from "../utils/logger";
6+
import { rateLimiter, RATE_LIMITS } from "../utils/rateLimiters";
57

68
export function useEventConfig() {
79
const [config, setConfig] = useState<EventConfig | null>(null);
810
const [loading, setLoading] = useState(true);
911
const [error, setError] = useState<string | null>(null);
1012

1113
useEffect(() => {
14+
let isMounted = true;
15+
1216
const fetchConfig = async () => {
17+
// ✅ Verificar rate limit ANTES de hacer la petición
18+
if (!rateLimiter.isAllowed("fetchConfig", RATE_LIMITS.CONFIG_FETCH)) {
19+
logger.warn("⚠️ Rate limit: fetchConfig bloqueado temporalmente");
20+
return;
21+
}
22+
1323
try {
1424
setLoading(true);
1525
setError(null);
1626

1727
const docRef = doc(db, "config", "event");
1828
const docSnap = await getDoc(docRef);
1929

30+
// Solo actualizar si el componente sigue montado
31+
if (!isMounted) return;
32+
2033
if (docSnap.exists()) {
2134
setConfig(docSnap.data() as EventConfig);
22-
console.log("✅ Configuración cargada desde Firestore");
35+
logger.log("✅ Configuración cargada desde Firestore");
2336
} else {
2437
setError("No se encontró la configuración del evento en Firestore");
25-
console.error("❌ Documento config/event no existe");
38+
logger.error("❌ Documento config/event no existe");
2639
}
2740
} catch (err) {
41+
if (!isMounted) return;
42+
2843
const errorMessage =
2944
err instanceof Error ? err.message : "Error desconocido";
3045
setError(`Error al cargar configuración: ${errorMessage}`);
31-
console.error("❌ Error en useEventConfig:", err);
46+
logger.error("❌ Error en useEventConfig:", err);
3247
} finally {
33-
setLoading(false);
48+
if (isMounted) {
49+
setLoading(false);
50+
}
3451
}
3552
};
3653

3754
fetchConfig();
55+
56+
// Cleanup: marcar componente como desmontado
57+
return () => {
58+
isMounted = false;
59+
};
3860
}, []);
3961

4062
return { config, loading, error };

0 commit comments

Comments
 (0)