diff --git a/src/App.jsx b/src/App.jsx
index 3b9e6ff..41c1184 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -1,29 +1,176 @@
-import { BrowserRouter, Routes, Route } from "react-router-dom";
+import { BrowserRouter, Routes, Route, Navigate, useLocation } from "react-router-dom";
+import { supabase } from "./services/supabase";
+import { useEffect, useState, createContext, useContext } from "react";
+import Login from "./components/Login";
import EmployeeCard from "./components/EmployeeCard";
import IdCardGenerator from "./Generator";
+import "./components/Login.css";
-export default function App() {
+const AuthContext = createContext(null);
+
+export const useAuth = () => useContext(AuthContext);
+
+const ALLOWED_DOMAIN = "@gomezleemarketing.com";
+const GLM_LOGO = "https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM_completo.png";
+
+function AuthProvider({ children }) {
+ const [user, setUser] = useState(null);
+ const [loading, setLoading] = useState(true);
+
+ useEffect(() => {
+ if (!supabase) {
+ console.warn("Supabase no configurado - falta VITE_SUPABASE_ANON_KEY");
+ setLoading(false);
+ return;
+ }
+
+ supabase.auth.getSession().then(({ data: { session } }) => {
+ setUser(session?.user ?? null);
+ setLoading(false);
+ });
+
+ const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => {
+ setUser(session?.user ?? null);
+ setLoading(false);
+ });
+
+ return () => subscription.unsubscribe();
+ }, []);
+
+ const signOut = async () => {
+ if (supabase) await supabase.auth.signOut();
+ };
+
+ if (loading) {
+ return (
+
+ );
+ }
return (
-
-
-
-
-
- }
- />
-
- }
- />
-
-
-
-
-
+
+ {children}
+
);
+}
+function ProtectedRoute({ children }) {
+ const { user, signOut, loading } = useAuth();
+ const location = useLocation();
+
+ if (loading) return null;
+
+ if (!user) {
+ return ;
+ }
+
+ if (!user.email?.endsWith(ALLOWED_DOMAIN)) {
+ signOut();
+ return (
+
+
+
+ Acceso no autorizado
+
+
+ Solo se permite acceso con cuentas corporativas @gomezleemarketing.com .
+ Tu cuenta {user.email} no tiene permisos.
+
+
+ Cerrar sesión y volver a intentar
+
+
+ );
+ }
+
+ return children;
+}
+
+function PublicRoute({ children }) {
+ const { user, loading } = useAuth();
+ const location = useLocation();
+
+ if (loading) return null;
+
+ if (user?.email?.endsWith(ALLOWED_DOMAIN)) {
+ const from = location.state?.from?.pathname || '/empleado-id/';
+ return ;
+ }
+
+ return children;
+}
+
+export default function App() {
+ return (
+
+
+
+
+
+
+ }
+ />
+
+
+
+
+ }
+ />
+
+
+
+
+ }
+ />
+
+ } />
+
+
+
+ );
}
\ No newline at end of file
diff --git a/src/components/Login.css b/src/components/Login.css
new file mode 100644
index 0000000..9a89d74
--- /dev/null
+++ b/src/components/Login.css
@@ -0,0 +1,163 @@
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+:root {
+ --azul-glm: #4F758B;
+ --azul-medio: #5B7F95;
+ --acero-claro: #6B8FA3;
+ --verde-glm: #6CC24A;
+ --verde-claro: #A4D65E;
+ --verde-bg: #EEF6E8;
+ --azul-seccion: #D6E8F4;
+ --gris-oscuro: #4A4A4A;
+ --gris-medio: #D0D0D0;
+ --gris-claro: #F5F5F5;
+ --blanco: #FFFFFF;
+ --naranja: #FF6A13;
+}
+
+.login-page {
+ min-height: 100vh;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 24px;
+ background: linear-gradient(135deg, var(--blanco) 0%, var(--gris-claro) 100%);
+ font-family: Arial, Helvetica, sans-serif;
+}
+
+.login-page.loading .login-card {
+ pointer-events: none;
+ opacity: 0.8;
+}
+
+.login-card {
+ background: var(--blanco);
+ border-radius: 16px;
+ box-shadow: 0 8px 32px rgba(79, 117, 139, 0.12);
+ padding: 48px 40px;
+ width: 100%;
+ max-width: 400px;
+ text-align: center;
+ border: 1px solid var(--gris-medio);
+}
+
+.logo {
+ width: 180px;
+ height: auto;
+ margin-bottom: 24px;
+}
+
+.title {
+ font-size: 22px;
+ font-weight: 700;
+ color: var(--azul-glm);
+ letter-spacing: 0.5px;
+ margin-bottom: 6px;
+ text-transform: uppercase;
+}
+
+.subtitle {
+ font-size: 13px;
+ color: var(--acero-claro);
+ font-weight: 400;
+ margin-bottom: 24px;
+}
+
+.login-divider {
+ height: 2px;
+ background: var(--verde-glm);
+ margin: 24px 0;
+ border-radius: 1px;
+}
+
+.btn-google {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 12px;
+ width: 100%;
+ padding: 14px 24px;
+ background: var(--blanco);
+ border: 2px solid var(--gris-medio);
+ border-radius: 10px;
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 15px;
+ font-weight: 600;
+ color: var(--gris-oscuro);
+ cursor: pointer;
+ transition: all 0.2s ease;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+}
+
+.btn-google:hover:not(:disabled) {
+ border-color: var(--verde-glm);
+ background: var(--verde-bg);
+ color: var(--azul-glm);
+ box-shadow: 0 4px 16px rgba(108, 194, 74, 0.2);
+ transform: translateY(-1px);
+}
+
+.btn-google:active:not(:disabled) {
+ transform: translateY(0);
+}
+
+.btn-google:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+}
+
+.google-icon {
+ flex-shrink: 0;
+}
+
+.domain-hint {
+ margin-top: 14px;
+ font-size: 12px;
+ color: var(--acero-claro);
+ font-weight: 500;
+}
+
+.error-message {
+ margin-top: 16px;
+ padding: 12px 16px;
+ background: #FDECEA;
+ border: 1px solid #F5C6CB;
+ border-radius: 8px;
+ font-size: 13px;
+ color: #C0392B;
+ text-align: left;
+}
+
+.spinner {
+ width: 40px;
+ height: 40px;
+ border: 3px solid var(--gris-medio);
+ border-top-color: var(--verde-glm);
+ border-radius: 50%;
+ animation: spin 0.8s linear infinite;
+ margin: 16px auto 0;
+}
+
+@keyframes spin {
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+@media (max-width: 480px) {
+ .login-card {
+ padding: 36px 24px;
+ }
+
+ .title {
+ font-size: 19px;
+ }
+
+ .logo {
+ width: 150px;
+ }
+}
\ No newline at end of file
diff --git a/src/components/Login.jsx b/src/components/Login.jsx
new file mode 100644
index 0000000..47057c5
--- /dev/null
+++ b/src/components/Login.jsx
@@ -0,0 +1,97 @@
+import { useState, useEffect } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { supabase } from '../services/supabase';
+import './Login.css';
+
+const ALLOWED_DOMAIN = '@gomezleemarketing.com';
+const GLM_LOGO = 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM_completo.png';
+
+export default function Login() {
+ const navigate = useNavigate();
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState('');
+
+ useEffect(() => {
+ supabase.auth.getSession().then(({ data: { session } }) => {
+ if (session?.user?.email?.endsWith(ALLOWED_DOMAIN)) {
+ navigate('/empleado-id/');
+ }
+ });
+ }, [navigate]);
+
+ const handleGoogleLogin = async () => {
+ setLoading(true);
+ setError('');
+
+ const redirectUrl = `${window.location.origin}/empleado-id/`;
+
+ const { error: authError } = await supabase.auth.signInWithOAuth({
+ provider: 'google',
+ options: {
+ redirectTo: redirectUrl,
+ queryParams: {
+ hd: 'gomezleemarketing.com',
+ },
+ },
+ });
+
+ if (authError) {
+ setError(authError.message);
+ setLoading(false);
+ }
+ };
+
+ if (loading) {
+ return (
+
+
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
GLM ID Card Generator
+
GomezLee Marketing · Carnet Corporativo CR80
+
+
+
+
+
+
+
+
+
+
+ Continuar con Google
+
+
+
Solo cuentas @gomezleemarketing.com
+
+ {error &&
{error}
}
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/services/supabase.js b/src/services/supabase.js
index 7380337..2ea490e 100644
--- a/src/services/supabase.js
+++ b/src/services/supabase.js
@@ -1,6 +1,7 @@
import { createClient } from "@supabase/supabase-js";
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
-const supabaseKey = import.meta.env.VITE_SUPABASE_SERVICE_ROLE_KEY;
+const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;
-export const supabase = createClient(supabaseUrl, supabaseKey);
\ No newline at end of file
+// Single client for auth (uses anon key)
+export const supabase = createClient(supabaseUrl, supabaseAnonKey);
\ No newline at end of file