175 lines
4.6 KiB
Vue
175 lines
4.6 KiB
Vue
<script setup lang="ts">
|
|
import { ref } from "vue";
|
|
import Icon from "../Icon.vue";
|
|
|
|
const currentPassword = ref("");
|
|
const newPassword = ref("");
|
|
const confirmPassword = ref("");
|
|
const loading = ref(false);
|
|
const message = ref<{ type: "success" | "error"; text: string } | null>(null);
|
|
|
|
async function changePassword() {
|
|
if (newPassword.value !== confirmPassword.value) {
|
|
message.value = { type: "error", text: "New passwords do not match" };
|
|
return;
|
|
}
|
|
|
|
if (newPassword.value.length < 8) {
|
|
message.value = {
|
|
type: "error",
|
|
text: "Password must be at least 8 characters",
|
|
};
|
|
return;
|
|
}
|
|
|
|
loading.value = true;
|
|
message.value = null;
|
|
|
|
try {
|
|
const response = await fetch("/api/user/change-password", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
currentPassword: currentPassword.value,
|
|
newPassword: newPassword.value,
|
|
confirmPassword: confirmPassword.value,
|
|
}),
|
|
});
|
|
|
|
if (response.ok) {
|
|
message.value = {
|
|
type: "success",
|
|
text: "Password changed successfully!",
|
|
};
|
|
currentPassword.value = "";
|
|
newPassword.value = "";
|
|
confirmPassword.value = "";
|
|
|
|
setTimeout(() => {
|
|
message.value = null;
|
|
}, 3000);
|
|
} else {
|
|
const data = await response.json().catch(() => ({}));
|
|
message.value = {
|
|
type: "error",
|
|
text: data.error || "Failed to change password",
|
|
};
|
|
}
|
|
} catch (error) {
|
|
message.value = { type: "error", text: "An error occurred" };
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
<!-- Success/Error Message Display -->
|
|
<div
|
|
v-if="message"
|
|
:class="[
|
|
'alert mb-6',
|
|
message.type === 'success' ? 'alert-success' : 'alert-error',
|
|
]"
|
|
>
|
|
<Icon
|
|
:name="
|
|
message.type === 'success'
|
|
? 'check-circle'
|
|
: 'exclamation-circle'
|
|
"
|
|
class="w-6 h-6 shrink-0"
|
|
/>
|
|
<span>{{ message.text }}</span>
|
|
</div>
|
|
|
|
<div class="card bg-base-100 shadow-xl border border-base-200 mb-6">
|
|
<div class="card-body p-4 sm:p-6">
|
|
<h2 class="card-title mb-6 text-lg sm:text-xl">
|
|
<Icon name="key" class="w-5 h-5 sm:w-6 sm:h-6" />
|
|
Change Password
|
|
</h2>
|
|
|
|
<form @submit.prevent="changePassword" class="space-y-5">
|
|
<div class="form-control">
|
|
<label
|
|
class="label pb-2 font-medium text-sm sm:text-base"
|
|
for="current-password"
|
|
>
|
|
Current Password
|
|
</label>
|
|
<input
|
|
type="password"
|
|
id="current-password"
|
|
v-model="currentPassword"
|
|
placeholder="Enter current password"
|
|
class="input input-bordered w-full"
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
<div class="form-control">
|
|
<label
|
|
class="label pb-2 font-medium text-sm sm:text-base"
|
|
for="new-password"
|
|
>
|
|
New Password
|
|
</label>
|
|
<input
|
|
type="password"
|
|
id="new-password"
|
|
v-model="newPassword"
|
|
placeholder="Enter new password"
|
|
class="input input-bordered w-full"
|
|
required
|
|
minlength="8"
|
|
/>
|
|
<div class="label pt-2">
|
|
<span
|
|
class="label-text-alt text-base-content/60 text-xs sm:text-sm"
|
|
>Minimum 8 characters</span
|
|
>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-control">
|
|
<label
|
|
class="label pb-2 font-medium text-sm sm:text-base"
|
|
for="confirm-password"
|
|
>
|
|
Confirm New Password
|
|
</label>
|
|
<input
|
|
type="password"
|
|
id="confirm-password"
|
|
v-model="confirmPassword"
|
|
placeholder="Confirm new password"
|
|
class="input input-bordered w-full"
|
|
required
|
|
minlength="8"
|
|
/>
|
|
</div>
|
|
|
|
<div class="flex justify-end pt-4">
|
|
<button
|
|
type="submit"
|
|
class="btn btn-primary w-full sm:w-auto"
|
|
:disabled="loading"
|
|
>
|
|
<span
|
|
v-if="loading"
|
|
class="loading loading-spinner loading-sm"
|
|
></span>
|
|
<Icon v-else name="lock-closed" class="w-5 h-5" />
|
|
Update Password
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|