Files
MotoresArgentinosV2/Frontend/src/pages/PerfilPage.tsx

285 lines
12 KiB
TypeScript
Raw Normal View History

import { useState, useEffect } from "react";
import { ProfileService } from "../services/profile.service";
import { useAuth } from "../context/AuthContext";
import { AuthService } from "../services/auth.service";
2026-01-29 13:43:44 -03:00
export default function PerfilPage() {
const { user, refreshSession } = useAuth();
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
const [showEmailModal, setShowEmailModal] = useState(false);
const [newEmail, setNewEmail] = useState("");
const [authCode, setAuthCode] = useState(""); // Código Google Authenticator
const [loadingEmail, setLoadingEmail] = useState(false);
2026-01-29 13:43:44 -03:00
const [formData, setFormData] = useState({
firstName: "",
lastName: "",
phoneNumber: "",
2026-01-29 13:43:44 -03:00
});
useEffect(() => {
loadProfile();
}, []);
const loadProfile = async () => {
try {
const data = await ProfileService.getProfile();
setFormData({
firstName: data.firstName || "",
lastName: data.lastName || "",
phoneNumber: data.phoneNumber || "",
2026-01-29 13:43:44 -03:00
});
} catch (err) {
console.error("Error loading profile", err);
} finally {
setLoading(false);
}
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setSaving(true);
try {
await ProfileService.updateProfile(formData);
alert("Perfil actualizado con éxito");
2026-01-29 13:43:44 -03:00
if (refreshSession) refreshSession();
} catch (err) {
alert("Error al actualizar el perfil");
2026-01-29 13:43:44 -03:00
} finally {
setSaving(false);
}
};
if (loading) {
return (
<div className="flex justify-center p-40">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500"></div>
</div>
);
}
return (
<div className="container mx-auto px-6 py-12 max-w-4xl">
<div className="mb-12">
<h1 className="text-5xl font-black tracking-tighter uppercase mb-2">
Mi <span className="text-blue-500">Perfil</span>
</h1>
<p className="text-gray-500 font-bold tracking-widest uppercase text-xs">
Gestiona tu información personal
</p>
2026-01-29 13:43:44 -03:00
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
{/* Sidebar Info */}
<div className="lg:col-span-1 space-y-6">
<div className="glass p-8 rounded-[2rem] border border-white/5 text-center">
<div className="w-24 h-24 bg-blue-600/20 rounded-full flex items-center justify-center text-4xl text-blue-400 font-bold mx-auto mb-4 border border-blue-500/20 shadow-lg shadow-blue-500/10">
{user?.username?.[0].toUpperCase()}
</div>
<h2 className="text-xl font-black text-white uppercase tracking-tight">
{user?.username}
</h2>
<p className="text-xs text-gray-500 font-medium mb-6">
{user?.email}
</p>
2026-01-29 13:43:44 -03:00
<div className="flex flex-col gap-2">
<span
className={`px-4 py-1.5 rounded-full text-[10px] font-black uppercase tracking-widest ${user?.userType === 3 ? "bg-amber-500/10 text-amber-500 border border-amber-500/20" : "bg-blue-600/10 text-blue-400 border border-blue-600/20"}`}
>
{user?.userType === 3
? "🛡️ Administrador"
: "👤 Usuario Particular"}
2026-01-29 13:43:44 -03:00
</span>
</div>
</div>
</div>
{/* Edit Form */}
<div className="lg:col-span-2">
<form
onSubmit={handleSubmit}
className="glass p-8 rounded-[2.5rem] border border-white/5 space-y-8"
>
2026-01-29 13:43:44 -03:00
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="space-y-2">
<label className="text-[10px] font-black uppercase tracking-widest text-gray-500 ml-1">
Nombre
</label>
2026-01-29 13:43:44 -03:00
<input
type="text"
value={formData.firstName}
onChange={(e) =>
setFormData({ ...formData, firstName: e.target.value })
}
2026-01-29 13:43:44 -03:00
className="w-full bg-white/5 border border-white/10 rounded-2xl px-5 py-4 text-sm text-white outline-none focus:border-blue-500 transition-all font-medium"
placeholder="Tu nombre"
/>
</div>
<div className="space-y-2">
<label className="text-[10px] font-black uppercase tracking-widest text-gray-500 ml-1">
Apellido
</label>
2026-01-29 13:43:44 -03:00
<input
type="text"
value={formData.lastName}
onChange={(e) =>
setFormData({ ...formData, lastName: e.target.value })
}
2026-01-29 13:43:44 -03:00
className="w-full bg-white/5 border border-white/10 rounded-2xl px-5 py-4 text-sm text-white outline-none focus:border-blue-500 transition-all font-medium"
placeholder="Tu apellido"
/>
</div>
<div className="space-y-2 md:col-span-2">
<label className="text-[10px] font-black uppercase tracking-widest text-gray-500 ml-1">
Teléfono de Contacto
</label>
2026-01-29 13:43:44 -03:00
<input
type="text"
value={formData.phoneNumber}
onChange={(e) =>
setFormData({ ...formData, phoneNumber: e.target.value })
}
2026-01-29 13:43:44 -03:00
className="w-full bg-white/5 border border-white/10 rounded-2xl px-5 py-4 text-sm text-white outline-none focus:border-blue-500 transition-all font-medium"
placeholder="Ej: +54 9 11 1234 5678"
/>
</div>
<div className="space-y-2 md:col-span-2">
<label className="text-[10px] font-black uppercase tracking-widest text-gray-500 ml-1">
Email
</label>
<div className="flex gap-2">
<input
type="email"
value={user?.email}
disabled
className="flex-1 bg-white/5 border border-white/10 rounded-2xl px-5 py-4 text-sm text-gray-500 outline-none cursor-not-allowed font-medium"
/>
<button
type="button"
onClick={() => {
if (!user?.isMFAEnabled) {
alert(
"⚠️ Acción restringida.\n\nPara cambiar tu email, primero debes activar la Autenticación de Dos Factores (2FA) en la sección de Seguridad.",
);
return;
}
setShowEmailModal(true);
}}
className="bg-white/5 hover:bg-blue-600 hover:text-white border border-white/10 text-gray-300 px-6 rounded-2xl text-[10px] font-black uppercase tracking-widest transition-all"
>
Cambiar
</button>
</div>
2026-01-29 13:43:44 -03:00
</div>
</div>
<div className="pt-6 border-t border-white/5">
<button
type="submit"
disabled={saving}
className="w-full md:w-auto bg-blue-600 hover:bg-blue-500 text-white py-4 px-12 rounded-2xl text-[10px] font-black uppercase tracking-widest transition-all shadow-lg shadow-blue-600/20 active:scale-95 disabled:opacity-50"
>
{saving ? "Guardando..." : "Guardar Cambios"}
2026-01-29 13:43:44 -03:00
</button>
</div>
</form>
</div>
</div>
{showEmailModal && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-md p-4 animate-fade-in">
<div className="glass p-8 rounded-[2.5rem] border border-white/10 max-w-md w-full relative shadow-2xl animate-scale-up">
<button
onClick={() => setShowEmailModal(false)}
className="absolute top-6 right-6 text-gray-500 hover:text-white font-bold"
>
</button>
<div className="text-center mb-6">
<div className="w-16 h-16 bg-blue-600/10 rounded-2xl flex items-center justify-center text-2xl mx-auto mb-4 border border-blue-500/20 text-blue-400">
📧
</div>
<h3 className="text-2xl font-black text-white uppercase tracking-tighter">
Cambiar Email
</h3>
<p className="text-xs text-gray-400 mt-2 font-medium">
Ingresa tu nueva dirección y valida con tu autenticador.
</p>
</div>
<div className="space-y-5">
<div>
<label className="text-[9px] font-black uppercase tracking-widest text-gray-500 block mb-2 ml-1">
Nuevo Correo Electrónico
</label>
<input
type="email"
placeholder="nuevo@email.com"
value={newEmail}
onChange={(e) => setNewEmail(e.target.value)}
className="w-full bg-black/40 border border-white/10 rounded-xl px-4 py-3 text-white text-sm outline-none focus:border-blue-500 transition-all placeholder:text-gray-700"
/>
</div>
<div>
<label className="text-[9px] font-black uppercase tracking-widest text-gray-500 block mb-2 ml-1">
Código Authenticator (2FA)
</label>
<input
type="text"
maxLength={6}
placeholder="000 000"
value={authCode}
onChange={(e) =>
setAuthCode(e.target.value.replace(/\D/g, ""))
}
className="w-full bg-black/40 border border-white/10 rounded-xl px-4 py-3 text-white text-center font-mono text-xl tracking-[0.3em] outline-none focus:border-blue-500 transition-all placeholder:text-gray-700 placeholder:tracking-normal"
/>
</div>
<button
onClick={async () => {
if (newEmail === user?.email) {
alert("El nuevo email debe ser diferente al actual.");
return;
}
try {
setLoadingEmail(true);
const res = await AuthService.initiateEmailChange(
newEmail,
authCode,
);
alert(res.message); // "Se ha enviado un enlace..."
setShowEmailModal(false);
setNewEmail("");
setAuthCode("");
} catch (err: any) {
alert(
err.response?.data?.message ||
"Error al solicitar cambio.",
);
} finally {
setLoadingEmail(false);
}
}}
disabled={
loadingEmail || newEmail.length < 5 || authCode.length < 6
}
className="w-full bg-blue-600 hover:bg-blue-500 text-white py-4 rounded-xl font-bold uppercase tracking-widest text-xs shadow-lg shadow-blue-600/20 transition-all disabled:opacity-50 disabled:cursor-not-allowed hover:scale-[1.02]"
>
{loadingEmail ? "Procesando..." : "Enviar Confirmación"}
</button>
</div>
</div>
</div>
)}
2026-01-29 13:43:44 -03:00
</div>
);
}