'use client'; import { useState, useEffect, useRef, useCallback } from 'react'; import Link from 'next/link'; import { createClient } from '@/lib/supabase/client'; // 계좌이체 구매 모달. // - 열릴 때 세션 확인 → 미로그인이면 로그인 유도(구매 폼 미노출) // - 로그인 상태: 입금자명 + 약관 동의 → POST /api/orders // - 주문 금액은 서버가 DB price로 확정한다. 아래 표시 금액은 안내용일 뿐이다. // 접근성: role="dialog" aria-modal, Esc/backdrop 닫기, TopNav 드로어 패턴 차용. const KOR_TIGHT = { letterSpacing: '-0.02em' } as const; const KOR_BODY = { letterSpacing: '-0.01em' } as const; const BANK = { name: '케이뱅크', account: '100-116-337157', holder: '박재오' }; interface Props { product: { id: string; name: string; price: number }; isOpen: boolean; onClose: () => void; } type AuthState = 'checking' | 'guest' | 'user'; interface SuccessInfo { orderId: string; depositorName: string; reused: boolean; } export default function BankTransferModal({ product, isOpen, onClose }: Props) { const [authState, setAuthState] = useState('checking'); const [depositorName, setDepositorName] = useState(''); const [agreed, setAgreed] = useState(false); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(''); const [success, setSuccess] = useState(null); const closeBtnRef = useRef(null); const priceLabel = `₩${product.price.toLocaleString('ko-KR')}`; const loginHref = `/login?next=${encodeURIComponent(`/products/${product.id}`)}`; // 열릴 때마다 상태 초기화 + 세션 확인 useEffect(() => { if (!isOpen) return; let mounted = true; setAuthState('checking'); setDepositorName(''); setAgreed(false); setSubmitting(false); setError(''); setSuccess(null); const supabase = createClient(); supabase.auth .getSession() .then(({ data }) => { if (mounted) setAuthState(data.session?.user ? 'user' : 'guest'); }) .catch(() => { if (mounted) setAuthState('guest'); }); return () => { mounted = false; }; }, [isOpen]); // Esc 닫기 + body 스크롤 잠금 useEffect(() => { if (!isOpen) return; const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); }; window.addEventListener('keydown', onKey); const prevOverflow = document.body.style.overflow; document.body.style.overflow = 'hidden'; return () => { window.removeEventListener('keydown', onKey); document.body.style.overflow = prevOverflow; }; }, [isOpen, onClose]); // 초기 포커스: 모달 열릴 때 닫기 버튼으로 포커스 이동 useEffect(() => { if (isOpen) closeBtnRef.current?.focus(); }, [isOpen]); const handleSubmit = useCallback( async (e: React.FormEvent) => { e.preventDefault(); const name = depositorName.trim(); if (!name || !agreed || submitting) return; setSubmitting(true); setError(''); try { const res = await fetch('/api/orders', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ productId: product.id, depositorName: name }), }); const data = await res.json().catch(() => ({})); if (!res.ok) { if (res.status === 401) { setSubmitting(false); setAuthState('guest'); return; } setError(data?.error || '주문 처리 중 오류가 발생했습니다. 잠시 후 다시 시도해주세요.'); setSubmitting(false); return; } setSuccess({ orderId: data.orderId as string, depositorName: name, reused: Boolean(data.reused), }); } catch { setError('네트워크 오류가 발생했습니다. 잠시 후 다시 시도해주세요.'); } finally { setSubmitting(false); } }, [depositorName, agreed, submitting, product.id], ); if (!isOpen) return null; const canSubmit = depositorName.trim().length > 0 && agreed && !submitting; return (
e.stopPropagation()} className="w-full sm:max-w-md max-h-[92vh] overflow-y-auto rounded-t-2xl sm:rounded-2xl shadow-xl" style={{ background: 'var(--jsm-surface)' }} > {/* 헤더 */}

{success ? '주문 접수 완료' : '계좌이체 구매'}

{/* 상품 요약 */} {!success && (
{product.name} {priceLabel}
)} {/* ── 세션 확인 중 ── */} {authState === 'checking' && !success && (
)} {/* ── 미로그인 ── */} {authState === 'guest' && !success && (

로그인 후 구매할 수 있습니다.

로그인하기
)} {/* ── 로그인 상태: 구매 폼 ── */} {authState === 'user' && !success && (
setDepositorName(e.target.value)} placeholder="입금하실 분의 성함" required maxLength={40} disabled={submitting} className="w-full px-3.5 py-2.5 rounded-lg text-sm outline-none focus-visible:ring-2 focus-visible:ring-[var(--jsm-accent)]" style={{ background: 'var(--jsm-surface)', border: '1px solid var(--jsm-line)', color: 'var(--jsm-ink)', }} />

입금자명이 다르면 확인이 늦어질 수 있습니다.

{error && (
{error}
)}
)} {/* ── 성공 화면 ── */} {success && (

{success.reused ? '이미 접수된 주문이 있습니다' : '주문이 접수되었습니다'}

아래 계좌로 입금해 주세요. 입금이 확인되면 마이페이지에서 다운로드할 수 있습니다.

{[ { k: '입금 계좌', v: `${BANK.name} ${BANK.account}` }, { k: '예금주', v: BANK.holder }, { k: '입금 금액', v: priceLabel }, { k: '입금자명', v: success.depositorName }, ].map((row) => (
{row.k}
{row.v}
))}

입금 확인 후 마이페이지 → 내 제품에서 다운로드할 수 있습니다. 최대 24시간 내 처리됩니다.

마이페이지로
)}
); }