Some checks failed
ci / ci (22, ubuntu-latest) (push) Failing after 26m43s
- Introduced a new `.mcp.json` file for server configurations. - Updated `package.json` to include the `shadcn` dependency. - Modified `pnpm-lock.yaml` to reflect the new dependency. - Adjusted `.claude/settings.local.json` to disable specific MCP servers. - Enhanced the login page design in `login.vue` with improved layout and user experience elements.
258 lines
8.3 KiB
Vue
258 lines
8.3 KiB
Vue
<script setup lang="ts">
|
|
definePageMeta({ layout: false });
|
|
|
|
const supabase = useSupabaseClient();
|
|
const email = ref("");
|
|
const loading = ref(false);
|
|
const message = ref("");
|
|
const error = ref("");
|
|
|
|
async function sendMagicLink() {
|
|
if (!email.value) return;
|
|
loading.value = true;
|
|
error.value = "";
|
|
message.value = "";
|
|
|
|
const { error: err } = await supabase.auth.signInWithOtp({
|
|
email: email.value,
|
|
options: {
|
|
emailRedirectTo: `${window.location.origin}/confirm`,
|
|
},
|
|
});
|
|
|
|
loading.value = false;
|
|
if (err) {
|
|
error.value = err.message;
|
|
} else {
|
|
message.value = `${email.value}로 로그인 링크를 전송했습니다. 이메일을 확인해주세요.`;
|
|
}
|
|
}
|
|
|
|
async function signInWithGoogle() {
|
|
await supabase.auth.signInWithOAuth({
|
|
provider: "google",
|
|
options: {
|
|
redirectTo: `${window.location.origin}/confirm`,
|
|
},
|
|
});
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<UApp>
|
|
<div class="min-h-screen flex">
|
|
<!-- 좌측 브랜딩 패널 (데스크탑에서만 표시) -->
|
|
<div
|
|
class="hidden lg:flex lg:w-1/2 relative overflow-hidden bg-gradient-to-br from-green-500 via-emerald-600 to-green-800 flex-col justify-between p-12"
|
|
>
|
|
<!-- 배경 패턴 -->
|
|
<div class="absolute inset-0 opacity-10">
|
|
<div
|
|
class="absolute top-0 left-0 w-64 h-64 rounded-full bg-white -translate-x-1/2 -translate-y-1/2"
|
|
/>
|
|
<div
|
|
class="absolute bottom-0 right-0 w-96 h-96 rounded-full bg-white translate-x-1/3 translate-y-1/3"
|
|
/>
|
|
<div
|
|
class="absolute top-1/2 left-1/2 w-48 h-48 rounded-full bg-white -translate-x-1/2 -translate-y-1/2"
|
|
/>
|
|
</div>
|
|
|
|
<!-- 로고 -->
|
|
<div class="relative z-10 flex items-center gap-3">
|
|
<div
|
|
class="w-10 h-10 rounded-xl bg-white/20 backdrop-blur flex items-center justify-center"
|
|
>
|
|
<UIcon name="i-lucide-tent" class="text-white text-xl" />
|
|
</div>
|
|
<span class="text-white text-xl font-bold tracking-tight"
|
|
>CampGear</span
|
|
>
|
|
</div>
|
|
|
|
<!-- 메인 카피 -->
|
|
<div class="relative z-10 space-y-6">
|
|
<div class="space-y-3">
|
|
<h2 class="text-4xl font-bold text-white leading-tight">
|
|
캠핑의 모든 것,<br />한 곳에서 관리하세요
|
|
</h2>
|
|
<p class="text-green-100 text-lg leading-relaxed">
|
|
장비 구매부터 중고 거래까지,<br />스마트한 캠핑 장비 관리를
|
|
경험하세요.
|
|
</p>
|
|
</div>
|
|
|
|
<!-- 기능 목록 -->
|
|
<div class="space-y-3">
|
|
<div
|
|
v-for="feature in [
|
|
{ icon: 'i-lucide-package', text: '장비 구매 내역 관리' },
|
|
{ icon: 'i-lucide-repeat', text: '중고 장비 판매 · 추적' },
|
|
{ icon: 'i-lucide-bot', text: 'AI 캠핑 어시스턴트' },
|
|
]"
|
|
:key="feature.text"
|
|
class="flex items-center gap-3"
|
|
>
|
|
<div
|
|
class="w-8 h-8 rounded-lg bg-white/20 flex items-center justify-center flex-shrink-0"
|
|
>
|
|
<UIcon :name="feature.icon" class="text-white text-sm" />
|
|
</div>
|
|
<span class="text-green-50 text-sm font-medium">{{
|
|
feature.text
|
|
}}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 하단 문구 -->
|
|
<div class="relative z-10">
|
|
<p class="text-green-200 text-sm">
|
|
© 2026 CampGear. All rights reserved.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 우측 로그인 폼 -->
|
|
<div
|
|
class="flex-1 flex items-center justify-center p-6 sm:p-12 bg-white dark:bg-gray-950"
|
|
>
|
|
<div class="w-full max-w-sm space-y-8">
|
|
<!-- 모바일용 로고 -->
|
|
<div class="lg:hidden text-center space-y-2">
|
|
<div
|
|
class="inline-flex items-center justify-center w-14 h-14 rounded-2xl bg-green-500 mb-2"
|
|
>
|
|
<UIcon name="i-lucide-tent" class="text-white text-2xl" />
|
|
</div>
|
|
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">
|
|
CampGear
|
|
</h1>
|
|
<p class="text-gray-500 dark:text-gray-400 text-sm">
|
|
캠핑 장비 관리 앱
|
|
</p>
|
|
</div>
|
|
|
|
<!-- 폼 헤더 -->
|
|
<div class="space-y-1">
|
|
<h2 class="text-2xl font-bold text-gray-900 dark:text-white">
|
|
로그인
|
|
</h2>
|
|
<p class="text-gray-500 dark:text-gray-400 text-sm">
|
|
계속하려면 이메일을 입력하거나 Google 계정으로 로그인하세요.
|
|
</p>
|
|
</div>
|
|
|
|
<!-- 로그인 폼 -->
|
|
<div class="space-y-4">
|
|
<!-- Google 로그인 -->
|
|
<UButton
|
|
color="neutral"
|
|
variant="outline"
|
|
class="w-full"
|
|
size="lg"
|
|
@click="signInWithGoogle"
|
|
>
|
|
<template #leading>
|
|
<UIcon name="i-simple-icons-google" class="text-base" />
|
|
</template>
|
|
Google로 계속하기
|
|
</UButton>
|
|
|
|
<!-- 구분선 -->
|
|
<div class="relative flex items-center gap-3">
|
|
<div class="flex-1 h-px bg-gray-200 dark:bg-gray-800" />
|
|
<span class="text-xs text-gray-400 dark:text-gray-500 font-medium"
|
|
>또는 이메일로</span
|
|
>
|
|
<div class="flex-1 h-px bg-gray-200 dark:bg-gray-800" />
|
|
</div>
|
|
|
|
<!-- 이메일 입력 -->
|
|
<div class="space-y-3">
|
|
<UFormField name="email">
|
|
<UInput
|
|
v-model="email"
|
|
type="email"
|
|
placeholder="your@email.com"
|
|
size="lg"
|
|
class="w-full"
|
|
:ui="{ base: 'w-full' }"
|
|
@keyup.enter="sendMagicLink"
|
|
>
|
|
<template #leading>
|
|
<UIcon name="i-lucide-mail" class="text-gray-400" />
|
|
</template>
|
|
</UInput>
|
|
</UFormField>
|
|
|
|
<UButton
|
|
color="primary"
|
|
class="w-full"
|
|
size="lg"
|
|
:loading="loading"
|
|
@click="sendMagicLink"
|
|
>
|
|
<template #leading>
|
|
<UIcon
|
|
v-if="!loading"
|
|
name="i-lucide-send"
|
|
class="text-base"
|
|
/>
|
|
</template>
|
|
매직 링크 전송
|
|
</UButton>
|
|
</div>
|
|
|
|
<!-- 피드백 메시지 -->
|
|
<Transition
|
|
enter-active-class="transition duration-300 ease-out"
|
|
enter-from-class="opacity-0 -translate-y-2"
|
|
enter-to-class="opacity-100 translate-y-0"
|
|
>
|
|
<UAlert
|
|
v-if="message"
|
|
color="success"
|
|
icon="i-lucide-check-circle"
|
|
:description="message"
|
|
class="mt-2"
|
|
/>
|
|
</Transition>
|
|
<Transition
|
|
enter-active-class="transition duration-300 ease-out"
|
|
enter-from-class="opacity-0 -translate-y-2"
|
|
enter-to-class="opacity-100 translate-y-0"
|
|
>
|
|
<UAlert
|
|
v-if="error"
|
|
color="error"
|
|
icon="i-lucide-alert-circle"
|
|
:description="error"
|
|
class="mt-2"
|
|
/>
|
|
</Transition>
|
|
</div>
|
|
|
|
<!-- 안내 문구 -->
|
|
<p
|
|
class="text-center text-xs text-gray-400 dark:text-gray-500 leading-relaxed"
|
|
>
|
|
로그인 시 CampGear의
|
|
<a
|
|
href="#"
|
|
class="text-green-600 dark:text-green-400 hover:underline"
|
|
>이용약관</a
|
|
>
|
|
및
|
|
<a
|
|
href="#"
|
|
class="text-green-600 dark:text-green-400 hover:underline"
|
|
>개인정보처리방침</a
|
|
>에 동의하게 됩니다.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</UApp>
|
|
</template>
|