diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 0000000..1d8c2ff --- /dev/null +++ b/.coderabbit.yaml @@ -0,0 +1,52 @@ +language: ko-KR +early_access: false +reviews: + profile: assertive + request_changes_workflow: false + high_level_summary: true + high_level_summary_placeholder: '@coderabbitai summary' + auto_title_placeholder: '@coderabbitai' + poem: false + review_status: true + collapse_walkthrough: false + changed_files_summary: false + sequence_diagrams: false + path_filters: + - '**' + - '!pnpm-lock.yaml' + - '!**/package-lock.json' + - '!**/yarn.lock' + - '!**/*.min.js' + - '!**/*.map' + - '!**/*.snap' + - '!**/test-results/**' + - '!**/playwright-report/**' + - '!**/.next/**' + - '!**/coverage/**' + path_instructions: + - path: '**/*' + instructions: | + Always write review comments in Korean. + + Review priority: + 1) Potential bug or runtime exception + 2) Regression risk (impact on existing behavior) + 3) Missing tests or weak test coverage points + 4) Refactoring points that improve maintainability + 5) Style and formatting suggestions (lowest priority) + + Comment quality bar: + - Explain why it matters and include a concrete failure scenario. + - Avoid preference-only comments unless there is clear user or quality impact. + auto_review: + enabled: true + auto_incremental_review: true + ignore_title_keywords: + - wip + - draft + - do not review + base_branches: + - main + - develop +chat: + auto_reply: true diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 0ec8b5b..a8fed99 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -20,6 +20,7 @@ Provide a clear, concise description of this PR. - [ ] Build passes locally (`pnpm build`) - [ ] No breaking changes, or they are documented below - [ ] Tests added/updated where appropriate +- [ ] CodeRabbit comments reviewed and handled (accepted/deferred/ignored with reason) - [ ] Docs/comments updated where helpful ## Breaking Changes (if any) diff --git a/app/(home)/_section/CTASection.tsx b/app/(home)/_section/CTASection.tsx index 654b8cf..d1064fe 100644 --- a/app/(home)/_section/CTASection.tsx +++ b/app/(home)/_section/CTASection.tsx @@ -4,23 +4,23 @@ import { ArrowRight, Clock } from 'lucide-react'; export default function CTASection() { return ( -
+

나만의 스타일을 무료로 찾아보세요

-

+

런칭 기념 무료 이벤트! 평생 써먹을 수 있는 나만의 스타일 가이드를 지금 받아보세요

