Initial commit

This commit is contained in:
2026-06-12 17:41:53 -04:00
commit a7dcd5cba9
20 changed files with 5176 additions and 0 deletions
+145
View File
@@ -0,0 +1,145 @@
/**
* @license
* SPDX-License-Identifier: Apache-2.0
*/
import { useEffect, useState } from "react";
import type { Session } from "@supabase/supabase-js";
import { AnimatePresence, motion } from "motion/react";
import { LoginScreen } from "./components/LoginScreen";
import { PortalPrincipal } from "./components/PortalPrincipal";
import { ALLOWED_EMAILS, isAllowedEmail } from "./lib/auth";
import { isSupabaseConfigured, supabase } from "./lib/supabase";
export default function App() {
const [session, setSession] = useState<Session | null>(null);
const [isLoadingSession, setIsLoadingSession] = useState(true);
const [authError, setAuthError] = useState("");
const validateAndSetSession = async (currentSession: Session | null) => {
const email = currentSession?.user?.email;
if (!currentSession) {
setSession(null);
return;
}
if (!isAllowedEmail(email)) {
setSession(null);
setAuthError(
"Este correo no tiene acceso al portal. Usa ymadera@gomezleemarketing.com o iaracena@gomezleemarketing.com."
);
await supabase?.auth.signOut();
return;
}
setAuthError("");
setSession(currentSession);
};
useEffect(() => {
if (!isSupabaseConfigured || !supabase) {
setAuthError("Falta configurar Supabase en el archivo .env.local.");
setIsLoadingSession(false);
return;
}
let isMounted = true;
supabase.auth.getSession().then(async ({ data, error }) => {
if (!isMounted) return;
if (error) {
setAuthError("No se pudo validar la sesión. Intenta iniciar sesión nuevamente.");
setSession(null);
} else {
await validateAndSetSession(data.session);
}
setIsLoadingSession(false);
});
const { data: listener } = supabase.auth.onAuthStateChange(async (_event, currentSession) => {
await validateAndSetSession(currentSession);
setIsLoadingSession(false);
});
return () => {
isMounted = false;
listener.subscription.unsubscribe();
};
}, []);
const handleLogin = async () => {
setAuthError("");
if (!isSupabaseConfigured || !supabase) {
setAuthError("Falta configurar VITE_SUPABASE_URL y VITE_SUPABASE_ANON_KEY en .env.local.");
return;
}
const redirectTo = `${window.location.origin}${import.meta.env.BASE_URL}`;
const { error } = await supabase.auth.signInWithOAuth({
provider: "google",
options: {
redirectTo,
},
});
if (error) {
setAuthError("No se pudo iniciar sesión con Google. Revisa la configuración de Supabase Auth.");
}
};
const handleLogout = async () => {
setAuthError("");
await supabase?.auth.signOut();
setSession(null);
};
if (isLoadingSession) {
return (
<div className="min-h-screen bg-[#f5f3f3] flex items-center justify-center text-[#365c71] text-sm font-semibold">
Validando sesión...
</div>
);
}
const isLoggedIn = Boolean(session && isAllowedEmail(session.user.email));
return (
<div className="min-h-screen bg-[#fbf9f8]" id="app-container">
<AnimatePresence mode="wait">
{!isLoggedIn ? (
<motion.div
key="login"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.3 }}
className="w-full h-full"
>
<LoginScreen
onLogin={handleLogin}
authError={authError}
isSupabaseConfigured={isSupabaseConfigured}
allowedEmails={ALLOWED_EMAILS}
/>
</motion.div>
) : (
<motion.div
key="portal"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.3 }}
className="w-full h-full"
>
<PortalPrincipal onLogout={handleLogout} userEmail={session?.user.email ?? ""} />
</motion.div>
)}
</AnimatePresence>
</div>
);
}