Skip to content

Commit 460f154

Browse files
authored
Merge pull request #20 from Frasquito3/refactor/addapt-styles-from-login-register
ref:change-styles-from-login-and-register
2 parents 0727a9b + 2dfb698 commit 460f154

6 files changed

Lines changed: 226 additions & 146 deletions

File tree

src/components/layouts/AuthCard.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,19 @@ import React from 'react';
33
interface AuthCardProps {
44
title: string;
55
description: string;
6-
children: React.ReactNode;
6+
children: React.ReactNode;
77
}
88

99
const AuthCard = ({ title, description, children }: AuthCardProps) => {
1010
return (
11-
<div className="bg-white/95 backdrop-blur-sm border border-white/20 rounded-2xl p-8 shadow-strong w-full max-w-md animate-fadeIn">
12-
<div className="text-center mb-8">
13-
<h1 className="text-neutral-900 text-3xl font-bold mb-2">{title}</h1>
14-
<p className="text-neutral-600">{description}</p>
11+
<div className="bg-white/90 backdrop-blur-sm rounded-2xl p-12 shadow-2xl shadow-slate-black/50 w-full max-w-xl">
12+
<div className="text-center mb-10">
13+
<h1 className="text-slate-900 text-3xl font-bold">{title}</h1>
14+
<p className="text-slate-500 text-base mt-3">{description}</p>
1515
</div>
1616
{children}
1717
</div>
1818
);
1919
};
2020

21-
export default AuthCard;
21+
export default AuthCard;
Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,29 @@
11
import { Outlet, Link } from 'react-router-dom';
2+
import { BookOpen, ArrowLeft } from 'lucide-react';
23

34
const AuthLayout = () => {
45
return (
5-
<div className="min-h-screen w-full flex items-center justify-center p-4 bg-gradient-to-br from-purple-400 via-pink-500 to-red-500">
6-
<Link
7-
to="/"
8-
className="absolute top-8 left-8 text-primary-100 no-underline font-semibold hover:text-white transition-colors"
9-
>
10-
Home
11-
</Link>
12-
13-
<main>
6+
<div className="min-h-screen w-full flex items-center justify-center p-4 bg-gradient-to-br from-blue-50 via-white to-green-50">
7+
<div className="absolute top-6 left-6">
8+
<Link
9+
to="/"
10+
className="flex items-center gap-4 text-slate-800 hover:text-blue-600 transition-colors group"
11+
>
12+
<ArrowLeft className="w-6 h-6" />
13+
<div className="flex items-center gap-2">
14+
<div className="w-8 h-8 bg-gradient-to-br from-blue-400 to-green-400 rounded-lg flex items-center justify-center shadow-md group-hover:scale-105 transition-transform">
15+
<BookOpen className="w-5 h-5 text-white" />
16+
</div>
17+
<span className="text-xl font-bold">UpSkill</span>
18+
</div>
19+
</Link>
20+
</div>
21+
22+
<main className="w-full flex justify-center">
1423
<Outlet />
1524
</main>
1625
</div>
1726
);
1827
};
1928

20-
export default AuthLayout;
29+
export default AuthLayout;

src/components/ui/Button.tsx

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,45 @@
11
import React from 'react';
22

3+
type ButtonVariant = 'primary' | 'outline';
4+
35
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
4-
children: React.ReactNode;
5-
isLoading?: boolean;
6+
children: React.ReactNode;
7+
isLoading?: boolean;
8+
variant?: ButtonVariant;
69
}
710

8-
const Button = ({ children, isLoading = false, ...props }: ButtonProps) => {
9-
const baseClasses = "w-full inline-flex items-center justify-center py-3 px-6 rounded-lg font-medium text-sm text-white bg-gradient-to-r from-primary-600 to-primary-700 shadow-medium hover:from-primary-700 hover:to-primary-800 hover:scale-[1.02] hover:shadow-strong transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed disabled:transform-none";
11+
const Button = ({
12+
children,
13+
isLoading = false,
14+
variant = 'primary',
15+
className,
16+
...props
17+
}: ButtonProps) => {
18+
const baseClasses = `
19+
w-full inline-flex items-center justify-center py-2.5 px-4 rounded-lg font-medium
20+
text-sm transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2
21+
disabled:opacity-50 disabled:cursor-not-allowed
22+
`;
23+
24+
const variantClasses = {
25+
primary: 'bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500',
26+
outline:
27+
'bg-transparent border border-slate-300 text-slate-700 hover:bg-slate-50 focus:ring-slate-400',
28+
};
1029

1130
return (
1231
<button
13-
className={baseClasses}
14-
disabled={isLoading}
15-
{...props}
32+
className={`${baseClasses} ${variantClasses[variant]} ${className || ''}`}
33+
disabled={isLoading || props.disabled}
34+
{...props}
1635
>
1736
{isLoading ? (
18-
<span className="animate-spin h-5 w-5 border-2 border-transparent border-t-white rounded-full"></span>
37+
<span className="animate-spin h-5 w-5 border-2 border-transparent border-t-current rounded-full"></span>
1938
) : (
2039
children
2140
)}
2241
</button>
2342
);
2443
};
2544

26-
export default Button;
45+
export default Button;

src/components/ui/Input.tsx

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,62 @@
1-
import React, { useState } from 'react';
2-
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
3-
import { faEye, faEyeSlash } from '@fortawesome/free-solid-svg-icons';
1+
import React from 'react';
2+
import { Eye, EyeOff } from 'lucide-react';
43

54
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
65
label: string;
7-
icon?: React.ReactNode;
8-
error?: string | null;
6+
icon?: React.ReactNode;
7+
error?: string | null;
98
}
109