-
+
10분 만에 완료
@@ -28,4 +28,4 @@ export default function CTASection() {
); -} \ No newline at end of file +} diff --git a/app/(home)/_section/FAQSection.tsx b/app/(home)/_section/FAQSection.tsx index b9e0a64..35c9449 100644 --- a/app/(home)/_section/FAQSection.tsx +++ b/app/(home)/_section/FAQSection.tsx @@ -25,7 +25,7 @@ export default function FAQSection() { - 총 17개의 질문에 답변하는데 약 10-15분 정도 소요됩니다. 각 질문은 선택형으로 되어있어 + 총 15개의 질문에 답변하는데 약 10-15분 정도 소요됩니다. 각 질문은 선택형으로 되어있어 빠르게 진행할 수 있으며, 결제 완료 후 즉시 진단을 시작할 수 있습니다. 진단 결과는 설문 완료 즉시 확인 가능합니다. diff --git a/app/(home)/_section/HeroSection.tsx b/app/(home)/_section/HeroSection.tsx index 20607bf..5ae7021 100644 --- a/app/(home)/_section/HeroSection.tsx +++ b/app/(home)/_section/HeroSection.tsx @@ -1,101 +1,114 @@ -import { Check, Sparkles, Star } from 'lucide-react'; -import { Card, CardContent } from '@/components/ui/card'; +import { ArrowRight, BadgeCheck, Check, Clock3, LineChart, ShieldCheck, Sparkles } from 'lucide-react'; import Link from 'next/link'; import { Button } from '@/components/ui/button'; export default function HeroSection() { return ( -
-
-
-
-
+
+
+
+
+
-
-
-
- - AI 기반 개인 맞춤 스타일링 -
+
+
+
+
+ + AI 기반 개인 맞춤 스타일링 +
-

- 나만의 -
- - 완벽한 스타일 - -
- 을 찾아보세요 -

+

+ 결제 후 바로 받는 +
+ + 프리미엄 스타일 리포트 + +

-

- 17가지 정밀한 골격 분석으로 당신에게 가장 잘 어울리는 스타일을 찾아드려요. -
- 전문 스타일리스트의 노하우가 담긴 맞춤형 가이드를 받아보세요. -

+

+ 골격 진단 결과를 끝내지 않고, 실제 코디에 바로 쓰는 추천/주의/보완 포인트까지 한 번에 제공합니다. +

- {/* Service Card */} -
- - -
- 👗 -
-

AI 골격진단

-

정확하고 세심한 맞춤 스타일링

-
- 30,000원 - 0원 -
🎉 런칭 이벤트
-
-
    -
  • - - 17문항 정밀 골격 분석 -
  • -
  • - - 개인 맞춤 스타일링 가이드 -
  • -
  • - - 개인 맞춤 패션 제품 추천 -
  • -
  • - - 액세서리 스타일링 팁 -
  • -
  • - - 체형별 코디 가이드 -
  • -
  • - - 카카오톡 채널로 연락주시면 1:1 상담을 통해 더 자세한 정보를 드립니다 -
  • -
- - - -
-
+
+ + + 10분 내 완료 + + + + 개인 맞춤 분석 + + + + 결과 PDF 저장 + +
+ +
+ + + + + 서비스 살펴보기 + +
+ +
+
+

1,000+

+

누적 진단

+
+
+

99%

+

만족도

+
+
+

PDF

+

리포트 제공

+
+
-
-
-
10,000+
-
만족한 고객
+
+
+

RESULT PREVIEW

+ + 분석 완료 + +
+
+

진단 타입

+

웨이브 타입

+

곡선을 살리는 실루엣이 강점을 극대화합니다.

-
-
99%
-
만족도
+ +
+ {[ + '추천: 허리 라인이 보이는 자켓', + '피해야 할 스타일: 과한 박시 핏 상의', + '보완 팁: 목선이 열린 상의로 밸런스 조절', + ].map((item, index) => ( +
+ + {item} +
+ ))}
-
-
무료
-
이벤트 중
+ +
+ + 리포트 PDF로 저장해서 쇼핑할 때 바로 참고
diff --git a/app/(home)/_section/ServiceFeaturesSection.tsx b/app/(home)/_section/ServiceFeaturesSection.tsx index a36b903..c43d5b1 100644 --- a/app/(home)/_section/ServiceFeaturesSection.tsx +++ b/app/(home)/_section/ServiceFeaturesSection.tsx @@ -1,96 +1,80 @@ -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { Card, CardContent } from '@/components/ui/card'; import SectionHeader from '@/components/common/section-header/section-header'; +import { Brain, Palette, Shirt } from 'lucide-react'; export default function ServiceFeaturesSection() { return ( -
+
-
- - - - 💄 - AI 퍼스널 스타일링 - - - -
-
- 30,000원 - 0원 +
+ {[ + { + title: '정밀 골격 분석', + description: '설문 기반으로 체형 강점과 실루엣 특성을 분류합니다.', + icon: , + tone: 'border-rose-200 bg-rose-50/70', + }, + { + title: '개인화 스타일 가이드', + description: '추천/주의/보완 포인트를 실행 가능한 문장으로 제공합니다.', + icon: , + tone: 'border-amber-200 bg-amber-50/70', + }, + { + title: '코디 중심 결과물', + description: '쇼핑과 코디에 바로 쓸 수 있는 요약형 PDF를 생성합니다.', + icon: , + tone: 'border-sky-200 bg-sky-50/70', + }, + ].map((feature) => ( + + +
{feature.icon}
+

{feature.title}

+

{feature.description}

+
+
+ ))} +
+ +
+ + +
+
+

현재 제공 범위

+

AI 퍼스널 스타일링 리포트

-
- 🎉 런칭 기념 무료 이벤트 +
+

정가 30,000원

+

런칭 이벤트 0원

-

- 최신 AI 기술로 정확한 골격 분석을 받고, 전문 스타일리스트가 설계한 맞춤형 스타일링 - 가이드를 무료로 만나보세요! -

-
-
-

- - 포함 서비스 -

-
    -
  • - 👗 - 17문항 정밀 골격 진단 -
  • -
  • - 🎨 - 개인 맞춤 스타일링 가이드 -
  • -
  • - 🛍️ - 개인 맞춤 패션 제품 추천 -
  • -
  • - 💎 - 액세서리 스타일링 팁 -
  • +
    +
    +

    포함 항목

    +
      +
    • 15문항 정밀 골격 진단
    • +
    • 개인 맞춤 스타일링 가이드
    • +
    • 패션 제품 및 액세서리 추천
    -
    -

    - 💎 - 특별 혜택 -

    -
      -
    • - - 즉시 결과 확인 (10분) -
    • -
    • - 📱 - 모바일 최적화 가이드 -
    • -
    • - 👔 - 체형별 코디 가이드 -
    • -
    • - ♾️ - 평생 활용 가능 -
    • +
      +

      이런 분께 추천

      +
        +
      • 옷을 살 때 확신이 필요한 분
      • +
      • 체형에 맞는 실루엣이 궁금한 분
      • +
      • 빠르게 스타일 방향을 잡고 싶은 분
    - -
    -

    - 이런 분께 추천: 나에게 어울리는 스타일을 찾고 싶은 분, 쇼핑할 때 - 확신이 필요한 분, 이미지 변신을 원하는 분 -

    -
    @@ -98,3 +82,4 @@ export default function ServiceFeaturesSection() {
); } + diff --git a/app/(home)/components/contact-modal/ContactModal.tsx b/app/(home)/components/contact-modal/ContactModal.tsx index 2097b06..4b7c40a 100644 --- a/app/(home)/components/contact-modal/ContactModal.tsx +++ b/app/(home)/components/contact-modal/ContactModal.tsx @@ -1,35 +1,205 @@ +import { useMemo, useState } from 'react'; import { Card, CardContent } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; -import { Mail } from 'lucide-react'; +import { Loader2, Send, CircleCheck } from 'lucide-react'; +import { submitContactInquiry } from '@/firebase'; +import { captureAppError } from '@/lib/error-policy'; + +const CONTACT_TOPICS = [ + { value: 'service', label: '서비스 문의' }, + { value: 'payment', label: '결제/환불 문의' }, + { value: 'result', label: '진단 결과 문의' }, + { value: 'etc', label: '기타 문의' }, +] as const; type ContactModalProps = { isOpen?: boolean; - setShowContact: (show: boolean) => void; + setShowContact: () => void; }; export default function ContactModal({ isOpen, setShowContact }: ContactModalProps) { + const [name, setName] = useState(''); + const [email, setEmail] = useState(''); + const [topic, setTopic] = useState<(typeof CONTACT_TOPICS)[number]['value']>('service'); + const [message, setMessage] = useState(''); + const [honeypot, setHoneypot] = useState(''); + const [isSubmitting, setIsSubmitting] = useState(false); + const [isSubmitted, setIsSubmitted] = useState(false); + + const isFormValid = useMemo(() => { + return ( + name.trim().length >= 2 && + /\S+@\S+\.\S+/.test(email) && + message.trim().length >= 10 && + message.trim().length <= 1000 + ); + }, [name, email, message]); + + const handleSubmit = async (event: React.FormEvent) => { + event.preventDefault(); + + if (honeypot.trim().length > 0) { + setShowContact(); + return; + } + + if (!isFormValid || isSubmitting) { + return; + } + + setIsSubmitting(true); + try { + await submitContactInquiry({ + name: name.trim(), + email: email.trim(), + topic, + message: message.trim(), + source: 'floating-button', + userAgent: typeof window === 'undefined' ? '' : window.navigator.userAgent, + }); + setIsSubmitted(true); + } catch (error) { + captureAppError(error, { + layer: 'ui', + feature: 'contact', + action: 'submit-contact-inquiry', + }); + alert('문의 전송에 실패했습니다. 잠시 후 다시 시도해주세요.'); + } finally { + setIsSubmitting(false); + } + }; + if (!isOpen) return null; + return (

문의하기

-
-
-
- - yourmode0604@gmail.com + + {isSubmitted ? ( +
+ +

문의가 접수되었습니다

+

+ 확인 후 등록하신 이메일로 답변드릴게요. +

+
-
+ ) : ( +
+
+ + setHoneypot(event.target.value)} + tabIndex={-1} + autoComplete='off' + /> +
+ +
+ + setName(event.target.value)} + placeholder='홍길동' + maxLength={40} + className='w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:border-rose-400 focus:outline-none' + required + /> +
+ +
+ + setEmail(event.target.value)} + placeholder='name@example.com' + maxLength={100} + className='w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:border-rose-400 focus:outline-none' + required + /> +
+ +
+ + +
+ +
+ +