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 ( +
+ GLM +

+ Acceso no autorizado +

+

+ Solo se permite acceso con cuentas corporativas @gomezleemarketing.com. + Tu cuenta {user.email} no tiene permisos. +

+ +
+ ); + } + + 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 ( +
+
+ GLM +
+
+
+ ); + } + + return ( +
+
+ GomezLee Marketing + +

GLM ID Card Generator

+

GomezLee Marketing · Carnet Corporativo CR80

+ +
+ + + +

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