Some checks failed
continuous-integration/drone/push Build was killed
Replaced the username field with an email field in the profile form. Updated form logic, validation messages, and input placeholders accordingly. Adjusted UI labels and input types to reflect the change.
255 lines
8.3 KiB
JavaScript
255 lines
8.3 KiB
JavaScript
import React, { useEffect, useMemo, useState } from 'react';
|
|
import '../../styles/components.css';
|
|
|
|
const ProfileRow = ({ label, value, icon }) => (
|
|
<div className="form-row">
|
|
<div className="form-group" style={{ flex: 1 }}>
|
|
<label style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
|
{icon && <i className={icon} aria-hidden="true"></i>}
|
|
{label}
|
|
</label>
|
|
<div className="input" style={{ background: 'var(--bg-elevated)', border: '1px solid var(--border)', color: 'var(--text)' }}>
|
|
{value}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
const storageKey = 'user.profile.v1';
|
|
|
|
const loadProfile = () => {
|
|
try {
|
|
const raw = localStorage.getItem(storageKey);
|
|
if (!raw) return null;
|
|
return JSON.parse(raw);
|
|
} catch (_e) {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
const saveProfile = (profile) => {
|
|
try {
|
|
localStorage.setItem(storageKey, JSON.stringify(profile));
|
|
} catch (_e) {}
|
|
};
|
|
|
|
const computeInitials = (firstName = '', lastName = '') => {
|
|
const a = (firstName || '').trim().charAt(0).toUpperCase();
|
|
const b = (lastName || '').trim().charAt(0).toUpperCase();
|
|
return `${a}${b || ''}` || 'U';
|
|
};
|
|
|
|
const ProfilePage = () => {
|
|
const defaultProfile = useMemo(() => ({
|
|
username: 'jperez',
|
|
firstName: 'Juan',
|
|
lastName: 'Pérez',
|
|
email: 'juan.perez@empresa.com',
|
|
role: 'Administrador',
|
|
}), []);
|
|
|
|
const [profile, setProfile] = useState(() => loadProfile() || defaultProfile);
|
|
const [showModal, setShowModal] = useState(false);
|
|
|
|
useEffect(() => {
|
|
saveProfile(profile);
|
|
}, [profile]);
|
|
|
|
const initials = computeInitials(profile.firstName, profile.lastName);
|
|
|
|
const [form, setForm] = useState({
|
|
email: profile.email,
|
|
firstName: profile.firstName,
|
|
lastName: profile.lastName,
|
|
password: '',
|
|
});
|
|
|
|
useEffect(() => {
|
|
if (showModal) {
|
|
setForm({
|
|
email: profile.email,
|
|
firstName: profile.firstName,
|
|
lastName: profile.lastName,
|
|
password: '',
|
|
});
|
|
}
|
|
}, [showModal, profile]);
|
|
|
|
const openModal = () => setShowModal(true);
|
|
const closeModal = () => setShowModal(false);
|
|
|
|
const handleChange = (e) => {
|
|
const { name, value } = e.target;
|
|
setForm((prev) => ({ ...prev, [name]: value }));
|
|
};
|
|
|
|
const handleSave = (e) => {
|
|
e.preventDefault();
|
|
|
|
// Validaciones básicas
|
|
if (!form.email.trim() || !form.firstName.trim() || !form.lastName.trim()) {
|
|
alert('Por favor, completa los campos: correo, nombre y apellido.');
|
|
return;
|
|
}
|
|
|
|
if (form.password && form.password.length < 6) {
|
|
alert('La contraseña debe tener al menos 6 caracteres.');
|
|
return;
|
|
}
|
|
|
|
// En una app real, aquí llamarías a la API para actualizar perfil y contraseña.
|
|
setProfile((prev) => ({
|
|
...prev,
|
|
email: form.email.trim(),
|
|
firstName: form.firstName.trim(),
|
|
lastName: form.lastName.trim(),
|
|
// La contraseña NO se guarda en localStorage; solo se simula el flujo.
|
|
}));
|
|
|
|
if (form.password) {
|
|
// Simulación de actualización de contraseña
|
|
console.log('Contraseña actualizada (simulado).');
|
|
}
|
|
|
|
setShowModal(false);
|
|
};
|
|
|
|
return (
|
|
<section className="content-wrapper">
|
|
<div className="card" style={{ marginBottom: 24 }}>
|
|
<div className="form-header">
|
|
<h3>
|
|
<i className="fas fa-user" style={{ marginRight: 8 }}></i>
|
|
Perfil de usuario
|
|
</h3>
|
|
<div>
|
|
<button className="btn btn-primary" onClick={openModal}>
|
|
<i className="fas fa-edit"></i> Actualizar
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
|
|
<div className="user-avatar" aria-hidden="true">{initials}</div>
|
|
<div>
|
|
<div className="user-name" style={{ margin: 0 }}>{profile.firstName} {profile.lastName}</div>
|
|
<div className="user-role" style={{ margin: 0 }}>{profile.role}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="card">
|
|
<div className="form-header">
|
|
<h3>
|
|
<i className="fas fa-id-card" style={{ marginRight: 8 }}></i>
|
|
Información
|
|
</h3>
|
|
</div>
|
|
<div>
|
|
<ProfileRow label="Usuario" value={profile.username} icon="fas fa-user-circle" />
|
|
<ProfileRow label="Nombre" value={`${profile.firstName} ${profile.lastName}`} icon="fas fa-user" />
|
|
<ProfileRow label="Correo" value={profile.email} icon="fas fa-envelope" />
|
|
<ProfileRow label="Rol" value={profile.role} icon="fas fa-user-shield" />
|
|
</div>
|
|
</div>
|
|
|
|
{showModal && (
|
|
<div className="modal-overlay" onClick={closeModal}>
|
|
<div className="modal" onClick={(e) => e.stopPropagation()}>
|
|
<div className="modal-header">
|
|
<h3>
|
|
<i className="fas fa-user-cog"></i>
|
|
Actualizar perfil
|
|
</h3>
|
|
<button className="btn btn-outline btn-sm" onClick={closeModal}>
|
|
<i className="fas fa-times"></i> Cerrar
|
|
</button>
|
|
</div>
|
|
<div className="modal-body">
|
|
<form className="form-container" onSubmit={handleSave}>
|
|
<div className="form-row">
|
|
<div className="form-group" style={{ flex: 1 }}>
|
|
<label>
|
|
<i className="fas fa-envelope" aria-hidden="true"></i> Correo electrónico
|
|
</label>
|
|
<input
|
|
className="input"
|
|
type="email"
|
|
name="email"
|
|
value={form.email}
|
|
onChange={handleChange}
|
|
placeholder="correo@ejemplo.com"
|
|
required
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="form-row">
|
|
<div className="form-group" style={{ flex: 1 }}>
|
|
<label>
|
|
<i className="fas fa-user" aria-hidden="true"></i> Nombre
|
|
</label>
|
|
<input
|
|
className="input"
|
|
type="text"
|
|
name="firstName"
|
|
value={form.firstName}
|
|
onChange={handleChange}
|
|
placeholder="Nombre"
|
|
required
|
|
/>
|
|
</div>
|
|
<div className="form-group" style={{ flex: 1 }}>
|
|
<label>
|
|
<i className="fas fa-user" aria-hidden="true"></i> Apellido
|
|
</label>
|
|
<input
|
|
className="input"
|
|
type="text"
|
|
name="lastName"
|
|
value={form.lastName}
|
|
onChange={handleChange}
|
|
placeholder="Apellido"
|
|
required
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="form-row">
|
|
<div className="form-group" style={{ flex: 1 }}>
|
|
<label>
|
|
<i className="fas fa-lock" aria-hidden="true"></i> Contraseña (opcional)
|
|
</label>
|
|
<input
|
|
className="input"
|
|
type="password"
|
|
name="password"
|
|
value={form.password}
|
|
onChange={handleChange}
|
|
placeholder="Nueva contraseña"
|
|
minLength={6}
|
|
/>
|
|
<small style={{ color: 'var(--gray)' }}>
|
|
Déjala vacía si no deseas cambiarla.
|
|
</small>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="form-row" style={{ justifyContent: 'flex-end', gap: 8 }}>
|
|
<button type="button" className="btn btn-outline" onClick={closeModal}>
|
|
Cancelar
|
|
</button>
|
|
<button type="submit" className="btn btn-primary">
|
|
<i className="fas fa-save"></i> Guardar
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export default ProfilePage;
|