feat: add MCP configuration and update login page design
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.
This commit is contained in:
2026-03-08 17:26:33 +09:00
parent e66321386a
commit d594bf5a67
5 changed files with 1677 additions and 80 deletions

View File

@@ -5,5 +5,11 @@
"mcp__context7__query-docs", "mcp__context7__query-docs",
"Bash(npx nuxi@latest:*)" "Bash(npx nuxi@latest:*)"
] ]
} },
"disabledMcpjsonServers": [
"context7",
"playwright",
"sequential-thinking",
"shadcn"
]
} }

30
.mcp.json Normal file
View File

@@ -0,0 +1,30 @@
{
"mcpServers": {
"context7": {
"type": "http",
"url": "https://mcp.context7.com/mcp"
},
"playwright": {
"type": "stdio",
"command": "cmd",
"args": ["/c", "npx", "@playwright/mcp@latest"],
"env": {}
},
"sequential-thinking": {
"type": "stdio",
"command": "cmd",
"args": [
"/c",
"npx",
"-y",
"@modelcontextprotocol/server-sequential-thinking"
],
"env": {}
},
"shadcn": {
"type": "stdio",
"command": "cmd",
"args": ["/c", "npx", "shadcn@latest", "mcp"]
}
}
}

View File

@@ -1,108 +1,257 @@
<script setup lang="ts"> <script setup lang="ts">
definePageMeta({ layout: false }) definePageMeta({ layout: false });
const supabase = useSupabaseClient() const supabase = useSupabaseClient();
const email = ref('') const email = ref("");
const loading = ref(false) const loading = ref(false);
const message = ref('') const message = ref("");
const error = ref('') const error = ref("");
async function sendMagicLink() { async function sendMagicLink() {
if (!email.value) return if (!email.value) return;
loading.value = true loading.value = true;
error.value = '' error.value = "";
message.value = '' message.value = "";
const { error: err } = await supabase.auth.signInWithOtp({ const { error: err } = await supabase.auth.signInWithOtp({
email: email.value, email: email.value,
options: { options: {
emailRedirectTo: `${window.location.origin}/confirm` emailRedirectTo: `${window.location.origin}/confirm`,
} },
}) });
loading.value = false loading.value = false;
if (err) { if (err) {
error.value = err.message error.value = err.message;
} else { } else {
message.value = `${email.value}로 로그인 링크를 전송했습니다. 이메일을 확인해주세요.` message.value = `${email.value}로 로그인 링크를 전송했습니다. 이메일을 확인해주세요.`;
} }
} }
async function signInWithGoogle() { async function signInWithGoogle() {
await supabase.auth.signInWithOAuth({ await supabase.auth.signInWithOAuth({
provider: 'google', provider: "google",
options: { options: {
redirectTo: `${window.location.origin}/confirm` redirectTo: `${window.location.origin}/confirm`,
} },
}) });
} }
</script> </script>
<template> <template>
<UApp> <UApp>
<div class="min-h-screen flex items-center justify-center bg-gradient-to-br from-green-50 to-emerald-100 dark:from-gray-900 dark:to-gray-800 p-4"> <div class="min-h-screen flex">
<UCard class="w-full max-w-md"> <!-- 좌측 브랜딩 패널 (데스크탑에서만 표시) -->
<!-- Logo --> <div
<template #header> 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="text-center py-4"> >
<div class="flex items-center justify-center gap-2 mb-2"> <!-- 배경 패턴 -->
<UIcon name="i-lucide-tent" class="text-4xl text-primary-500" /> <div class="absolute inset-0 opacity-10">
<h1 class="text-3xl font-bold text-gray-900 dark:text-white">CampGear</h1> <div
</div> class="absolute top-0 left-0 w-64 h-64 rounded-full bg-white -translate-x-1/2 -translate-y-1/2"
<p class="text-gray-500 dark:text-gray-400">캠핑 장비 관리 </p>
</div>
</template>
<div class="space-y-4 py-2">
<!-- Magic Link -->
<div class="space-y-3">
<UFormField label="이메일" name="email">
<UInput
v-model="email"
type="email"
placeholder="your@email.com"
class="w-full"
@keyup.enter="sendMagicLink"
/>
</UFormField>
<UButton
label="매직 링크로 로그인"
icon="i-lucide-mail"
class="w-full"
:loading="loading"
@click="sendMagicLink"
/>
</div>
<!-- Divider -->
<USeparator label="또는" />
<!-- Google OAuth -->
<UButton
label="Google로 로그인"
icon="i-simple-icons-google"
color="neutral"
variant="outline"
class="w-full"
@click="signInWithGoogle"
/> />
<div
<!-- Feedback --> class="absolute bottom-0 right-0 w-96 h-96 rounded-full bg-white translate-x-1/3 translate-y-1/3"
<UAlert
v-if="message"
color="success"
icon="i-lucide-check-circle"
:description="message"
/> />
<UAlert <div
v-if="error" class="absolute top-1/2 left-1/2 w-48 h-48 rounded-full bg-white -translate-x-1/2 -translate-y-1/2"
color="error"
icon="i-lucide-alert-circle"
:description="error"
/> />
</div> </div>
</UCard>
<!-- 로고 -->
<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">
&copy; 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> </div>
</UApp> </UApp>
</template> </template>

View File

@@ -24,6 +24,7 @@
"@nuxt/eslint": "^1.15.2", "@nuxt/eslint": "^1.15.2",
"@types/node": "^22.13.10", "@types/node": "^22.13.10",
"eslint": "^10.0.2", "eslint": "^10.0.2",
"shadcn": "^4.0.0",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"vue-tsc": "^3.2.5" "vue-tsc": "^3.2.5"
}, },

1411
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff