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:
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import '../../styles/components.css';
|
||||
|
||||
const ProfileRow = ({ label, value, icon }) => (
|
||||
@@ -15,14 +15,103 @@ const ProfileRow = ({ label, value, icon }) => (
|
||||
</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 = () => {
|
||||
// En esta versión demo, la información del usuario es estática.
|
||||
// En una integración real, estos datos vendrían del backend o del contexto de autenticación.
|
||||
const user = {
|
||||
initials: 'JP',
|
||||
name: 'Juan Pérez',
|
||||
role: 'Administrador',
|
||||
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({
|
||||
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 (
|
||||
@@ -33,12 +122,17 @@ const ProfilePage = () => {
|
||||
<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">{user.initials}</div>
|
||||
<div className="user-avatar" aria-hidden="true">{initials}</div>
|
||||
<div>
|
||||
<div className="user-name" style={{ margin: 0 }}>{user.name}</div>
|
||||
<div className="user-role" style={{ margin: 0 }}>{user.role}</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>
|
||||
@@ -51,11 +145,108 @@ const ProfilePage = () => {
|
||||
</h3>
|
||||
</div>
|
||||
<div>
|
||||
<ProfileRow label="Nombre" value={user.name} icon="fas fa-user" />
|
||||
<ProfileRow label="Correo" value={user.email} icon="fas fa-envelope" />
|
||||
<ProfileRow label="Rol" value={user.role} icon="fas fa-user-shield" />
|
||||
<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-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>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user