fix al la posicion de donde se encuentra el carrito de compra y animacion al carrito al hacer hover

This commit is contained in:
Miguel Ortega committed 2026-08-24 22:03:16 -05:00
1 parent 2ae7091a3a
commit b76ae84bf6
6 files changed
+311 -60

No files matched your search

+21 -3
View File
@@ -1,6 +1,6 @@
import { useState } from 'react'; import { useState } from 'react';
import { Routes, Route } from 'react-router'; import { Routes, Route } from 'react-router';
import type { Course, CartItem } from './types/course'; import type { Course, CartItem, User } from './types/course';
import { mockCourses } from './data/mockCourses'; import { mockCourses } from './data/mockCourses';
import { Navbar } from './components/layout/Navbar'; import { Navbar } from './components/layout/Navbar';
import { Footer } from './components/layout/Footer'; import { Footer } from './components/layout/Footer';
@@ -27,6 +27,22 @@ export function App() {
const [isCartOpen, setIsCartOpen] = useState(false); const [isCartOpen, setIsCartOpen] = useState(false);
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
// Auth State
const [currentUser, setCurrentUser] = useState<User | null>(() => {
const savedUser = localStorage.getItem('rycourses_user');
return savedUser ? JSON.parse(savedUser) : null;
});
const handleLogin = (user: User) => {
setCurrentUser(user);
localStorage.setItem('rycourses_user', JSON.stringify(user));
};
const handleLogout = () => {
setCurrentUser(null);
localStorage.removeItem('rycourses_user');
};
// Toast feedback state // Toast feedback state
const [toastMessage, setToastMessage] = useState<string | null>(null); const [toastMessage, setToastMessage] = useState<string | null>(null);
@@ -88,6 +104,8 @@ export function App() {
{/* Main Navigation Bar */} {/* Main Navigation Bar */}
<Navbar <Navbar
currentUser={currentUser}
onLogout={handleLogout}
cartCount={cartItems.length} cartCount={cartItems.length}
onOpenCart={() => setIsCartOpen(true)} onOpenCart={() => setIsCartOpen(true)}
onOpenCreateCourseModal={() => setIsCreateModalOpen(true)} onOpenCreateCourseModal={() => setIsCreateModalOpen(true)}
@@ -171,11 +189,11 @@ export function App() {
/> />
<Route <Route
path="/login" path="/login"
element={<LoginView onShowToast={showToast} />} element={<LoginView onShowToast={showToast} onLogin={handleLogin} />}
/> />
<Route <Route
path="/register" path="/register"
element={<RegisterView onShowToast={showToast} />} element={<RegisterView onShowToast={showToast} onLogin={handleLogin} />}
/> />
</Routes> </Routes>
</main> </main>
+181 -45
View File
@@ -1,4 +1,4 @@
import React, { useState } from 'react'; import React, { useState, useEffect } from 'react';
import { useNavigate, useLocation } from 'react-router'; import { useNavigate, useLocation } from 'react-router';
import { import {
FiSearch, FiSearch,
@@ -11,10 +11,13 @@ import {
FiUser, FiUser,
FiShield, FiShield,
} from 'react-icons/fi'; } from 'react-icons/fi';
import { FaChalkboardTeacher } from 'react-icons/fa'; import { FaChalkboardTeacher, FaSignOutAlt } from 'react-icons/fa';
import { BrandLogo } from '../common/BrandLogo'; import { BrandLogo } from '../common/BrandLogo';
import type { User } from '../../types/course';
interface NavbarProps { interface NavbarProps {
currentUser: User | null;
onLogout: () => void;
cartCount: number; cartCount: number;
onOpenCart: () => void; onOpenCart: () => void;
onOpenCreateCourseModal?: () => void; onOpenCreateCourseModal?: () => void;
@@ -23,6 +26,8 @@ interface NavbarProps {
} }
export const Navbar: React.FC<NavbarProps> = ({ export const Navbar: React.FC<NavbarProps> = ({
currentUser,
onLogout,
cartCount, cartCount,
onOpenCart, onOpenCart,
searchQuery, searchQuery,
@@ -31,6 +36,31 @@ export const Navbar: React.FC<NavbarProps> = ({
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
const [mobileMenuOpen, setMobileMenuOpen] = useState(false); const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const [cartHovered, setCartHovered] = useState(false);
// Inject keyframe animation styles inline (no external CSS needed)
useEffect(() => {
const styleId = 'cart-run-keyframes';
if (!document.getElementById(styleId)) {
const style = document.createElement('style');
style.id = styleId;
style.textContent = `
@keyframes cart-run {
0% { transform: rotate(0deg) translateY(0px); }
25% { transform: rotate(-8deg) translateY(-1px); }
50% { transform: rotate(0deg) translateY(0px); }
75% { transform: rotate(-5deg) translateY(-1px); }
100% { transform: rotate(0deg) translateY(0px); }
}
@keyframes speed-streak {
0% { opacity: 0; transform: translateX(2px) scaleX(0.1); }
40% { opacity: 0.6; transform: translateX(-3px) scaleX(1); }
100% { opacity: 0; transform: translateX(-6px) scaleX(0); }
}
`;
document.head.appendChild(style);
}
}, []);
const currentPath = location.pathname; const currentPath = location.pathname;
const isSellerMode = currentPath === '/seller' || currentPath.startsWith('/seller/'); const isSellerMode = currentPath === '/seller' || currentPath.startsWith('/seller/');
@@ -57,8 +87,7 @@ export const Navbar: React.FC<NavbarProps> = ({
<nav className="hidden lg:flex items-center gap-1"> <nav className="hidden lg:flex items-center gap-1">
<button <button
onClick={() => navigate('/')} onClick={() => navigate('/')}
className={`flex items-center gap-1.5 px-3 py-2 rounded-xl text-xs font-semibold transition-all ${ className={`flex items-center gap-1.5 px-3 py-2 rounded-xl text-xs font-semibold transition-all ${currentPath === '/'
currentPath === '/'
? 'bg-slate-800 text-cyan-400 border border-cyan-500/30' ? 'bg-slate-800 text-cyan-400 border border-cyan-500/30'
: 'text-slate-300 hover:text-white hover:bg-slate-900' : 'text-slate-300 hover:text-white hover:bg-slate-900'
}`} }`}
@@ -69,8 +98,7 @@ export const Navbar: React.FC<NavbarProps> = ({
<button <button
onClick={() => navigate('/courses')} onClick={() => navigate('/courses')}
className={`flex items-center gap-1.5 px-3 py-2 rounded-xl text-xs font-semibold transition-all ${ className={`flex items-center gap-1.5 px-3 py-2 rounded-xl text-xs font-semibold transition-all ${currentPath === '/courses'
currentPath === '/courses'
? 'bg-slate-800 text-cyan-400 border border-cyan-500/30' ? 'bg-slate-800 text-cyan-400 border border-cyan-500/30'
: 'text-slate-300 hover:text-white hover:bg-slate-900' : 'text-slate-300 hover:text-white hover:bg-slate-900'
}`} }`}
@@ -81,8 +109,7 @@ export const Navbar: React.FC<NavbarProps> = ({
<button <button
onClick={() => navigate('/admin')} onClick={() => navigate('/admin')}
className={`flex items-center gap-1.5 px-3 py-2 rounded-xl text-xs font-semibold transition-all ${ className={`flex items-center gap-1.5 px-3 py-2 rounded-xl text-xs font-semibold transition-all ${currentPath === '/admin'
currentPath === '/admin'
? 'bg-slate-800 text-cyan-400 border border-cyan-500/30' ? 'bg-slate-800 text-cyan-400 border border-cyan-500/30'
: 'text-slate-300 hover:text-white hover:bg-slate-900' : 'text-slate-300 hover:text-white hover:bg-slate-900'
}`} }`}
@@ -123,8 +150,7 @@ export const Navbar: React.FC<NavbarProps> = ({
<div className="bg-slate-900 p-1 rounded-xl border border-slate-800 flex items-center shadow-inner"> <div className="bg-slate-900 p-1 rounded-xl border border-slate-800 flex items-center shadow-inner">
<button <button
onClick={() => navigate('/courses')} onClick={() => navigate('/courses')}
className={`flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-semibold transition-all ${ className={`flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-semibold transition-all ${!isSellerMode
!isSellerMode
? 'bg-gradient-to-r from-blue-600 to-cyan-500 text-white shadow-md shadow-cyan-500/20' ? 'bg-gradient-to-r from-blue-600 to-cyan-500 text-white shadow-md shadow-cyan-500/20'
: 'text-slate-400 hover:text-slate-200' : 'text-slate-400 hover:text-slate-200'
}`} }`}
@@ -136,8 +162,7 @@ export const Navbar: React.FC<NavbarProps> = ({
<button <button
onClick={() => navigate('/seller')} onClick={() => navigate('/seller')}
className={`flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-semibold transition-all ${ className={`flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-semibold transition-all ${isSellerMode
isSellerMode
? 'bg-gradient-to-r from-indigo-600 to-violet-500 text-white shadow-md shadow-violet-500/20' ? 'bg-gradient-to-r from-indigo-600 to-violet-500 text-white shadow-md shadow-violet-500/20'
: 'text-slate-400 hover:text-slate-200' : 'text-slate-400 hover:text-slate-200'
}`} }`}
@@ -159,23 +184,8 @@ export const Navbar: React.FC<NavbarProps> = ({
</button> </button>
)} )}
{/* Cart button (Buyer mode) */} {/* Login & Register Buttons OR User Profile */}
{!isSellerMode && ( {!currentUser ? (
<button
onClick={onOpenCart}
className="relative p-2.5 text-slate-300 hover:text-white bg-slate-900 hover:bg-slate-800 border border-slate-700/80 rounded-xl transition"
aria-label="Carrito de compras"
>
<FiShoppingCart className="text-base" />
{cartCount > 0 && (
<span className="absolute -top-1.5 -right-1.5 w-5 h-5 bg-gradient-to-tr from-rose-500 to-pink-500 text-white text-[11px] font-bold rounded-full flex items-center justify-center shadow-md">
{cartCount}
</span>
)}
</button>
)}
{/* Login & Register Buttons */}
<div className="hidden xl:flex items-center gap-2 pl-2 border-l border-slate-800"> <div className="hidden xl:flex items-center gap-2 pl-2 border-l border-slate-800">
<button <button
onClick={() => navigate('/login')} onClick={() => navigate('/login')}
@@ -190,23 +200,127 @@ export const Navbar: React.FC<NavbarProps> = ({
Registrarse Registrarse
</button> </button>
</div> </div>
) : (
{/* User Profile Avatar */} <div className="flex items-center gap-2 pl-2 border-l border-slate-800 relative group">
<div className="flex items-center gap-2 pl-2 border-l border-slate-800"> <div className="relative cursor-pointer flex items-center gap-2" title="Mi Cuenta">
<div <div className="hidden sm:block text-right">
onClick={() => navigate('/login')} <p className="text-[11px] font-bold text-slate-200 leading-none">{currentUser.name}</p>
className="relative cursor-pointer group" <p className="text-[9px] text-cyan-400 font-medium capitalize mt-0.5">{currentUser.role === 'buyer' ? 'Estudiante' : 'Instructor'}</p>
title="Mi Cuenta" </div>
> <div className="relative">
{currentUser.avatar ? (
<img <img
src="https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=100&auto=format&fit=crop&q=80" src={currentUser.avatar}
alt="Usuario" alt={currentUser.name}
className="w-9 h-9 rounded-full object-cover ring-2 ring-indigo-500/50 group-hover:ring-cyan-400 transition" className="w-9 h-9 rounded-full object-cover ring-2 ring-indigo-500/50 group-hover:ring-cyan-400 transition"
/> />
) : (
<div className="w-9 h-9 rounded-full bg-slate-800 flex items-center justify-center text-sm font-bold text-slate-300 ring-2 ring-indigo-500/50 group-hover:ring-cyan-400 transition">
{currentUser.name.charAt(0).toUpperCase()}
</div>
)}
<span className="absolute bottom-0 right-0 w-2.5 h-2.5 bg-emerald-500 ring-2 ring-slate-950 rounded-full"></span> <span className="absolute bottom-0 right-0 w-2.5 h-2.5 bg-emerald-500 ring-2 ring-slate-950 rounded-full"></span>
</div> </div>
</div> </div>
{/* Dropdown Menu */}
<div className="absolute top-full right-0 mt-2 w-48 bg-slate-900 border border-slate-800 rounded-xl shadow-2xl opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 transform origin-top-right scale-95 group-hover:scale-100 z-50">
<div className="p-2 space-y-1">
<button onClick={onLogout} className="w-full flex items-center gap-2 px-3 py-2 text-xs font-semibold text-rose-400 hover:text-rose-300 hover:bg-rose-500/10 rounded-lg transition">
<FaSignOutAlt />
<span>Cerrar Sesión</span>
</button>
</div>
</div>
</div>
)}
{/* Cart button (Buyer mode) - Fixed Far Right, inline animation */}
{!isSellerMode && (
<button
onClick={onOpenCart}
onMouseEnter={() => setCartHovered(true)}
onMouseLeave={() => setCartHovered(false)}
className="group fixed top-4 right-4 z-50 p-3 text-slate-300 hover:text-white hover:bg-slate-800 rounded-xl transition-all shadow-lg shadow-black/30 backdrop-blur-sm overflow-hidden"
aria-label="Carrito de compras"
>
{/* Cart icon with inline running animation */}
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
position: 'relative',
animation: cartHovered ? 'cart-run 0.45s ease-in-out infinite' : 'none',
}}
>
<FiShoppingCart style={{ fontSize: '1.2rem' }} />
{/* Speed streaks — subtle, contained inside icon area */}
<span style={{
position: 'absolute',
top: '25%',
left: '-4px',
width: '5px',
height: '1.5px',
borderRadius: '9999px',
background: 'rgb(34 211 238)',
animation: cartHovered ? 'speed-streak 0.5s linear infinite' : 'none',
opacity: 0,
}} />
<span style={{
position: 'absolute',
top: '55%',
left: '-2px',
width: '4px',
height: '1.5px',
borderRadius: '9999px',
background: 'rgb(34 211 238)',
animation: cartHovered ? 'speed-streak 0.5s linear 0.18s infinite' : 'none',
opacity: 0,
}} />
<span style={{
position: 'absolute',
bottom: '15%',
left: '-5px',
width: '6px',
height: '1.5px',
borderRadius: '9999px',
background: 'rgb(99 102 241)',
animation: cartHovered ? 'speed-streak 0.5s linear 0.35s infinite' : 'none',
opacity: 0,
}} />
</div>
{/* Badge counter */}
{cartCount > 0 && (
<span
style={{
position: 'absolute',
top: '-8px',
right: '-8px',
width: '20px',
height: '20px',
background: 'linear-gradient(135deg, #f43f5e, #ec4899)',
color: 'white',
fontSize: '11px',
fontWeight: 700,
borderRadius: '9999px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
boxShadow: '0 2px 8px rgba(244,63,94,0.5)',
zIndex: 20,
transform: cartHovered ? 'scale(1.2)' : 'scale(1)',
transition: 'transform 0.15s ease',
}}
>
{cartCount}
</span>
)}
</button>
)}
{/* Mobile menu trigger */} {/* Mobile menu trigger */}
<button <button
onClick={() => setMobileMenuOpen(!mobileMenuOpen)} onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
@@ -234,24 +348,21 @@ export const Navbar: React.FC<NavbarProps> = ({
<div className="grid grid-cols-3 gap-2"> <div className="grid grid-cols-3 gap-2">
<button <button
onClick={() => { navigate('/'); setMobileMenuOpen(false); }} onClick={() => { navigate('/'); setMobileMenuOpen(false); }}
className={`flex items-center justify-center gap-1.5 p-2 rounded-lg text-xs font-semibold ${ className={`flex items-center justify-center gap-1.5 p-2 rounded-lg text-xs font-semibold ${currentPath === '/' ? 'bg-blue-600 text-white' : 'bg-slate-800 text-slate-300'
currentPath === '/' ? 'bg-blue-600 text-white' : 'bg-slate-800 text-slate-300'
}`} }`}
> >
<FiHome /> Inicio <FiHome /> Inicio
</button> </button>
<button <button
onClick={() => { navigate('/courses'); setMobileMenuOpen(false); }} onClick={() => { navigate('/courses'); setMobileMenuOpen(false); }}
className={`flex items-center justify-center gap-1.5 p-2 rounded-lg text-xs font-semibold ${ className={`flex items-center justify-center gap-1.5 p-2 rounded-lg text-xs font-semibold ${currentPath === '/courses' ? 'bg-cyan-600 text-white' : 'bg-slate-800 text-slate-300'
currentPath === '/courses' ? 'bg-cyan-600 text-white' : 'bg-slate-800 text-slate-300'
}`} }`}
> >
<FiGrid /> Catálogo <FiGrid /> Catálogo
</button> </button>
<button <button
onClick={() => { navigate('/seller'); setMobileMenuOpen(false); }} onClick={() => { navigate('/seller'); setMobileMenuOpen(false); }}
className={`flex items-center justify-center gap-1.5 p-2 rounded-lg text-xs font-semibold ${ className={`flex items-center justify-center gap-1.5 p-2 rounded-lg text-xs font-semibold ${currentPath === '/seller' ? 'bg-violet-600 text-white' : 'bg-slate-800 text-slate-300'
currentPath === '/seller' ? 'bg-violet-600 text-white' : 'bg-slate-800 text-slate-300'
}`} }`}
> >
<FaChalkboardTeacher /> Vender <FaChalkboardTeacher /> Vender
@@ -259,6 +370,8 @@ export const Navbar: React.FC<NavbarProps> = ({
</div> </div>
<div className="grid grid-cols-2 gap-2 pt-2 border-t border-slate-800"> <div className="grid grid-cols-2 gap-2 pt-2 border-t border-slate-800">
{!currentUser ? (
<>
<button <button
onClick={() => { navigate('/login'); setMobileMenuOpen(false); }} onClick={() => { navigate('/login'); setMobileMenuOpen(false); }}
className="w-full py-2.5 bg-slate-800 text-white font-semibold rounded-lg text-xs flex items-center justify-center gap-2" className="w-full py-2.5 bg-slate-800 text-white font-semibold rounded-lg text-xs flex items-center justify-center gap-2"
@@ -271,6 +384,29 @@ export const Navbar: React.FC<NavbarProps> = ({
> >
Registrarse Registrarse
</button> </button>
</>
) : (
<>
<div className="w-full py-2.5 px-3 bg-slate-800 rounded-lg flex items-center gap-3">
{currentUser.avatar ? (
<img src={currentUser.avatar} alt="Avatar" className="w-6 h-6 rounded-full object-cover" />
) : (
<div className="w-6 h-6 rounded-full bg-slate-700 flex items-center justify-center text-[10px] font-bold text-white">
{currentUser.name.charAt(0).toUpperCase()}
</div>
)}
<div className="text-left">
<p className="text-[10px] font-bold text-white truncate max-w-[100px]">{currentUser.name}</p>
</div>
</div>
<button
onClick={() => { onLogout(); setMobileMenuOpen(false); }}
className="w-full py-2.5 bg-rose-500/10 text-rose-400 font-semibold rounded-lg text-xs flex items-center justify-center gap-2 border border-rose-500/20"
>
<FaSignOutAlt /> Cerrar Sesión
</button>
</>
)}
</div> </div>
{isSellerMode && ( {isSellerMode && (
+72
View File
@@ -25,3 +25,75 @@
::-webkit-scrollbar-thumb:hover { ::-webkit-scrollbar-thumb:hover {
background: #475569; background: #475569;
} }
/* Cart Running Animation */
@keyframes cart-run {
0% {
transform: translateX(0) rotate(0deg) translateY(0);
}
20% {
transform: translateX(3px) rotate(-10deg) translateY(-2px);
}
40% {
transform: translateX(1px) rotate(-5deg) translateY(1px);
}
60% {
transform: translateX(4px) rotate(-12deg) translateY(-2px);
}
80% {
transform: translateX(2px) rotate(-8deg) translateY(0px);
}
100% {
transform: translateX(0) rotate(0deg) translateY(0);
}
}
.animate-cart-run {
animation: cart-run 0.5s ease-in-out infinite;
}
/* Speed Streaks for Cart */
@keyframes speed-streak {
0% {
opacity: 0;
transform: translateX(0) scaleX(0.2);
}
50% {
opacity: 0.8;
transform: translateX(-8px) scaleX(1);
}
100% {
opacity: 0;
transform: translateX(-16px) scaleX(0);
}
}
.speed-line {
position: absolute;
height: 2px;
background: theme('colors.cyan.400');
border-radius: 9999px;
opacity: 0;
pointer-events: none;
}
.group-hover\\:animate-cart-run:hover .speed-line-1 {
animation: speed-streak 0.6s infinite linear;
top: 30%;
right: 110%;
width: 12px;
}
.group-hover\\:animate-cart-run:hover .speed-line-2 {
animation: speed-streak 0.5s infinite linear 0.2s;
top: 60%;
right: 100%;
width: 8px;
}
.group-hover\\:animate-cart-run:hover .speed-line-3 {
animation: speed-streak 0.7s infinite linear 0.4s;
bottom: 10%;
right: 120%;
width: 14px;
}
+8
View File
@@ -1,5 +1,13 @@
export type CourseLevel = 'Principiante' | 'Intermedio' | 'Avanzado' | 'Todos los niveles'; export type CourseLevel = 'Principiante' | 'Intermedio' | 'Avanzado' | 'Todos los niveles';
export interface User {
id: string;
name: string;
email: string;
role: 'buyer' | 'seller' | 'admin';
avatar?: string;
}
export interface Lesson { export interface Lesson {
id: string; id: string;
title: string; title: string;
+19 -11
View File
@@ -3,22 +3,36 @@ import { useNavigate, Link } from 'react-router';
import { BrandLogo } from '../components/common/BrandLogo'; import { BrandLogo } from '../components/common/BrandLogo';
import { FiMail, FiLock, FiArrowRight } from 'react-icons/fi'; import { FiMail, FiLock, FiArrowRight } from 'react-icons/fi';
import { FaGoogle, FaGithub } from 'react-icons/fa'; import { FaGoogle, FaGithub } from 'react-icons/fa';
import type { User } from '../types/course';
interface LoginViewProps { interface LoginViewProps {
onShowToast: (msg: string) => void; onShowToast: (msg: string) => void;
onLogin: (user: User) => void;
} }
export const LoginView: React.FC<LoginViewProps> = ({ onShowToast }) => { export const LoginView: React.FC<LoginViewProps> = ({ onShowToast, onLogin }) => {
const navigate = useNavigate(); const navigate = useNavigate();
const [email, setEmail] = useState(''); const [email, setEmail] = useState('');
const [password, setPassword] = useState(''); const [password, setPassword] = useState('');
const [rememberMe, setRememberMe] = useState(true); const [rememberMe, setRememberMe] = useState(true);
const handleMockLogin = (name: string, role: 'buyer' | 'seller', avatarUrl?: string) => {
const mockUser: User = {
id: Math.random().toString(36).substr(2, 9),
name,
email: email || '[email protected]',
role,
avatar: avatarUrl,
};
onLogin(mockUser);
onShowToast(`¡Bienvenido de nuevo a RyCourses, ${name}!`);
navigate('/');
};
const handleSubmit = (e: React.FormEvent) => { const handleSubmit = (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();
if (email && password) { if (email && password) {
onShowToast('¡Bienvenido de nuevo a RyCourses!'); handleMockLogin('Usuario Demo', 'buyer', 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=100&auto=format&fit=crop&q=80');
navigate('/');
} }
}; };
@@ -47,10 +61,7 @@ export const LoginView: React.FC<LoginViewProps> = ({ onShowToast }) => {
<div className="grid grid-cols-2 gap-3 pt-2"> <div className="grid grid-cols-2 gap-3 pt-2">
<button <button
type="button" type="button"
onClick={() => { onClick={() => handleMockLogin('Google User', 'buyer')}
onShowToast('Iniciando sesión con Google...');
navigate('/');
}}
className="flex items-center justify-center gap-2 py-2.5 px-4 rounded-xl bg-slate-800 hover:bg-slate-700/80 border border-slate-700 text-xs font-semibold text-slate-200 transition" className="flex items-center justify-center gap-2 py-2.5 px-4 rounded-xl bg-slate-800 hover:bg-slate-700/80 border border-slate-700 text-xs font-semibold text-slate-200 transition"
> >
<FaGoogle className="text-red-400" /> <FaGoogle className="text-red-400" />
@@ -59,10 +70,7 @@ export const LoginView: React.FC<LoginViewProps> = ({ onShowToast }) => {
<button <button
type="button" type="button"
onClick={() => { onClick={() => handleMockLogin('GitHub User', 'buyer')}
onShowToast('Iniciando sesión con GitHub...');
navigate('/');
}}
className="flex items-center justify-center gap-2 py-2.5 px-4 rounded-xl bg-slate-800 hover:bg-slate-700/80 border border-slate-700 text-xs font-semibold text-slate-200 transition" className="flex items-center justify-center gap-2 py-2.5 px-4 rounded-xl bg-slate-800 hover:bg-slate-700/80 border border-slate-700 text-xs font-semibold text-slate-200 transition"
> >
<FaGithub className="text-white" /> <FaGithub className="text-white" />
+10 -1
View File
@@ -3,12 +3,14 @@ import { useNavigate, Link } from 'react-router';
import { BrandLogo } from '../components/common/BrandLogo'; import { BrandLogo } from '../components/common/BrandLogo';
import { FiMail, FiLock, FiUser, FiArrowRight, FiCheckCircle } from 'react-icons/fi'; import { FiMail, FiLock, FiUser, FiArrowRight, FiCheckCircle } from 'react-icons/fi';
import { FaGraduationCap, FaChalkboardTeacher } from 'react-icons/fa'; import { FaGraduationCap, FaChalkboardTeacher } from 'react-icons/fa';
import type { User } from '../types/course';
interface RegisterViewProps { interface RegisterViewProps {
onShowToast: (msg: string) => void; onShowToast: (msg: string) => void;
onLogin: (user: User) => void;
} }
export const RegisterView: React.FC<RegisterViewProps> = ({ onShowToast }) => { export const RegisterView: React.FC<RegisterViewProps> = ({ onShowToast, onLogin }) => {
const navigate = useNavigate(); const navigate = useNavigate();
const [name, setName] = useState(''); const [name, setName] = useState('');
const [email, setEmail] = useState(''); const [email, setEmail] = useState('');
@@ -18,6 +20,13 @@ export const RegisterView: React.FC<RegisterViewProps> = ({ onShowToast }) => {
const handleSubmit = (e: React.FormEvent) => { const handleSubmit = (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();
if (name && email && password) { if (name && email && password) {
const newUser: User = {
id: Math.random().toString(36).substr(2, 9),
name,
email,
role: accountType,
};
onLogin(newUser);
onShowToast(`¡Cuenta de ${accountType === 'buyer' ? 'Estudiante' : 'Instructor'} creada con éxito!`); onShowToast(`¡Cuenta de ${accountType === 'buyer' ? 'Estudiante' : 'Instructor'} creada con éxito!`);
navigate(accountType === 'seller' ? '/seller' : '/courses'); navigate(accountType === 'seller' ? '/seller' : '/courses');
} }