tsk-6: Add feature to update password in profile #13
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "textsender",
|
"name": "textsender",
|
||||||
"version": "0.0.6",
|
"version": "0.0.7",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "textsender",
|
"name": "textsender",
|
||||||
"version": "0.0.6",
|
"version": "0.0.7",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react": "^19.2.7",
|
"react": "^19.2.7",
|
||||||
"react-dom": "^19.2.7",
|
"react-dom": "^19.2.7",
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "textsender",
|
"name": "textsender",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.6",
|
"version": "0.0.7",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -37,4 +37,33 @@ export const userApi = {
|
|||||||
throw new Error('Error logging in');
|
throw new Error('Error logging in');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
update_password: async (
|
||||||
|
userId,
|
||||||
|
currentPassword,
|
||||||
|
updatedPassword,
|
||||||
|
confirmedPassword
|
||||||
|
) => {
|
||||||
|
const request = {
|
||||||
|
user_id: userId,
|
||||||
|
current_password: currentPassword,
|
||||||
|
updated_password: updatedPassword,
|
||||||
|
confirmed_password: confirmedPassword,
|
||||||
|
};
|
||||||
|
|
||||||
|
const reponse = await fetch(
|
||||||
|
`${AUTH_API_BASE_URL}/api/v1/user/password/update`,
|
||||||
|
{
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(request),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (reponse.ok) {
|
||||||
|
return reponse.json();
|
||||||
|
} else {
|
||||||
|
throw new Error('Error logging in');
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,41 +1,95 @@
|
|||||||
.profile-form {
|
.profile-form {
|
||||||
max-width: 400px;
|
background: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||||
|
padding: 40px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 420px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 20px;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
|
||||||
background-color: #ffffff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-form h2 {
|
.profile-form h2 {
|
||||||
|
color: #333;
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 600;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-form form {
|
.profile-form form {
|
||||||
display: flex;
|
margin-bottom: 25px;
|
||||||
flex-direction: column;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-form div {
|
.profile-form div {
|
||||||
margin-bottom: 15px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-form label {
|
.profile-form label {
|
||||||
font-weight: bold;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
display: block;
|
display: block;
|
||||||
|
color: #555;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-form input[type='text'],
|
.profile-form input[type='text'],
|
||||||
.profile-form input[type='tel'] {
|
.profile-form input[type='tel'],
|
||||||
|
.profile-form input[type='password'] {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 8px;
|
padding: 12px 16px;
|
||||||
border: 1px solid #ccc;
|
border: 2px solid #e1e5e9;
|
||||||
border-radius: 4px;
|
border-radius: 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-form input[type='text']:focus,
|
||||||
|
.profile-form input[type='tel']:focus,
|
||||||
|
.profile-form input[type='password']:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #667eea;
|
||||||
|
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-form input[readonly] {
|
.profile-form input[readonly] {
|
||||||
background-color: #f9f9f9;
|
background-color: #f9fafb;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-form button[type='submit'] {
|
||||||
|
width: 100%;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 14px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-form button[type='submit']:hover:not(:disabled) {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 10px 25px rgba(102, 126, 234, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-form button[type='button'] {
|
||||||
|
width: 100%;
|
||||||
|
background: #f1f5f9;
|
||||||
|
color: #334155;
|
||||||
|
border: 1px solid #cbd5e1;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 14px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-form button[type='button']:hover {
|
||||||
|
background: #e2e8f0;
|
||||||
}
|
}
|
||||||
|
|||||||
+165
-69
@@ -9,6 +9,7 @@ import './ProfileForm.css';
|
|||||||
const ProfileForm = () => {
|
const ProfileForm = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [activeView, setActiveView] = useState('profile');
|
const [activeView, setActiveView] = useState('profile');
|
||||||
|
const [subView, setSubView] = useState('details');
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
firstname: '',
|
firstname: '',
|
||||||
lastname: '',
|
lastname: '',
|
||||||
@@ -18,6 +19,12 @@ const ProfileForm = () => {
|
|||||||
lastLoginDate: new Date().toISOString().split('T')[0],
|
lastLoginDate: new Date().toISOString().split('T')[0],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [passwordData, setPasswordData] = useState({
|
||||||
|
currentPassword: '',
|
||||||
|
newPassword: '',
|
||||||
|
confirmNewPassword: '',
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
const userId = tokenService.getUserId();
|
const userId = tokenService.getUserId();
|
||||||
@@ -47,6 +54,14 @@ const ProfileForm = () => {
|
|||||||
setFormData({ ...formData, [name]: value });
|
setFormData({ ...formData, [name]: value });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handlePasswordChange = (e) => {
|
||||||
|
const { name, value } = e.target;
|
||||||
|
setPasswordData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[name]: value,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
const handleNavClick = (view) => {
|
const handleNavClick = (view) => {
|
||||||
if (view === 'profile') {
|
if (view === 'profile') {
|
||||||
navigate('/user/profile');
|
navigate('/user/profile');
|
||||||
@@ -55,6 +70,155 @@ const ProfileForm = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handlePasswordSubmit = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const userId = tokenService.getUserId();
|
||||||
|
const currentPassword = passwordData.currentPassword;
|
||||||
|
const newPassword = passwordData.newPassword;
|
||||||
|
const confirmNewPassword = passwordData.confirmNewPassword;
|
||||||
|
|
||||||
|
const response = await userApi.update_password(
|
||||||
|
userId,
|
||||||
|
currentPassword,
|
||||||
|
newPassword,
|
||||||
|
confirmNewPassword
|
||||||
|
);
|
||||||
|
if (response) {
|
||||||
|
console.log('Successfully updated password');
|
||||||
|
} else {
|
||||||
|
console.error('Error updating password');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Login error:', error);
|
||||||
|
} finally {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderPasswordForm = () => (
|
||||||
|
<div className="profile-form">
|
||||||
|
<h2>Change Password</h2>
|
||||||
|
<form onSubmit={handlePasswordSubmit}>
|
||||||
|
<div>
|
||||||
|
<label htmlFor="currentPassword">Current Password</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="currentPassword"
|
||||||
|
name="currentPassword"
|
||||||
|
value={passwordData.currentPassword}
|
||||||
|
onChange={handlePasswordChange}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label htmlFor="newPassword">New Password</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="newPassword"
|
||||||
|
name="newPassword"
|
||||||
|
value={passwordData.newPassword}
|
||||||
|
onChange={handlePasswordChange}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label htmlFor="confirmNewPassword">Confirm New Password</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="confirmNewPassword"
|
||||||
|
name="confirmNewPassword"
|
||||||
|
value={passwordData.confirmNewPassword}
|
||||||
|
onChange={handlePasswordChange}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button type="submit">Update Password</button>
|
||||||
|
<button type="button" onClick={() => setSubView('details')}>
|
||||||
|
Back to Profile
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const renderProfileForm = () => (
|
||||||
|
<div className="profile-form">
|
||||||
|
<h2>User Profile</h2>
|
||||||
|
<form>
|
||||||
|
<div>
|
||||||
|
<label htmlFor="firstname">First Name</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="firstname"
|
||||||
|
name="firstname"
|
||||||
|
value={formData.firstname}
|
||||||
|
onChange={handleChange}
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label htmlFor="lastname">Last Name</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="lastname"
|
||||||
|
name="lastname"
|
||||||
|
value={formData.lastname}
|
||||||
|
onChange={handleChange}
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label htmlFor="phone">Phone</label>
|
||||||
|
<input
|
||||||
|
type="tel"
|
||||||
|
id="phone"
|
||||||
|
name="phone"
|
||||||
|
value={formData.phone}
|
||||||
|
onChange={handleChange}
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label htmlFor="username">Username</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="username"
|
||||||
|
name="username"
|
||||||
|
value={formData.username}
|
||||||
|
onChange={handleChange}
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label htmlFor="createdDate">Created Date</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="createdDate"
|
||||||
|
name="createdDate"
|
||||||
|
value={formData.createdDate}
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label htmlFor="lastLoginDate">Last Login Date</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="lastLoginDate"
|
||||||
|
name="lastLoginDate"
|
||||||
|
value={formData.lastLoginDate}
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<button type="button" onClick={() => setSubView('password')}>
|
||||||
|
Change Password
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="dashboard-container">
|
<div className="dashboard-container">
|
||||||
{/* Navbar */}
|
{/* Navbar */}
|
||||||
@@ -85,75 +249,7 @@ const ProfileForm = () => {
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{/* Main Content */}
|
{/* Main Content */}
|
||||||
<div className="profile-form">
|
{subView === 'details' ? renderProfileForm() : renderPasswordForm()}
|
||||||
<h2>User Profile</h2>
|
|
||||||
<form>
|
|
||||||
<div>
|
|
||||||
<label htmlFor="firstname">First Name:</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="firstname"
|
|
||||||
name="firstname"
|
|
||||||
value={formData.firstname}
|
|
||||||
onChange={handleChange}
|
|
||||||
readOnly
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label htmlFor="lastname">Last Name:</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="lastname"
|
|
||||||
name="lastname"
|
|
||||||
value={formData.lastname}
|
|
||||||
onChange={handleChange}
|
|
||||||
readOnly
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label htmlFor="phone">Phone:</label>
|
|
||||||
<input
|
|
||||||
type="tel"
|
|
||||||
id="phone"
|
|
||||||
name="phone"
|
|
||||||
value={formData.phone}
|
|
||||||
onChange={handleChange}
|
|
||||||
readOnly
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label htmlFor="username">Username:</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="username"
|
|
||||||
name="username"
|
|
||||||
value={formData.username}
|
|
||||||
onChange={handleChange}
|
|
||||||
readOnly
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label htmlFor="createdDate">Created Date:</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="createdDate"
|
|
||||||
name="createdDate"
|
|
||||||
value={formData.createdDate}
|
|
||||||
readOnly
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label htmlFor="lastLoginDate">Last Login Date:</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="lastLoginDate"
|
|
||||||
name="lastLoginDate"
|
|
||||||
value={formData.lastLoginDate}
|
|
||||||
readOnly
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user