As Admin we can change passwords for other users

Role is displayed in user profile
This commit is contained in:
2025-12-30 08:31:37 +01:00
parent fd2c2462b2
commit 5df67ff649
6 changed files with 196 additions and 4 deletions
+13 -1
View File
@@ -456,7 +456,8 @@ export default {
createdAt: 'Erstellt am',
actions: 'Aktionen',
changeRole: 'Rolle ändern',
delete: 'Löschen',
changePassword: 'Passwort ändern✏️',
delete: 'Löschen🗑️',
changeRoleTitle: 'Benutzerrolle ändern',
changeRoleFor: 'Rolle ändern für',
selectRole: 'Rolle auswählen',
@@ -467,6 +468,16 @@ export default {
deleteWarning: 'Diese Aktion kann nicht rückgängig gemacht werden!',
updateError: 'Fehler beim Aktualisieren der Benutzerrolle',
deleteError: 'Fehler beim Löschen des Benutzers',
changePasswordTitle: 'Benutzerpasswort ändern',
changePasswordFor: 'Passwort ändern für',
newPassword: 'Neues Passwort',
newPasswordPlaceholder: 'Mindestens 6 Zeichen',
confirmPassword: 'Passwort bestätigen',
confirmPasswordPlaceholder: 'Neues Passwort wiederholen',
passwordRequired: 'Passwort ist erforderlich',
passwordTooShort: 'Das Passwort muss mindestens 6 Zeichen lang sein',
passwordMismatch: 'Die Passwörter stimmen nicht überein',
passwordChangeError: 'Fehler beim Ändern des Passworts',
roles: {
standard: 'Standard-Benutzer',
maintainer: 'Maintainer',
@@ -479,6 +490,7 @@ export default {
userInfo: 'Benutzerinformationen',
username: 'Benutzername',
currentEmail: 'Aktuelle E-Mail',
role: 'Rolle',
changeEmail: 'E-Mail ändern',
newEmail: 'Neue E-Mail',
emailPlaceholder: 'ihre.email@example.com',
+12
View File
@@ -452,6 +452,7 @@ export default {
createdAt: 'Created At',
actions: 'Actions',
changeRole: 'Change Role',
changePassword: 'Change Password',
delete: 'Delete',
changeRoleTitle: 'Change User Role',
changeRoleFor: 'Change role for',
@@ -463,6 +464,16 @@ export default {
deleteWarning: 'This action cannot be undone!',
updateError: 'Failed to update user role',
deleteError: 'Failed to delete user',
changePasswordTitle: 'Change User Password',
changePasswordFor: 'Change password for',
newPassword: 'New Password',
newPasswordPlaceholder: 'At least 6 characters',
confirmPassword: 'Confirm Password',
confirmPasswordPlaceholder: 'Repeat new password',
passwordRequired: 'Password is required',
passwordTooShort: 'Password must be at least 6 characters long',
passwordMismatch: 'Passwords do not match',
passwordChangeError: 'Failed to change password',
roles: {
standard: 'Standard User',
maintainer: 'Maintainer',
@@ -475,6 +486,7 @@ export default {
userInfo: 'User Information',
username: 'Username',
currentEmail: 'Current Email',
role: 'Role',
changeEmail: 'Change Email',
newEmail: 'New Email',
emailPlaceholder: 'your.email@example.com',
+81 -2
View File
@@ -39,9 +39,15 @@
>
{{ $t('userManagement.changeRole') }}
</button>
<button
@click="openPasswordDialog(user)"
class="btn btn-sm"
>
{{ $t('userManagement.changePassword') }}
</button>
<button
@click="confirmDeleteUser(user)"
class="btn btn-danger btn-sm"
class="btn btn-sm"
:disabled="user.id === currentUser.id"
>
{{ $t('userManagement.delete') }}
@@ -100,6 +106,46 @@
</div>
</div>
</div>
<!-- Change Password Dialog -->
<div v-if="showPasswordDialog" class="modal-overlay" @click.self="showPasswordDialog = false">
<div class="modal-content">
<div class="modal-header">
<h3>{{ $t('userManagement.changePasswordTitle') }}</h3>
</div>
<div class="modal-body">
<p>{{ $t('userManagement.changePasswordFor') }}: <strong>{{ selectedUser.username }}</strong></p>
<div class="form-group">
<label>{{ $t('userManagement.newPassword') }}</label>
<input
v-model="newPassword"
type="password"
class="form-control"
:placeholder="$t('userManagement.newPasswordPlaceholder')"
minlength="6"
/>
</div>
<div class="form-group">
<label>{{ $t('userManagement.confirmPassword') }}</label>
<input
v-model="confirmPassword"
type="password"
class="form-control"
:placeholder="$t('userManagement.confirmPasswordPlaceholder')"
minlength="6"
/>
</div>
</div>
<div class="modal-footer">
<button @click="changeUserPassword" class="btn btn-primary">
{{ $t('userManagement.save') }}
</button>
<button @click="showPasswordDialog = false" class="btn btn-secondary">
{{ $t('userManagement.cancel') }}
</button>
</div>
</div>
</div>
</div>
</template>
@@ -219,8 +265,11 @@ export default {
error: null,
showRoleDialog: false,
showDeleteDialog: false,
showPasswordDialog: false,
selectedUser: null,
newRole: ''
newRole: '',
newPassword: '',
confirmPassword: ''
}
},
computed: {
@@ -277,6 +326,36 @@ export default {
this.error = this.$t('userManagement.deleteError')
}
},
openPasswordDialog(user) {
this.selectedUser = user
this.newPassword = ''
this.confirmPassword = ''
this.showPasswordDialog = true
},
async changeUserPassword() {
if (!this.newPassword || !this.confirmPassword) {
this.error = this.$t('userManagement.passwordRequired')
return
}
if (this.newPassword.length < 6) {
this.error = this.$t('userManagement.passwordTooShort')
return
}
if (this.newPassword !== this.confirmPassword) {
this.error = this.$t('userManagement.passwordMismatch')
return
}
try {
await API.put(`/api/users/${this.selectedUser.id}/password`, {
new_password: this.newPassword
})
this.showPasswordDialog = false
this.error = null
} catch (error) {
console.error('Failed to change password:', error)
this.error = this.$t('userManagement.passwordChangeError')
}
},
getRoleBadgeClass(role) {
return `badge-role-${role}`
},
+38 -1
View File
@@ -19,6 +19,12 @@
<label>{{ $t('profile.currentEmail') }}:</label>
<span>{{ userProfile.email }}</span>
</div>
<div class="info-row">
<label>{{ $t('profile.role') }}:</label>
<span :class="getRoleBadgeClass(userProfile.role)">
{{ $t(`userManagement.roles.${userProfile.role}`) }}
</span>
</div>
</div>
<!-- Change Email Section -->
@@ -200,6 +206,33 @@ h1 {
opacity: 0.6;
cursor: not-allowed;
}
.badge-role-standard {
background-color: #6c757d;
color: white;
padding: 4px 12px;
border-radius: 4px;
font-size: 0.875rem;
font-weight: 600;
}
.badge-role-maintainer {
background-color: #0dcaf0;
color: white;
padding: 4px 12px;
border-radius: 4px;
font-size: 0.875rem;
font-weight: 600;
}
.badge-role-admin {
background-color: #dc3545;
color: white;
padding: 4px 12px;
border-radius: 4px;
font-size: 0.875rem;
font-weight: 600;
}
</style>
<script>
@@ -213,7 +246,8 @@ export default {
isUpdating: false,
userProfile: {
username: '',
email: ''
email: '',
role: 'standard'
},
emailForm: {
newEmail: ''
@@ -242,6 +276,9 @@ export default {
this.loading = false
}
},
getRoleBadgeClass(role) {
return `badge-role-${role}`
},
async updateEmail() {
if (!this.emailForm.newEmail) {
alert(this.$t('profile.emailRequired'))