131 lines
4.3 KiB
Vue
131 lines
4.3 KiB
Vue
<script setup lang="ts">
|
||
const route = useRoute();
|
||
const router = useRouter();
|
||
|
||
const token = computed(() => (typeof route.query.token === "string" ? route.query.token : ""));
|
||
const password = ref("");
|
||
const confirmPassword = ref("");
|
||
const showPassword = ref(false);
|
||
const showConfirmPassword = ref(false);
|
||
const error = ref("");
|
||
const success = ref("");
|
||
const pending = ref(false);
|
||
|
||
const handleSubmit = async () => {
|
||
error.value = "";
|
||
success.value = "";
|
||
|
||
if (!token.value) {
|
||
error.value = "Ссылка восстановления некорректна";
|
||
return;
|
||
}
|
||
|
||
pending.value = true;
|
||
|
||
try {
|
||
const result = await useApi<{ message: string }>("/auth/reset-password", {
|
||
method: "POST",
|
||
body: {
|
||
token: token.value,
|
||
password: password.value,
|
||
confirmPassword: confirmPassword.value
|
||
}
|
||
});
|
||
|
||
success.value = result.message;
|
||
password.value = "";
|
||
confirmPassword.value = "";
|
||
setTimeout(() => {
|
||
router.push("/login");
|
||
}, 1500);
|
||
} catch (submitError) {
|
||
error.value = submitError instanceof Error ? submitError.message : "Не удалось обновить пароль";
|
||
} finally {
|
||
pending.value = false;
|
||
}
|
||
};
|
||
</script>
|
||
|
||
<template>
|
||
<section class="mx-auto grid min-h-[calc(100vh-12rem)] w-full items-center gap-8 md:max-w-6xl md:grid-cols-[minmax(0,1.1fr)_minmax(22rem,28rem)] md:[min-height:calc(100vh-10rem)]">
|
||
<div class="grid max-w-lg gap-3">
|
||
<p class="m-0 text-xs font-semibold uppercase tracking-[0.18em] text-(--muted)">Alpinbet</p>
|
||
<h1 class="m-0 text-4xl font-semibold leading-tight">Новый пароль</h1>
|
||
<p class="m-0 text-base leading-7 text-(--muted)">
|
||
Установите новый пароль для аккаунта. После сохранения можно будет сразу войти в систему.
|
||
</p>
|
||
</div>
|
||
|
||
<form
|
||
class="grid w-full max-w-md gap-4 rounded-[28px] border p-4"
|
||
:style="{ borderColor: 'var(--border)', backgroundColor: 'var(--surface-strong)' }"
|
||
@submit.prevent="handleSubmit"
|
||
>
|
||
<p class="m-0 text-xs font-semibold uppercase tracking-[0.18em] text-(--muted)">Смена пароля</p>
|
||
|
||
<label class="grid gap-1">
|
||
<span>Новый пароль</span>
|
||
<div class="password-field">
|
||
<input
|
||
v-model="password"
|
||
:type="showPassword ? 'text' : 'password'"
|
||
autocomplete="new-password"
|
||
/>
|
||
<button
|
||
type="button"
|
||
class="password-field__toggle"
|
||
:aria-label="showPassword ? 'Скрыть пароль' : 'Показать пароль'"
|
||
@click="showPassword = !showPassword"
|
||
>
|
||
<i class="pi" :class="showPassword ? 'pi-eye-slash' : 'pi-eye'" aria-hidden="true" />
|
||
</button>
|
||
</div>
|
||
</label>
|
||
|
||
<label class="grid gap-1">
|
||
<span>Повторите пароль</span>
|
||
<div class="password-field">
|
||
<input
|
||
v-model="confirmPassword"
|
||
:type="showConfirmPassword ? 'text' : 'password'"
|
||
autocomplete="new-password"
|
||
/>
|
||
<button
|
||
type="button"
|
||
class="password-field__toggle"
|
||
:aria-label="showConfirmPassword ? 'Скрыть пароль' : 'Показать пароль'"
|
||
@click="showConfirmPassword = !showConfirmPassword"
|
||
>
|
||
<i class="pi" :class="showConfirmPassword ? 'pi-eye-slash' : 'pi-eye'" aria-hidden="true" />
|
||
</button>
|
||
</div>
|
||
</label>
|
||
|
||
<p
|
||
v-if="error"
|
||
class="rounded-2xl p-4 text-sm whitespace-pre-line"
|
||
:style="{ backgroundColor: 'var(--danger-bg)', color: 'var(--danger-text)' }"
|
||
>
|
||
{{ error }}
|
||
</p>
|
||
|
||
<p
|
||
v-if="success"
|
||
class="rounded-2xl p-4 text-sm"
|
||
:style="{ backgroundColor: 'var(--success-bg)', color: 'var(--success-text)' }"
|
||
>
|
||
{{ success }}
|
||
</p>
|
||
|
||
<button type="submit" :disabled="pending || !token">
|
||
{{ pending ? "Сохраняем..." : "Сохранить пароль" }}
|
||
</button>
|
||
|
||
<p class="m-0 text-(--muted)">
|
||
Нужна новая ссылка?
|
||
<NuxtLink class="text-(--accent-strong)" to="/forgot-password">Запросить повторно</NuxtLink>
|
||
</p>
|
||
</form>
|
||
</section>
|
||
</template>
|