1110
const Input = ({ label, id, type, icon, error, ...props }: InputProps) => {
12-
const [showPassword, setShowPassword] = useState(false);
13-
const isPassword = type === 'password';
14-
15-
const inputType = isPassword ? (showPassword ? 'text' : 'password') : type;
16-
17-
const inputClasses = `w-full ${icon ? 'pl-10' : 'pl-4'} ${isPassword ? 'pr-10' : 'pr-4'} py-3 border rounded-lg text-base bg-white focus:outline-none focus:border-primary-500 focus:ring-2 focus:ring-primary-500/20 transition-all`;
11+
const [isPasswordVisible, setPasswordVisible] = React.useState(false);
12+
const isPasswordInput = type === 'password';
13+
const inputType = isPasswordInput
14+
? isPasswordVisible
15+
? 'text'
16+
: 'password'
17+
: type;
18+
19+
const inputClasses = `
20+
w-full py-3 border rounded-lg transition-all
21+
bg-slate-100/70 border-transparent
22+
focus:outline-none focus:ring-2 focus:ring-blue-500/50 focus:border-blue-500 focus:bg-white
23+
${icon ? 'pl-10' : 'pl-4'}
24+
${isPasswordInput ? 'pr-10' : 'pr-4'}
25+
${error ? 'border-red-500 ring-red-200' : ''}
26+
`;
1827

1928
return (
2029
<div className="flex flex-col gap-2">
21-
<label htmlFor={id} className="text-sm font-medium text-neutral-700">
30+
<label htmlFor={id} className="text-sm font-medium text-slate-700">
2231
{label}
2332
</label>
2433
<div className="relative">
2534
{icon && (
26-
<span className="absolute left-3 top-1/2 -translate-y-1/2 text-neutral-400 pointer-events-none">
35+
<span className="absolute left-3 top-1/2 -translate-y-1/2 text-slate-400 pointer-events-none">
2736
{icon}
2837
</span>
2938
)}
30-
31-
<input
32-
id={id}
33-
type={inputType}
34-
className={`${inputClasses} ${error ? 'border-error' : 'border-neutral-300'}`}
35-
{...props}
36-
/>
37-
38-
{isPassword && (
39+
<input id={id} type={inputType} className={inputClasses} {...props} />
40+
{isPasswordInput && (
3941
<button
4042
type="button"
41-
onClick={() => setShowPassword(!showPassword)}
42-
className="absolute right-3 top-1/2 -translate-y-1/2 text-neutral-400 hover:text-neutral-600"
43-
aria-label={showPassword ? "Hide password" : "Show password"}
43+
onClick={() => setPasswordVisible(!isPasswordVisible)}
44+
className="absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 hover:text-slate-600"
45+
aria-label={
46+
isPasswordVisible ? 'Ocultar contraseña' : 'Mostrar contraseña'
47+
}
4448
>
45-
<FontAwesomeIcon icon={showPassword ? faEyeSlash : faEye} />
49+
{isPasswordVisible ? (
50+
<EyeOff className="h-5 w-5" />
51+
) : (
52+
<Eye className="h-5 w-5" />
53+
)}
4654
</button>
4755
)}
4856
</div>
49-
{error && <p className="text-sm text-error mt-1">{error}</p>}
57+
{error && <p className="text-sm text-red-500 mt-1">{error}</p>}
5058
</div>
5159
);
5260
};
5361

54-
export default Input;
62+
export default Input;

src/pages/Auth/LoginPage.tsx

Lines changed: 58 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,36 @@
11
import { useState } from 'react';
22
import { Link, useNavigate } from 'react-router-dom';
3+
import type React from 'react';
4+
import { Mail, Lock } from 'lucide-react';
35
import { useAuth } from '../../hooks/useAuth';
46
import { authService } from '../../api/services/auth.service';
5-
import type React from 'react';
6-
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
7-
import { faEnvelope, faLock } from '@fortawesome/free-solid-svg-icons';
8-
import Button from '../../components/ui/Button.tsx';
9-
import Input from '../../components/ui/Input.tsx';
10-
import AuthCard from '../../components/layouts/AuthCard.tsx';
7+
import Button from '../../components/ui/Button';
8+
import Input from '../../components/ui/Input';
9+
import AuthCard from '../../components/layouts/AuthCard';
1110

1211
const LoginPage = () => {
1312
const [credentials, setCredentials] = useState({ mail: '', password: '' });
1413
const [error, setError] = useState<string | null>(null);
1514
const [isLoading, setIsLoading] = useState(false);
1615
const navigate = useNavigate();
1716
const auth = useAuth();
18-
1917
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
2018
setCredentials((prev) => ({ ...prev, [e.target.name]: e.target.value }));
2119
};
22-
2320
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
2421
e.preventDefault();
2522
setIsLoading(true);
2623
setError(null);
27-
2824
try {
29-
const payload = { mail: credentials.mail, password_plaintext: credentials.password };
25+
const payload = {
26+
mail: credentials.mail,
27+
password_plaintext: credentials.password,
28+
};
3029
const { user, token } = await authService.login(payload);
3130
auth.login(user, token);
3231
navigate('/');
3332
} catch (err) {
34-
setError('Incorrect credentials. Please try again.');
33+
setError('Credenciales incorrectas. Por favor, inténtalo de nuevo.');
3534
console.error(err);
3635
} finally {
3736
setIsLoading(false);
@@ -40,61 +39,78 @@ const LoginPage = () => {
4039

4140
return (
4241
<AuthCard
43-
title='Welcome'
44-
description='Please enter your credentials to continue'
42+
title="Iniciar Sesión"
43+
description="Accede a tu cuenta para continuar aprendiendo"
4544
>
46-
47-
<form onSubmit={handleSubmit} className="flex flex-col gap-6">
45+
<form onSubmit={handleSubmit} className="space-y-6">
4846
<Input
4947
id="mail"
50-
label="Email"
48+
label="Correo electrónico"
5149
name="mail"
52-
type="mail"
53-
placeholder="your@email.com"
50+
type="email"
51+
placeholder="tu@email.com"
5452
value={credentials.mail}
5553
onChange={handleChange}
56-
icon={<FontAwesomeIcon icon={faEnvelope} />}
57-
error={error && error.includes('mail') ? error : null}
54+
icon={<Mail className="h-5 w-5" />}
5855
required
56+
autoComplete="email"
5957
/>
60-
6158
<Input
6259
id="password"
63-
label="Password"
60+
label="Contraseña"
6461
name="password"
6562
type="password"
66-
placeholder="••••••••"
63+
placeholder="Tu contraseña"
6764
value={credentials.password}
6865
onChange={handleChange}
69-
icon={<FontAwesomeIcon icon={faLock} />}
66+
icon={<Lock className="h-5 w-5" />}
7067
required
68+
autoComplete="current-password"
7169
/>
72-
73-
<div className="flex items-center justify-between mt-2">
70+
<div className="flex items-center justify-between text-sm pt-1">
7471
<div className="flex items-center gap-2">
75-
<input id="remember" name="remember" type="checkbox" className="h-4 w-4 rounded accent-primary-600" />
76-
<label htmlFor="remember" className="text-sm text-neutral-600 font-bold cursor-pointer">Remember me</label>
72+
<input
73+
id="remember"
74+
name="remember"
75+
type="checkbox"
76+
className="h-4 w-4 rounded border-slate-300 text-blue-500 focus:ring-blue-500"
77+
/>
78+
<label htmlFor="remember" className="text-slate-600 cursor-pointer">
79+
Recordarme
80+
</label>
7781
</div>
78-
<a href="#" className="text-sm text-primary-600 hover:text-primary-800 hover:underline font-medium">
79-
Forgot your password?
80-
</a>
82+
<Link
83+
to="/forgot-password"
84+
className="font-medium text-blue-500 hover:underline"
85+
>
86+
¿Olvidaste tu contraseña?
87+
</Link>
8188
</div>
8289

83-
{error && <p className="text-sm text-error mt-1">{error}</p>}
90+
{error && <p className="text-sm text-red-500 text-center">{error}</p>}
91+
92+
<div className="pt-6">
93+
<Button
94+
type="submit"
95+
isLoading={isLoading}
96+
className="w-full text-base py-3"
97+
>
98+
Iniciar Sesión
99+
</Button>
100+
</div>
84101

85-
<Button type="submit" isLoading={isLoading}>
86-
Login
87-
</Button>
88-
89-
<div className="text-center mt-2">
90-
<p className="text-neutral-600">
91-
You don't have an account?
92-
<Link to="/register" className="text-primary-600 hover:text-primary-800 font-medium ml-2 hover:underline">Create one</Link>
93-
</p>
102+
<div className="text-center text-sm text-slate-500 pt-6">
103+
¿No tienes una cuenta?{' '}
104+
<Link
105+
to="/register"
106+
className="font-semibold text-blue-500 hover:underline"
107+
>
108+
Regístrate aquí
109+
</Link>
94110
</div>
95111
</form>
96112
</AuthCard>
97113
);
98114
};
99115

100-
export default LoginPage;
116+
export default LoginPage;

0 commit comments

Comments
 (0)