added user profile
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
"bamort/maintenance"
|
||||
"bamort/pdfrender"
|
||||
"bamort/router"
|
||||
"bamort/user"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -72,6 +73,7 @@ func main() {
|
||||
logger.Debug("Registriere API-Routen...")
|
||||
protected := router.BaseRouterGrp(r)
|
||||
// Register your module routes
|
||||
user.RegisterRoutes(protected)
|
||||
gsmaster.RegisterRoutes(protected)
|
||||
character.RegisterRoutes(protected)
|
||||
equipment.RegisterRoutes(protected)
|
||||
|
||||
@@ -413,3 +413,137 @@ func ValidateResetToken(c *gin.Context) {
|
||||
"expires": user.ResetPwHashExpires,
|
||||
})
|
||||
}
|
||||
|
||||
// GetUserProfile Handler to get current user's profile information
|
||||
func GetUserProfile(c *gin.Context) {
|
||||
logger.Debug("Lade Benutzerprofil...")
|
||||
|
||||
// Get user ID from context (set by AuthMiddleware)
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
logger.Error("Benutzer-ID nicht im Context gefunden")
|
||||
respondWithError(c, http.StatusUnauthorized, "Unauthorized")
|
||||
return
|
||||
}
|
||||
|
||||
var user User
|
||||
if err := user.FirstId(userID.(uint)); err != nil {
|
||||
logger.Error("Benutzer mit ID %v nicht gefunden: %s", userID, err.Error())
|
||||
respondWithError(c, http.StatusNotFound, "User not found")
|
||||
return
|
||||
}
|
||||
|
||||
logger.Debug("Benutzerprofil geladen für: %s (ID: %d)", user.Username, user.UserID)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"id": user.UserID,
|
||||
"username": user.Username,
|
||||
"email": user.Email,
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateEmail Handler to update user's email address
|
||||
func UpdateEmail(c *gin.Context) {
|
||||
logger.Debug("Starte E-Mail-Aktualisierung...")
|
||||
|
||||
// Get user ID from context
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
logger.Error("Benutzer-ID nicht im Context gefunden")
|
||||
respondWithError(c, http.StatusUnauthorized, "Unauthorized")
|
||||
return
|
||||
}
|
||||
|
||||
var input struct {
|
||||
Email string `json:"email" binding:"required,email"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&input); err != nil {
|
||||
logger.Error("Fehler beim Parsen der E-Mail-Daten: %s", err.Error())
|
||||
respondWithError(c, http.StatusBadRequest, "Valid email address required")
|
||||
return
|
||||
}
|
||||
|
||||
var user User
|
||||
if err := user.FirstId(userID.(uint)); err != nil {
|
||||
logger.Error("Benutzer mit ID %v nicht gefunden: %s", userID, err.Error())
|
||||
respondWithError(c, http.StatusNotFound, "User not found")
|
||||
return
|
||||
}
|
||||
|
||||
// Check if email is already in use by another user
|
||||
var existingUser User
|
||||
if err := existingUser.FindByEmail(input.Email); err == nil {
|
||||
if existingUser.UserID != user.UserID {
|
||||
logger.Warn("E-Mail-Aktualisierung fehlgeschlagen - E-Mail bereits vergeben: %s", input.Email)
|
||||
respondWithError(c, http.StatusConflict, "Email already in use")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
user.Email = input.Email
|
||||
if err := user.Save(); err != nil {
|
||||
logger.Error("Fehler beim Speichern der E-Mail für Benutzer %s: %s", user.Username, err.Error())
|
||||
respondWithError(c, http.StatusInternalServerError, "Failed to update email")
|
||||
return
|
||||
}
|
||||
|
||||
logger.Info("E-Mail erfolgreich aktualisiert für Benutzer: %s (ID: %d)", user.Username, user.UserID)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "Email updated successfully",
|
||||
"email": user.Email,
|
||||
})
|
||||
}
|
||||
|
||||
// UpdatePassword Handler to update user's password
|
||||
func UpdatePassword(c *gin.Context) {
|
||||
logger.Debug("Starte Passwort-Aktualisierung...")
|
||||
|
||||
// Get user ID from context
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
logger.Error("Benutzer-ID nicht im Context gefunden")
|
||||
respondWithError(c, http.StatusUnauthorized, "Unauthorized")
|
||||
return
|
||||
}
|
||||
|
||||
var input struct {
|
||||
CurrentPassword string `json:"current_password" binding:"required"`
|
||||
NewPassword string `json:"new_password" binding:"required,min=6"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&input); err != nil {
|
||||
logger.Error("Fehler beim Parsen der Passwort-Daten: %s", err.Error())
|
||||
respondWithError(c, http.StatusBadRequest, "Current password and new password (min 6 characters) required")
|
||||
return
|
||||
}
|
||||
|
||||
var user User
|
||||
if err := user.FirstId(userID.(uint)); err != nil {
|
||||
logger.Error("Benutzer mit ID %v nicht gefunden: %s", userID, err.Error())
|
||||
respondWithError(c, http.StatusNotFound, "User not found")
|
||||
return
|
||||
}
|
||||
|
||||
// Verify current password
|
||||
hashedCurrentPassword := md5.Sum([]byte(input.CurrentPassword))
|
||||
if user.PasswordHash != hex.EncodeToString(hashedCurrentPassword[:]) {
|
||||
logger.Warn("Passwort-Aktualisierung fehlgeschlagen - Aktuelles Passwort ungültig für Benutzer: %s", user.Username)
|
||||
respondWithError(c, http.StatusUnauthorized, "Current password is incorrect")
|
||||
return
|
||||
}
|
||||
|
||||
// Hash new password
|
||||
hashedNewPassword := md5.Sum([]byte(input.NewPassword))
|
||||
user.PasswordHash = hex.EncodeToString(hashedNewPassword[:])
|
||||
|
||||
if err := user.Save(); err != nil {
|
||||
logger.Error("Fehler beim Speichern des Passworts für Benutzer %s: %s", user.Username, err.Error())
|
||||
respondWithError(c, http.StatusInternalServerError, "Failed to update password")
|
||||
return
|
||||
}
|
||||
|
||||
logger.Info("Passwort erfolgreich aktualisiert für Benutzer: %s (ID: %d)", user.Username, user.UserID)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "Password updated successfully",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// RegisterRoutes registers user-related routes
|
||||
func RegisterRoutes(r *gin.RouterGroup) {
|
||||
userGroup := r.Group("/user")
|
||||
{
|
||||
// Protected routes - require authentication
|
||||
userGroup.GET("/profile", GetUserProfile)
|
||||
userGroup.PUT("/email", UpdateEmail)
|
||||
userGroup.PUT("/password", UpdatePassword)
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,27 @@
|
||||
--color-text: var(--vt-c-text-light-1);
|
||||
|
||||
--section-gap: 160px;
|
||||
|
||||
/* Additional semantic colors */
|
||||
--color-primary: #007bff;
|
||||
--color-primary-dark: #0056b3;
|
||||
--color-bg-secondary: #f8f9fa;
|
||||
--color-text-primary: #333;
|
||||
--color-text-secondary: #495057;
|
||||
|
||||
/* Spacing variables */
|
||||
--padding-xs: 4px;
|
||||
--padding-sm: 8px;
|
||||
--padding-md: 16px;
|
||||
--padding-lg: 24px;
|
||||
--margin-xs: 4px;
|
||||
--margin-sm: 8px;
|
||||
--margin-md: 16px;
|
||||
--margin-lg: 24px;
|
||||
|
||||
/* Other utilities */
|
||||
--border-radius: 6px;
|
||||
--box-shadow-light: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
@@ -47,6 +68,11 @@
|
||||
|
||||
--color-heading: var(--vt-c-text-dark-1);
|
||||
--color-text: var(--vt-c-text-dark-2);
|
||||
|
||||
/* Dark mode adjustments for additional colors */
|
||||
--color-bg-secondary: #2a2a2a;
|
||||
--color-text-primary: #e9ecef;
|
||||
--color-text-secondary: #adb5bd;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,10 @@
|
||||
<router-link to="/maintenance" active-class="active">Maintenance</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
<LanguageSwitcher />
|
||||
<div class="menu-right">
|
||||
<LanguageSwitcher />
|
||||
<router-link v-if="isLoggedIn" to="/profile" active-class="active" class="profile-link">{{ $t('menu.Profile') }}</router-link>
|
||||
</div>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
@@ -80,4 +83,28 @@ export default {
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.menu-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.profile-link {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: var(--border-radius);
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.profile-link:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.profile-link.active {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -36,6 +36,7 @@ export default {
|
||||
Notes:'Notizen',
|
||||
Campagne:'Kampagne',
|
||||
DeleteChar:'Figur löschen',
|
||||
Profile:'Profil',
|
||||
//Character:'Charakter',
|
||||
},
|
||||
Equipment:'Ausrüstung',
|
||||
@@ -437,5 +438,37 @@ export default {
|
||||
generating: 'PDF wird generiert...',
|
||||
pleaseWait: 'Bitte warten, dies kann einen Moment dauern',
|
||||
popupBlocked: 'Popup wurde blockiert. Bitte erlauben Sie Popups für diese Seite.'
|
||||
},
|
||||
profile: {
|
||||
title: 'Benutzerprofil',
|
||||
loading: 'Lade Profil...',
|
||||
userInfo: 'Benutzerinformationen',
|
||||
username: 'Benutzername',
|
||||
currentEmail: 'Aktuelle E-Mail',
|
||||
changeEmail: 'E-Mail ändern',
|
||||
newEmail: 'Neue E-Mail',
|
||||
emailPlaceholder: 'ihre.email@example.com',
|
||||
updateEmail: 'E-Mail aktualisieren',
|
||||
changePassword: 'Passwort ändern',
|
||||
currentPassword: 'Aktuelles Passwort',
|
||||
currentPasswordPlaceholder: 'Ihr aktuelles Passwort',
|
||||
newPassword: 'Neues Passwort',
|
||||
newPasswordPlaceholder: 'Mindestens 6 Zeichen',
|
||||
confirmPassword: 'Passwort bestätigen',
|
||||
confirmPasswordPlaceholder: 'Neues Passwort wiederholen',
|
||||
updatePassword: 'Passwort aktualisieren',
|
||||
updating: 'Aktualisiere...',
|
||||
loadError: 'Fehler beim Laden des Profils',
|
||||
emailRequired: 'E-Mail-Adresse ist erforderlich',
|
||||
emailUnchanged: 'Die neue E-Mail ist identisch mit der aktuellen',
|
||||
emailUpdateSuccess: 'E-Mail erfolgreich aktualisiert',
|
||||
emailUpdateError: 'Fehler beim Aktualisieren der E-Mail',
|
||||
emailInUse: 'Diese E-Mail-Adresse wird bereits verwendet',
|
||||
allFieldsRequired: 'Alle Felder sind erforderlich',
|
||||
passwordTooShort: 'Das Passwort muss mindestens 6 Zeichen lang sein',
|
||||
passwordMismatch: 'Die Passwörter stimmen nicht überein',
|
||||
passwordUpdateSuccess: 'Passwort erfolgreich aktualisiert',
|
||||
passwordUpdateError: 'Fehler beim Aktualisieren des Passworts',
|
||||
currentPasswordIncorrect: 'Das aktuelle Passwort ist falsch'
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ export default {
|
||||
Notes:'Notes',
|
||||
Campagne:'Campagne',
|
||||
DeleteChar:'Delete Character',
|
||||
Profile:'Profile',
|
||||
//Character:'Charakter',
|
||||
},
|
||||
Equipment:'Equipment',
|
||||
@@ -435,5 +436,37 @@ export default {
|
||||
generating: 'Generating PDF...',
|
||||
pleaseWait: 'Please wait, this may take a moment',
|
||||
popupBlocked: 'Popup was blocked. Please allow popups for this site.'
|
||||
},
|
||||
profile: {
|
||||
title: 'User Profile',
|
||||
loading: 'Loading profile...',
|
||||
userInfo: 'User Information',
|
||||
username: 'Username',
|
||||
currentEmail: 'Current Email',
|
||||
changeEmail: 'Change Email',
|
||||
newEmail: 'New Email',
|
||||
emailPlaceholder: 'your.email@example.com',
|
||||
updateEmail: 'Update Email',
|
||||
changePassword: 'Change Password',
|
||||
currentPassword: 'Current Password',
|
||||
currentPasswordPlaceholder: 'Your current password',
|
||||
newPassword: 'New Password',
|
||||
newPasswordPlaceholder: 'At least 6 characters',
|
||||
confirmPassword: 'Confirm Password',
|
||||
confirmPasswordPlaceholder: 'Repeat new password',
|
||||
updatePassword: 'Update Password',
|
||||
updating: 'Updating...',
|
||||
loadError: 'Failed to load profile',
|
||||
emailRequired: 'Email address is required',
|
||||
emailUnchanged: 'The new email is identical to the current one',
|
||||
emailUpdateSuccess: 'Email updated successfully',
|
||||
emailUpdateError: 'Failed to update email',
|
||||
emailInUse: 'This email address is already in use',
|
||||
allFieldsRequired: 'All fields are required',
|
||||
passwordTooShort: 'Password must be at least 6 characters long',
|
||||
passwordMismatch: 'Passwords do not match',
|
||||
passwordUpdateSuccess: 'Password updated successfully',
|
||||
passwordUpdateError: 'Failed to update password',
|
||||
currentPasswordIncorrect: 'Current password is incorrect'
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import DashboardView from "../views/DashboardView.vue";
|
||||
import AusruestungView from "../views/AusruestungView.vue";
|
||||
import MaintenanceView from "../views/MaintenanceView.vue";
|
||||
import FileUploadPage from "../views/FileUploadPage.vue";
|
||||
import UserProfileView from "../views/UserProfileView.vue";
|
||||
|
||||
import CharacterDetails from "@/components/CharacterDetails.vue";
|
||||
import CharacterCreation from "@/components/CharacterCreation.vue";
|
||||
@@ -21,6 +22,7 @@ const routes = [
|
||||
{ path: "/forgot-password", name: "ForgotPassword", component: ForgotPasswordView },
|
||||
{ path: "/reset-password", name: "ResetPassword", component: ResetPasswordView },
|
||||
{ path: "/dashboard", name: "Dashboard", component: DashboardView, meta: { requiresAuth: true } },
|
||||
{ path: "/profile", name: "UserProfile", component: UserProfileView, meta: { requiresAuth: true } },
|
||||
{ path: "/ausruestung/:characterId", name: "Ausruestung", component: AusruestungView, meta: { requiresAuth: true } },
|
||||
{ path: "/maintenance", name: "Maintenance", component: MaintenanceView, meta: { requiresAuth: true } },
|
||||
{ path: "/upload", name: "FileUpload", component: FileUploadPage },
|
||||
|
||||
@@ -0,0 +1,325 @@
|
||||
<template>
|
||||
<div class="user-profile">
|
||||
<div class="container">
|
||||
<h1>{{ $t('profile.title') }}</h1>
|
||||
|
||||
<div v-if="loading" class="loading">
|
||||
{{ $t('profile.loading') }}
|
||||
</div>
|
||||
|
||||
<div v-else class="profile-sections">
|
||||
<!-- User Information Section -->
|
||||
<div class="profile-section">
|
||||
<h2>{{ $t('profile.userInfo') }}</h2>
|
||||
<div class="info-row">
|
||||
<label>{{ $t('profile.username') }}:</label>
|
||||
<span>{{ userProfile.username }}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<label>{{ $t('profile.currentEmail') }}:</label>
|
||||
<span>{{ userProfile.email }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Change Email Section -->
|
||||
<div class="profile-section">
|
||||
<h2>{{ $t('profile.changeEmail') }}</h2>
|
||||
<form @submit.prevent="updateEmail" class="profile-form">
|
||||
<div class="form-group">
|
||||
<label for="newEmail">{{ $t('profile.newEmail') }}:</label>
|
||||
<input
|
||||
type="email"
|
||||
id="newEmail"
|
||||
v-model="emailForm.newEmail"
|
||||
:placeholder="$t('profile.emailPlaceholder')"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<button type="submit" :disabled="isUpdating" class="btn-primary">
|
||||
<span v-if="!isUpdating">{{ $t('profile.updateEmail') }}</span>
|
||||
<span v-else>{{ $t('profile.updating') }}</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Change Password Section -->
|
||||
<div class="profile-section">
|
||||
<h2>{{ $t('profile.changePassword') }}</h2>
|
||||
<form @submit.prevent="updatePassword" class="profile-form">
|
||||
<div class="form-group">
|
||||
<label for="currentPassword">{{ $t('profile.currentPassword') }}:</label>
|
||||
<input
|
||||
type="password"
|
||||
id="currentPassword"
|
||||
v-model="passwordForm.currentPassword"
|
||||
:placeholder="$t('profile.currentPasswordPlaceholder')"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="newPassword">{{ $t('profile.newPassword') }}:</label>
|
||||
<input
|
||||
type="password"
|
||||
id="newPassword"
|
||||
v-model="passwordForm.newPassword"
|
||||
:placeholder="$t('profile.newPasswordPlaceholder')"
|
||||
minlength="6"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="confirmPassword">{{ $t('profile.confirmPassword') }}:</label>
|
||||
<input
|
||||
type="password"
|
||||
id="confirmPassword"
|
||||
v-model="passwordForm.confirmPassword"
|
||||
:placeholder="$t('profile.confirmPasswordPlaceholder')"
|
||||
minlength="6"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<button type="submit" :disabled="isUpdating" class="btn-primary">
|
||||
<span v-if="!isUpdating">{{ $t('profile.updatePassword') }}</span>
|
||||
<span v-else>{{ $t('profile.updating') }}</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.user-profile {
|
||||
padding: var(--padding-lg);
|
||||
margin-top: 2%;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: var(--color-primary);
|
||||
margin-bottom: var(--margin-lg);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: var(--padding-lg);
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.profile-sections {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--margin-lg);
|
||||
}
|
||||
|
||||
.profile-section {
|
||||
background-color: var(--color-bg-secondary);
|
||||
padding: var(--padding-md);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: var(--box-shadow-light);
|
||||
}
|
||||
|
||||
.profile-section h2 {
|
||||
color: var(--color-primary);
|
||||
margin-bottom: var(--margin-md);
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
padding: var(--padding-sm) 0;
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
}
|
||||
|
||||
.info-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.info-row label {
|
||||
font-weight: bold;
|
||||
width: 200px;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.info-row span {
|
||||
flex: 1;
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.profile-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--margin-md);
|
||||
}
|
||||
|
||||
.form-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--margin-xs);
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
font-weight: bold;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.form-group input {
|
||||
padding: var(--padding-sm);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--border-radius);
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.form-group input:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-primary);
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: var(--color-primary);
|
||||
color: white;
|
||||
padding: var(--padding-sm) var(--padding-md);
|
||||
border: none;
|
||||
border-radius: var(--border-radius);
|
||||
cursor: pointer;
|
||||
font-size: 1em;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.btn-primary:hover:not(:disabled) {
|
||||
background-color: var(--color-primary-dark);
|
||||
}
|
||||
|
||||
.btn-primary:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import API from '../utils/api'
|
||||
|
||||
export default {
|
||||
name: 'UserProfileView',
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
isUpdating: false,
|
||||
userProfile: {
|
||||
username: '',
|
||||
email: ''
|
||||
},
|
||||
emailForm: {
|
||||
newEmail: ''
|
||||
},
|
||||
passwordForm: {
|
||||
currentPassword: '',
|
||||
newPassword: '',
|
||||
confirmPassword: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
await this.loadProfile()
|
||||
},
|
||||
methods: {
|
||||
async loadProfile() {
|
||||
this.loading = true
|
||||
try {
|
||||
const response = await API.get('/api/user/profile')
|
||||
this.userProfile = response.data
|
||||
this.emailForm.newEmail = this.userProfile.email
|
||||
} catch (error) {
|
||||
console.error('Failed to load profile:', error)
|
||||
alert(this.$t('profile.loadError') + ': ' + (error.response?.data?.error || error.message))
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
async updateEmail() {
|
||||
if (!this.emailForm.newEmail) {
|
||||
alert(this.$t('profile.emailRequired'))
|
||||
return
|
||||
}
|
||||
|
||||
if (this.emailForm.newEmail === this.userProfile.email) {
|
||||
alert(this.$t('profile.emailUnchanged'))
|
||||
return
|
||||
}
|
||||
|
||||
this.isUpdating = true
|
||||
try {
|
||||
const response = await API.put('/api/user/email', {
|
||||
email: this.emailForm.newEmail
|
||||
})
|
||||
|
||||
this.userProfile.email = response.data.email
|
||||
alert(this.$t('profile.emailUpdateSuccess'))
|
||||
} catch (error) {
|
||||
console.error('Failed to update email:', error)
|
||||
let errorMsg = this.$t('profile.emailUpdateError')
|
||||
if (error.response?.data?.error) {
|
||||
if (error.response.data.error.includes('already in use')) {
|
||||
errorMsg = this.$t('profile.emailInUse')
|
||||
} else {
|
||||
errorMsg += ': ' + error.response.data.error
|
||||
}
|
||||
}
|
||||
alert(errorMsg)
|
||||
} finally {
|
||||
this.isUpdating = false
|
||||
}
|
||||
},
|
||||
async updatePassword() {
|
||||
if (!this.passwordForm.currentPassword || !this.passwordForm.newPassword || !this.passwordForm.confirmPassword) {
|
||||
alert(this.$t('profile.allFieldsRequired'))
|
||||
return
|
||||
}
|
||||
|
||||
if (this.passwordForm.newPassword.length < 6) {
|
||||
alert(this.$t('profile.passwordTooShort'))
|
||||
return
|
||||
}
|
||||
|
||||
if (this.passwordForm.newPassword !== this.passwordForm.confirmPassword) {
|
||||
alert(this.$t('profile.passwordMismatch'))
|
||||
return
|
||||
}
|
||||
|
||||
this.isUpdating = true
|
||||
try {
|
||||
await API.put('/api/user/password', {
|
||||
current_password: this.passwordForm.currentPassword,
|
||||
new_password: this.passwordForm.newPassword
|
||||
})
|
||||
|
||||
alert(this.$t('profile.passwordUpdateSuccess'))
|
||||
|
||||
// Clear password fields
|
||||
this.passwordForm.currentPassword = ''
|
||||
this.passwordForm.newPassword = ''
|
||||
this.passwordForm.confirmPassword = ''
|
||||
} catch (error) {
|
||||
console.error('Failed to update password:', error)
|
||||
let errorMsg = this.$t('profile.passwordUpdateError')
|
||||
if (error.response?.data?.error) {
|
||||
if (error.response.data.error.includes('incorrect')) {
|
||||
errorMsg = this.$t('profile.currentPasswordIncorrect')
|
||||
} else {
|
||||
errorMsg += ': ' + error.response.data.error
|
||||
}
|
||||
}
|
||||
alert(errorMsg)
|
||||
} finally {
|
||||
this.isUpdating = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user