101 lines
3.6 KiB
Vue
101 lines
3.6 KiB
Vue
<template>
|
|
<section class="flex min-h-dvh flex-col justify-center px-5 py-6 md:mx-auto md:max-w-sm">
|
|
<div class="mb-6">
|
|
<p class="text-sm font-semibold text-brand-700">Money</p>
|
|
<h1 class="mt-2 text-2xl font-bold tracking-normal text-ink">빠르게 기록하는 개인 가계부</h1>
|
|
<p class="mt-2 text-sm leading-6 text-muted">계정을 만들면 내 자산, 카테고리, 거래 내역을 따로 관리할 수 있습니다.</p>
|
|
</div>
|
|
|
|
<div class="mb-4 grid grid-cols-2 gap-2">
|
|
<button
|
|
type="button"
|
|
class="segmented-button border border-line py-2.5"
|
|
:class="{ 'border-red-500 text-red-600': mode === 'login' }"
|
|
@click="mode = 'login'"
|
|
>
|
|
로그인
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="segmented-button border border-line py-2.5"
|
|
:class="{ 'border-red-500 text-red-600': mode === 'register' }"
|
|
@click="mode = 'register'"
|
|
>
|
|
회원가입
|
|
</button>
|
|
</div>
|
|
|
|
<form class="space-y-3" @submit.prevent="submit">
|
|
<div v-if="mode === 'register'">
|
|
<label class="label" for="name">이름</label>
|
|
<input id="name" v-model="name" class="field" autocomplete="name" placeholder="표시 이름">
|
|
</div>
|
|
<div>
|
|
<label class="label" for="email">이메일</label>
|
|
<input id="email" v-model="email" class="field" type="email" autocomplete="email" placeholder="you@example.com" required>
|
|
</div>
|
|
<div>
|
|
<label class="label" for="password">비밀번호</label>
|
|
<input
|
|
id="password"
|
|
v-model="password"
|
|
class="field"
|
|
type="password"
|
|
:autocomplete="mode === 'login' ? 'current-password' : 'new-password'"
|
|
placeholder="8자 이상"
|
|
required
|
|
>
|
|
</div>
|
|
<p v-if="error" class="text-sm font-medium text-red-600">{{ error }}</p>
|
|
<button class="btn-primary w-full" type="submit" :disabled="pending">
|
|
{{ pending ? '확인 중...' : mode === 'login' ? '로그인' : '가입하고 시작하기' }}
|
|
</button>
|
|
</form>
|
|
</section>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
type AuthMode = 'login' | 'register'
|
|
|
|
const mode = ref<AuthMode>('login')
|
|
const name = ref('')
|
|
const email = ref('')
|
|
const password = ref('')
|
|
const error = ref('')
|
|
const pending = ref(false)
|
|
const session = useState('session')
|
|
|
|
async function submit() {
|
|
pending.value = true
|
|
error.value = ''
|
|
try {
|
|
const endpoint = mode.value === 'login' ? '/api/auth/login' : '/api/auth/register'
|
|
session.value = await $fetch(endpoint, {
|
|
method: 'POST',
|
|
body: { name: name.value, email: email.value, password: password.value }
|
|
})
|
|
await navigateTo('/')
|
|
} catch (err: any) {
|
|
const status = err?.statusCode || err?.response?.status
|
|
const statusMessage = err?.statusMessage || err?.response?._data?.statusMessage || ''
|
|
if (mode.value === 'register' && status === 409) {
|
|
error.value = '이미 가입된 이메일입니다.'
|
|
} else if (mode.value === 'register' && statusMessage) {
|
|
error.value = statusMessage === 'Invalid registration'
|
|
? '이메일과 8자 이상 비밀번호를 확인해 주세요.'
|
|
: '가입 처리 중 문제가 생겼습니다. 잠시 후 다시 시도해 주세요.'
|
|
} else if (mode.value === 'register') {
|
|
error.value = '가입 처리 중 문제가 생겼습니다. 잠시 후 다시 시도해 주세요.'
|
|
} else {
|
|
error.value = '이메일 또는 비밀번호를 확인해 주세요.'
|
|
}
|
|
} finally {
|
|
pending.value = false
|
|
}
|
|
}
|
|
|
|
watch(mode, () => {
|
|
error.value = ''
|
|
})
|
|
</script>
|