246 lines
8.1 KiB
Vue
246 lines
8.1 KiB
Vue
<script setup>
|
|
const props = defineProps({
|
|
open: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
mode: {
|
|
type: String,
|
|
default: 'login',
|
|
},
|
|
form: {
|
|
type: Object,
|
|
required: true,
|
|
},
|
|
busy: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
message: {
|
|
type: String,
|
|
default: '',
|
|
},
|
|
showResendVerification: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
})
|
|
|
|
const emit = defineEmits([
|
|
'close',
|
|
'submit',
|
|
'resend-verification',
|
|
'switch-mode',
|
|
'update:field',
|
|
])
|
|
|
|
function updateField(field, event) {
|
|
emit('update:field', {
|
|
field,
|
|
value: event.target.type === 'checkbox' ? event.target.checked : event.target.value,
|
|
})
|
|
}
|
|
|
|
function getTitle(mode) {
|
|
if (mode === 'signup') {
|
|
return '회원가입'
|
|
}
|
|
|
|
if (mode === 'reset-request') {
|
|
return '비밀번호 찾기'
|
|
}
|
|
|
|
if (mode === 'reset-confirm') {
|
|
return '새 비밀번호 설정'
|
|
}
|
|
|
|
return '로그인'
|
|
}
|
|
|
|
function getDescription(mode) {
|
|
if (mode === 'signup') {
|
|
return '기록을 저장할 계정을 만들어요.'
|
|
}
|
|
|
|
if (mode === 'reset-request') {
|
|
return '가입한 이메일로 재설정 링크를 받을 수 있습니다.'
|
|
}
|
|
|
|
if (mode === 'reset-confirm') {
|
|
return '메일로 받은 링크의 토큰과 새 비밀번호를 확인합니다.'
|
|
}
|
|
|
|
return '내 플래너를 이어서 열어요.'
|
|
}
|
|
|
|
function getSubmitLabel(mode, busy) {
|
|
if (busy) {
|
|
return '처리 중...'
|
|
}
|
|
|
|
if (mode === 'signup') {
|
|
return '가입하기'
|
|
}
|
|
|
|
if (mode === 'reset-request') {
|
|
return '재설정 링크 받기'
|
|
}
|
|
|
|
if (mode === 'reset-confirm') {
|
|
return '비밀번호 재설정'
|
|
}
|
|
|
|
return '로그인하기'
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div
|
|
v-if="open"
|
|
class="fixed inset-0 z-50 flex items-center justify-center bg-stone-900/45 px-4 py-6 backdrop-blur-sm"
|
|
>
|
|
<div class="w-full max-w-[420px] rounded-[26px] border border-white/70 bg-[#f7f2ea] p-5 shadow-[0_24px_80px_rgba(28,25,23,0.2)] sm:p-6">
|
|
<div class="flex items-start justify-between gap-4">
|
|
<div>
|
|
<p class="text-[10px] font-bold uppercase tracking-[0.24em] text-stone-500">10 Minute Planner</p>
|
|
<h2 class="mt-2 text-2xl font-semibold tracking-[-0.04em] text-stone-900">
|
|
{{ getTitle(mode) }}
|
|
</h2>
|
|
<p class="mt-2 text-sm leading-6 text-stone-600">
|
|
{{ getDescription(mode) }}
|
|
</p>
|
|
</div>
|
|
<button
|
|
type="button"
|
|
class="rounded-full border border-stone-300 px-3 py-2 text-[10px] font-bold tracking-[0.16em] text-stone-500 transition hover:border-stone-500 hover:text-stone-900"
|
|
@click="emit('close')"
|
|
>
|
|
CLOSE
|
|
</button>
|
|
</div>
|
|
|
|
<form class="mt-6 space-y-4" @submit.prevent="emit('submit')">
|
|
<div v-if="mode === 'signup'" class="space-y-2">
|
|
<label class="text-[11px] font-bold tracking-[0.16em] text-stone-600">닉네임</label>
|
|
<input
|
|
:value="form.nickname"
|
|
type="text"
|
|
class="w-full rounded-2xl border border-stone-300 bg-white/90 px-4 py-3 text-sm font-semibold text-stone-800 outline-none transition focus:border-stone-500"
|
|
placeholder="닉네임을 입력해 주세요."
|
|
@input="updateField('nickname', $event)"
|
|
/>
|
|
</div>
|
|
|
|
<div v-if="mode !== 'reset-confirm'" class="space-y-2">
|
|
<label class="text-[11px] font-bold tracking-[0.16em] text-stone-600">
|
|
{{ mode === 'login' ? '이메일 또는 아이디' : '이메일' }}
|
|
</label>
|
|
<input
|
|
:value="form.email"
|
|
:type="mode === 'login' ? 'text' : 'email'"
|
|
class="w-full rounded-2xl border border-stone-300 bg-white/90 px-4 py-3 text-sm font-semibold text-stone-800 outline-none transition focus:border-stone-500"
|
|
:placeholder="mode === 'login' ? '이메일 또는 아이디를 입력해 주세요.' : 'you@example.com'"
|
|
@input="updateField('email', $event)"
|
|
/>
|
|
</div>
|
|
|
|
<div v-if="mode === 'reset-confirm'" class="space-y-2">
|
|
<label class="text-[11px] font-bold tracking-[0.16em] text-stone-600">재설정 토큰</label>
|
|
<input
|
|
:value="form.resetToken"
|
|
type="text"
|
|
class="w-full rounded-2xl border border-stone-300 bg-white/90 px-4 py-3 text-sm font-semibold text-stone-800 outline-none transition focus:border-stone-500"
|
|
placeholder="메일 링크의 토큰"
|
|
@input="updateField('resetToken', $event)"
|
|
/>
|
|
</div>
|
|
|
|
<div v-if="mode === 'login' || mode === 'signup'" class="space-y-2">
|
|
<label class="text-[11px] font-bold tracking-[0.16em] text-stone-600">비밀번호</label>
|
|
<input
|
|
:value="form.password"
|
|
type="password"
|
|
class="w-full rounded-2xl border border-stone-300 bg-white/90 px-4 py-3 text-sm font-semibold text-stone-800 outline-none transition focus:border-stone-500"
|
|
placeholder="8자 이상 입력해 주세요."
|
|
@input="updateField('password', $event)"
|
|
/>
|
|
</div>
|
|
|
|
<div v-if="mode === 'reset-confirm'" class="space-y-2">
|
|
<label class="text-[11px] font-bold tracking-[0.16em] text-stone-600">새 비밀번호</label>
|
|
<input
|
|
:value="form.newPassword"
|
|
type="password"
|
|
class="w-full rounded-2xl border border-stone-300 bg-white/90 px-4 py-3 text-sm font-semibold text-stone-800 outline-none transition focus:border-stone-500"
|
|
placeholder="8자 이상 입력해 주세요."
|
|
@input="updateField('newPassword', $event)"
|
|
/>
|
|
</div>
|
|
|
|
<label
|
|
v-if="mode === 'login'"
|
|
class="-mt-1 flex items-center gap-2 px-1 text-left"
|
|
>
|
|
<input
|
|
:checked="form.rememberSession"
|
|
type="checkbox"
|
|
class="h-4 w-4 shrink-0 accent-stone-900"
|
|
@change="updateField('rememberSession', $event)"
|
|
/>
|
|
<span class="text-xs font-bold tracking-[0.08em] text-stone-700">로그인 상태 유지</span>
|
|
</label>
|
|
|
|
<div
|
|
v-if="message"
|
|
class="rounded-2xl border border-stone-300 bg-white/80 px-4 py-3"
|
|
>
|
|
<div class="flex items-start justify-between gap-4">
|
|
<p class="min-w-0 text-sm font-semibold leading-6 text-stone-700">
|
|
{{ message }}
|
|
</p>
|
|
<button
|
|
v-if="showResendVerification"
|
|
type="button"
|
|
class="shrink-0 rounded-full border border-stone-300 px-3 py-2 text-[10px] font-bold tracking-[0.14em] text-stone-600 transition hover:border-stone-500 hover:text-stone-900"
|
|
:disabled="busy"
|
|
@click="emit('resend-verification')"
|
|
>
|
|
재전송
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<button
|
|
type="submit"
|
|
class="w-full rounded-full bg-stone-900 px-5 py-3 text-xs font-bold tracking-[0.18em] text-white transition hover:bg-stone-700 disabled:cursor-not-allowed disabled:bg-stone-400"
|
|
:disabled="busy"
|
|
>
|
|
{{ getSubmitLabel(mode, busy) }}
|
|
</button>
|
|
</form>
|
|
|
|
<button
|
|
v-if="mode === 'login'"
|
|
type="button"
|
|
class="mt-4 w-full text-center text-xs font-bold tracking-[0.14em] text-stone-500 underline underline-offset-4 transition hover:text-stone-900"
|
|
@click="emit('switch-mode', 'reset-request')"
|
|
>
|
|
비밀번호를 잊으셨나요?
|
|
</button>
|
|
|
|
<div class="mt-5 flex items-center justify-center gap-2 border-t border-stone-300/70 pt-4">
|
|
<p class="text-sm font-semibold text-stone-600">
|
|
{{ mode === 'signup' ? '이미 계정이 있나요?' : '계정 화면으로 돌아갈까요?' }}
|
|
</p>
|
|
<button
|
|
type="button"
|
|
class="text-xs font-bold tracking-[0.14em] text-stone-900 underline underline-offset-4"
|
|
@click="emit('switch-mode', mode === 'login' ? 'signup' : 'login')"
|
|
>
|
|
{{ mode === 'login' ? '회원가입' : '로그인' }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|