Enhance ProfilePage with editable user profile and local storage support

Added functionality to edit and save user profiles in `ProfilePage`. Introduced local storage integration to persist updates. Implemented a modal for profile updates, including basic validations and password change simulation. Improved UI to dynamically render profile fields and enable updates.
This commit is contained in:
2025-11-22 22:38:32 -04:00
parent 38945d6336
commit 49efe3b441

View File

@@ -1,4 +1,4 @@
import React from 'react'; import React, { useEffect, useMemo, useState } from 'react';
import '../../styles/components.css'; import '../../styles/components.css';
const ProfileRow = ({ label, value, icon }) => ( const ProfileRow = ({ label, value, icon }) => (
@@ -15,14 +15,103 @@ const ProfileRow = ({ label, value, icon }) => (
</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 ProfilePage = () => {
// En esta versión demo, la información del usuario es estática. const defaultProfile = useMemo(() => ({
// En una integración real, estos datos vendrían del backend o del contexto de autenticación. username: 'jperez',
const user = { firstName: 'Juan',
initials: 'JP', lastName: 'Pérez',
name: 'Juan Pérez',
role: 'Administrador',
email: 'juan.perez@empresa.com', 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({
username: profile.username,
firstName: profile.firstName,
lastName: profile.lastName,
password: '',
});
useEffect(() => {
if (showModal) {
setForm({
username: profile.username,
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.username.trim() || !form.firstName.trim() || !form.lastName.trim()) {
alert('Por favor, completa los campos: usuario, 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,
username: form.username.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 ( return (
@@ -33,12 +122,17 @@ const ProfilePage = () => {
<i className="fas fa-user" style={{ marginRight: 8 }}></i> <i className="fas fa-user" style={{ marginRight: 8 }}></i>
Perfil de usuario Perfil de usuario
</h3> </h3>
<div>
<button className="btn btn-primary" onClick={openModal}>
<i className="fas fa-edit"></i> Actualizar
</button>
</div>
</div> </div>
<div style={{ display: 'flex', alignItems: 'center', gap: 16 }}> <div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
<div className="user-avatar" aria-hidden="true">{user.initials}</div> <div className="user-avatar" aria-hidden="true">{initials}</div>
<div> <div>
<div className="user-name" style={{ margin: 0 }}>{user.name}</div> <div className="user-name" style={{ margin: 0 }}>{profile.firstName} {profile.lastName}</div>
<div className="user-role" style={{ margin: 0 }}>{user.role}</div> <div className="user-role" style={{ margin: 0 }}>{profile.role}</div>
</div> </div>
</div> </div>
</div> </div>
@@ -51,11 +145,108 @@ const ProfilePage = () => {
</h3> </h3>
</div> </div>
<div> <div>
<ProfileRow label="Nombre" value={user.name} icon="fas fa-user" /> <ProfileRow label="Usuario" value={profile.username} icon="fas fa-user-circle" />
<ProfileRow label="Correo" value={user.email} icon="fas fa-envelope" /> <ProfileRow label="Nombre" value={`${profile.firstName} ${profile.lastName}`} icon="fas fa-user" />
<ProfileRow label="Rol" value={user.role} icon="fas fa-user-shield" /> <ProfileRow label="Correo" value={profile.email} icon="fas fa-envelope" />
<ProfileRow label="Rol" value={profile.role} icon="fas fa-user-shield" />
</div> </div>
</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-user-circle" aria-hidden="true"></i> Usuario
</label>
<input
className="input"
type="text"
name="username"
value={form.username}
onChange={handleChange}
placeholder="Nombre de usuario"
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> </section>
); );
}; };