v0.1.11 - 인증 UI 연결

This commit is contained in:
2026-04-21 18:06:24 +09:00
parent 5b1c4bcfca
commit f718342b93
7 changed files with 380 additions and 5 deletions

View File

@@ -1,8 +1,17 @@
<script setup>
import { computed, reactive, ref, watch, nextTick } from 'vue'
import { computed, onMounted, reactive, ref, watch, nextTick } from 'vue'
import AuthDialog from './components/AuthDialog.vue'
import MiniCalendar from './components/MiniCalendar.vue'
import PlannerPage from './components/PlannerPage.vue'
import StatsDashboard from './components/StatsDashboard.vue'
import {
clearAuthState,
fetchCurrentUser,
login,
persistAuthState,
readAuthState,
signup,
} from './lib/authClient'
import {
createInitialPlannerRecords,
persistPlannerState,
@@ -12,10 +21,21 @@ import {
const screenMode = ref('planner')
const viewMode = ref('focus')
const printLayout = ref('single')
const authDialogOpen = ref(false)
const authMode = ref('login')
const authBusy = ref(false)
const authMessage = ref('')
const authToken = ref('')
const currentUser = ref(null)
const selectedDate = ref(new Date())
const calendarViewDate = ref(new Date(selectedDate.value))
const statsRangeStart = ref(toKey(new Date(new Date().setDate(new Date().getDate() - 6))))
const statsRangeEnd = ref(toKey(new Date()))
const authForm = reactive({
nickname: '',
email: '',
password: '',
})
const hours = [
'6', '7', '8', '9', '10', '11', '12',
@@ -271,6 +291,8 @@ const markedDateKeys = computed(() =>
.map(([key]) => key),
)
const isAuthenticated = computed(() => Boolean(authToken.value && currentUser.value))
const filledTasks = computed(() =>
planner.value.tasks.filter((task) => task.title.trim()),
)
@@ -540,6 +562,93 @@ function clearTaskLabels(record) {
})
}
function resetAuthForm() {
authForm.nickname = ''
authForm.email = ''
authForm.password = ''
}
function openAuthDialog(mode = 'login') {
authMode.value = mode
authMessage.value = ''
authDialogOpen.value = true
}
function closeAuthDialog() {
authDialogOpen.value = false
authMessage.value = ''
resetAuthForm()
}
function updateAuthField({ field, value }) {
authForm[field] = value
}
function applyAuthSuccess(data) {
authToken.value = data.token
currentUser.value = data.user
persistAuthState({
token: data.token,
user: data.user,
})
closeAuthDialog()
}
async function submitAuthForm() {
authBusy.value = true
authMessage.value = ''
try {
const result =
authMode.value === 'login'
? await login({
email: authForm.email,
password: authForm.password,
})
: await signup({
nickname: authForm.nickname,
email: authForm.email,
password: authForm.password,
})
applyAuthSuccess(result)
} catch (error) {
authMessage.value = error.message || '인증 처리 중 문제가 발생했습니다.'
} finally {
authBusy.value = false
}
}
async function restoreAuthSession() {
const savedAuth = readAuthState()
if (!savedAuth.token) {
return
}
authToken.value = savedAuth.token
currentUser.value = savedAuth.user ?? null
try {
const result = await fetchCurrentUser(savedAuth.token)
currentUser.value = result.user
persistAuthState({
token: savedAuth.token,
user: result.user,
})
} catch (error) {
authToken.value = ''
currentUser.value = null
clearAuthState()
}
}
function logout() {
authToken.value = ''
currentUser.value = null
clearAuthState()
}
function applyPrintPageStyle(layout) {
if (typeof document === 'undefined') {
return
@@ -566,6 +675,10 @@ async function printSelectedPlanner(layout = 'single') {
await nextTick()
window.print()
}
onMounted(() => {
restoreAuthSession()
})
</script>
<template>
@@ -583,6 +696,39 @@ async function printSelectedPlanner(layout = 'single') {
</p>
</div>
<div class="flex flex-wrap items-center gap-3">
<div class="inline-flex items-center gap-2 rounded-full border border-stone-200 bg-white px-2 py-2">
<template v-if="isAuthenticated">
<div class="px-2">
<p class="text-[10px] font-bold tracking-[0.16em] text-stone-500">SIGNED IN</p>
<p class="text-sm font-semibold tracking-[0.02em] text-stone-900">
{{ currentUser.nickname }}
</p>
</div>
<button
type="button"
class="rounded-full border border-stone-200 px-3 py-2 text-xs font-bold tracking-[0.14em] text-stone-600 transition hover:border-stone-400 hover:text-ink"
@click="logout"
>
LOGOUT
</button>
</template>
<template v-else>
<button
type="button"
class="rounded-full border border-stone-200 px-3 py-2 text-xs font-bold tracking-[0.14em] text-stone-600 transition hover:border-stone-400 hover:text-ink"
@click="openAuthDialog('login')"
>
LOGIN
</button>
<button
type="button"
class="rounded-full border border-stone-900 bg-stone-900 px-3 py-2 text-xs font-bold tracking-[0.14em] text-white transition hover:bg-stone-700"
@click="openAuthDialog('signup')"
>
SIGN UP
</button>
</template>
</div>
<div class="inline-flex rounded-full border border-stone-200 bg-stone-100 p-1">
<button
type="button"
@@ -912,5 +1058,17 @@ async function printSelectedPlanner(layout = 'single') {
</div>
</section>
</div>
<AuthDialog
:open="authDialogOpen"
:mode="authMode"
:form="authForm"
:busy="authBusy"
:message="authMessage"
@close="closeAuthDialog"
@submit="submitAuthForm"
@switch-mode="authMode = $event; authMessage = ''"
@update:field="updateAuthField"
/>
</main>
</template>