Files
planner.sori.studio/src/components/AuthDialog.vue

228 lines
7.4 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: '',
},
})
const emit = defineEmits([
'close',
'submit',
'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>
<p
v-if="message"
class="rounded-2xl border border-stone-300 bg-white/80 px-4 py-3 text-sm font-semibold leading-6 text-stone-700"
>
{{ message }}
</p>
<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>