- Split UsersView.vue into UserCreateModal, UserEditModal, UserApiKeysModal, etc. - Split UsageView.vue into UsageStatsCards, UsageFilters, UsageTable, etc. - Split DashboardView.vue into UserDashboardStats, UserDashboardCharts, etc. - Split AccountsView.vue into AccountTableActions, AccountTableFilters, etc. - Standardized TypeScript types across new components to resolve implicit 'any' and 'never[]' errors. - Improved overall frontend maintainability and code clarity.
110 lines
3.1 KiB
Vue
110 lines
3.1 KiB
Vue
<template>
|
|
<div class="card">
|
|
<div class="border-b border-gray-100 px-6 py-4 dark:border-dark-700">
|
|
<h2 class="text-lg font-medium text-gray-900 dark:text-white">
|
|
{{ t('profile.changePassword') }}
|
|
</h2>
|
|
</div>
|
|
<div class="px-6 py-6">
|
|
<form @submit.prevent="handleChangePassword" class="space-y-4">
|
|
<div>
|
|
<label for="old_password" class="input-label">
|
|
{{ t('profile.currentPassword') }}
|
|
</label>
|
|
<input
|
|
id="old_password"
|
|
v-model="form.old_password"
|
|
type="password"
|
|
required
|
|
autocomplete="current-password"
|
|
class="input"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label for="new_password" class="input-label">
|
|
{{ t('profile.newPassword') }}
|
|
</label>
|
|
<input
|
|
id="new_password"
|
|
v-model="form.new_password"
|
|
type="password"
|
|
required
|
|
autocomplete="new-password"
|
|
class="input"
|
|
/>
|
|
<p class="input-hint">
|
|
{{ t('profile.passwordHint') }}
|
|
</p>
|
|
</div>
|
|
|
|
<div>
|
|
<label for="confirm_password" class="input-label">
|
|
{{ t('profile.confirmNewPassword') }}
|
|
</label>
|
|
<input
|
|
id="confirm_password"
|
|
v-model="form.confirm_password"
|
|
type="password"
|
|
required
|
|
autocomplete="new-password"
|
|
class="input"
|
|
/>
|
|
<p
|
|
v-if="form.new_password && form.confirm_password && form.new_password !== form.confirm_password"
|
|
class="input-error-text"
|
|
>
|
|
{{ t('profile.passwordsNotMatch') }}
|
|
</p>
|
|
</div>
|
|
|
|
<div class="flex justify-end pt-4">
|
|
<button type="submit" :disabled="loading" class="btn btn-primary">
|
|
{{ loading ? t('profile.changingPassword') : t('profile.changePasswordButton') }}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref } from 'vue'
|
|
import { useI18n } from 'vue-i18n'
|
|
import { useAppStore } from '@/stores/app'
|
|
import { userAPI } from '@/api'
|
|
|
|
const { t } = useI18n()
|
|
const appStore = useAppStore()
|
|
|
|
const loading = ref(false)
|
|
const form = ref({
|
|
old_password: '',
|
|
new_password: '',
|
|
confirm_password: ''
|
|
})
|
|
|
|
const handleChangePassword = async () => {
|
|
if (form.value.new_password !== form.value.confirm_password) {
|
|
appStore.showError(t('profile.passwordsNotMatch'))
|
|
return
|
|
}
|
|
|
|
if (form.value.new_password.length < 8) {
|
|
appStore.showError(t('profile.passwordTooShort'))
|
|
return
|
|
}
|
|
|
|
loading.value = true
|
|
try {
|
|
await userAPI.changePassword(form.value.old_password, form.value.new_password)
|
|
form.value = { old_password: '', new_password: '', confirm_password: '' }
|
|
appStore.showSuccess(t('profile.passwordChangeSuccess'))
|
|
} catch (error: any) {
|
|
appStore.showError(error.response?.data?.detail || t('profile.passwordChangeFailed'))
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
</script>
|