feat(member): 회원 설정/헤더 상태 UI와 관리자 멤버 관리 추가
로그인 상태를 헤더에서 즉시 인지하고 계정 관리를 이어갈 수 있도록 사용자 설정과 관리자 멤버 관측 기능을 연결했다. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -4,10 +4,10 @@ definePageMeta({
|
||||
})
|
||||
|
||||
const currentStep = ref(1)
|
||||
const resendCooldown = ref(0)
|
||||
const isSubmitting = ref(false)
|
||||
const signupCompleted = ref(false)
|
||||
const statusMessage = ref('')
|
||||
const submitErrorMessage = ref('')
|
||||
const { data: siteSettings } = await useFetch('/api/site-settings', {
|
||||
default: () => ({
|
||||
title: 'AFFiNE',
|
||||
@@ -32,7 +32,6 @@ const errors = reactive({
|
||||
const showSignupPassword = ref(false)
|
||||
const showSignupPasswordConfirm = ref(false)
|
||||
|
||||
const canResend = computed(() => resendCooldown.value <= 0)
|
||||
const welcomeTitle = computed(() => `Welcome to ${siteSettings.value?.title || 'AFFiNE'}`)
|
||||
const welcomeDescription = computed(() => siteSettings.value?.description || 'Configure your Self Host AFFiNE with a few simple settings.')
|
||||
|
||||
@@ -89,10 +88,11 @@ const validateStepTwo = () => {
|
||||
|
||||
/**
|
||||
* 다음 단계로 이동한다.
|
||||
* @returns {void}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const goNextStep = () => {
|
||||
const goNextStep = async () => {
|
||||
statusMessage.value = ''
|
||||
submitErrorMessage.value = ''
|
||||
|
||||
if (currentStep.value === 1) {
|
||||
currentStep.value = 2
|
||||
@@ -104,8 +104,27 @@ const goNextStep = () => {
|
||||
return
|
||||
}
|
||||
|
||||
currentStep.value = 3
|
||||
resendCooldown.value = 30
|
||||
isSubmitting.value = true
|
||||
|
||||
try {
|
||||
await $fetch('/api/auth/signup', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
username: form.username.trim(),
|
||||
email: form.email.trim(),
|
||||
password: form.password
|
||||
}
|
||||
})
|
||||
|
||||
signupCompleted.value = true
|
||||
currentStep.value = 3
|
||||
statusMessage.value = '회원가입이 완료되었습니다. 잠시 후 홈으로 이동합니다.'
|
||||
await navigateTo('/')
|
||||
} catch (error) {
|
||||
submitErrorMessage.value = error?.data?.message || '회원가입에 실패했습니다.'
|
||||
} finally {
|
||||
isSubmitting.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,49 +139,6 @@ const goPreviousStep = () => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 인증 메일 재전송을 시뮬레이션한다.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const resendVerificationEmail = async () => {
|
||||
if (!canResend.value || isSubmitting.value) {
|
||||
return
|
||||
}
|
||||
|
||||
isSubmitting.value = true
|
||||
await new Promise((resolve) => setTimeout(resolve, 500))
|
||||
isSubmitting.value = false
|
||||
resendCooldown.value = 30
|
||||
statusMessage.value = '인증 메일을 다시 보냈습니다. 메일함을 확인해 주세요.'
|
||||
}
|
||||
|
||||
/**
|
||||
* 이메일 인증 완료를 시뮬레이션한다.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const completeSignup = async () => {
|
||||
isSubmitting.value = true
|
||||
await new Promise((resolve) => setTimeout(resolve, 600))
|
||||
isSubmitting.value = false
|
||||
signupCompleted.value = true
|
||||
statusMessage.value = '이메일 인증이 완료되었습니다. 로그인 페이지로 이동할 수 있습니다.'
|
||||
}
|
||||
|
||||
const countdownTimer = ref(/** @type {ReturnType<typeof setInterval> | null} */ (null))
|
||||
|
||||
onMounted(() => {
|
||||
countdownTimer.value = setInterval(() => {
|
||||
if (resendCooldown.value > 0) {
|
||||
resendCooldown.value -= 1
|
||||
}
|
||||
}, 1000)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (countdownTimer.value) {
|
||||
clearInterval(countdownTimer.value)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -271,25 +247,17 @@ onBeforeUnmount(() => {
|
||||
|
||||
<template v-else>
|
||||
<p class="text-2xl font-semibold leading-tight">
|
||||
이메일 확인
|
||||
회원가입 완료
|
||||
</p>
|
||||
<p class="mt-2 text-sm text-[#9ba3af]">
|
||||
{{ form.email }} 주소로 인증 메일을 보냈습니다.<br>
|
||||
이메일 링크를 확인해야 회원가입이 확정됩니다.
|
||||
{{ form.email }} 계정으로 가입되었습니다.<br>
|
||||
이제 로그인 후 댓글을 작성할 수 있습니다.
|
||||
</p>
|
||||
|
||||
<div class="mt-8 rounded-[10px] border border-[#1a212a] bg-[#0d1116] p-4">
|
||||
<p class="text-sm text-[#d8dee6]">
|
||||
메일이 오지 않았다면 인증 메일을 재전송해 주세요.
|
||||
가입이 완료되면 자동으로 홈으로 이동합니다.
|
||||
</p>
|
||||
<button
|
||||
class="mt-3 h-9 rounded-[8px] border border-[#2f6feb] px-4 text-xs font-medium text-[#7eb8ff] transition-opacity disabled:cursor-not-allowed disabled:opacity-40"
|
||||
type="button"
|
||||
:disabled="!canResend || isSubmitting"
|
||||
@click="resendVerificationEmail"
|
||||
>
|
||||
{{ canResend ? '인증 메일 재전송' : `${resendCooldown}초 후 재전송` }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p v-if="statusMessage" class="mt-4 text-sm text-[#7ccf90]" aria-live="polite">
|
||||
@@ -324,9 +292,9 @@ onBeforeUnmount(() => {
|
||||
class="h-9 rounded-[8px] bg-[#2f6feb] px-8 text-xs font-medium text-white transition-opacity hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-60"
|
||||
type="button"
|
||||
:disabled="isSubmitting"
|
||||
@click="completeSignup"
|
||||
@click="goNextStep"
|
||||
>
|
||||
인증 완료
|
||||
가입 처리 중
|
||||
</button>
|
||||
|
||||
<NuxtLink
|
||||
@@ -338,6 +306,10 @@ onBeforeUnmount(() => {
|
||||
</NuxtLink>
|
||||
</div>
|
||||
|
||||
<p v-if="submitErrorMessage" class="mt-3 text-xs text-[#e5acb1]" aria-live="polite">
|
||||
{{ submitErrorMessage }}
|
||||
</p>
|
||||
|
||||
<div class="mt-8 flex items-center gap-1.5">
|
||||
<span class="h-[2px] w-9 rounded-full" :class="currentStep >= 1 ? 'bg-[#2f6feb]' : 'bg-[#222a34]'" />
|
||||
<span class="h-[2px] w-9 rounded-full" :class="currentStep >= 2 ? 'bg-[#2f6feb]' : 'bg-[#222a34]'" />
|
||||
|
||||
Reference in New Issue
Block a user