Files
jaengseung-made/app/saju/result/SajuAISection.tsx
2026-03-11 07:46:21 +09:00

163 lines
5.9 KiB
TypeScript

'use client';
import { useState, useEffect, useRef } from 'react';
import PaymentButton from '@/app/components/PaymentButton';
interface BirthKey {
birth_year: number;
birth_month: number;
birth_day: number;
birth_hour?: number;
gender: string;
}
interface SajuAISectionProps {
hasPaid: boolean;
savedInterpretation: string | null;
sajuData: object;
daeun: object | null;
daeunList: object[];
gender: string;
birthKey: BirthKey;
currentUrl: string;
// Python 엔진 데이터 (더 정밀한 절기 계산 결과)
engineData?: {
interactions?: any[];
shinsal?: any[];
gongmang?: any;
hiddenStems?: any[];
};
}
export default function SajuAISection({
hasPaid,
savedInterpretation,
sajuData,
daeun,
daeunList,
gender,
birthKey,
currentUrl,
engineData,
}: SajuAISectionProps) {
const [status, setStatus] = useState<'idle' | 'loading' | 'done' | 'error'>(
savedInterpretation ? 'done' : 'idle'
);
const [interpretation, setInterpretation] = useState(savedInterpretation ?? '');
const called = useRef(false);
useEffect(() => {
if (!hasPaid || savedInterpretation || called.current) return;
called.current = true;
setStatus('loading');
fetch('/api/saju/analyze', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
saju: sajuData,
daeun,
daeunList,
gender,
engineData, // Python 엔진 데이터 전달
}),
})
.then((r) => r.json())
.then((data) => {
if (data.interpretation) {
setInterpretation(data.interpretation);
setStatus('done');
// birthKey 유효성 검사 후 저장 (NaN/null 방지)
const { birth_year, birth_month, birth_day } = birthKey;
if (
typeof birth_year === 'number' && !isNaN(birth_year) &&
typeof birth_month === 'number' && !isNaN(birth_month) &&
typeof birth_day === 'number' && !isNaN(birth_day)
) {
fetch('/api/saju/save-interpretation', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ interpretation: data.interpretation, birthKey }),
}).catch(() => {});
}
} else {
setStatus('error');
}
})
.catch(() => setStatus('error'));
}, [hasPaid]);
// 미결제 상태
if (!hasPaid) {
return (
<div className="bg-gradient-to-br from-[#04102b] via-[#0a1f5c] to-[#04102b] rounded-2xl border border-[#1a3a7a] p-7 text-center relative overflow-hidden">
<div className="absolute inset-0 opacity-[0.05]"
style={{ backgroundImage: 'radial-gradient(circle, #a78bfa 1px, transparent 1px)', backgroundSize: '22px 22px' }} />
<div className="relative">
<div className="inline-flex items-center gap-2 bg-amber-400/10 border border-amber-400/25 text-amber-300 text-xs font-semibold px-3 py-1 rounded-full mb-3">
AI PREMIUM
</div>
<h3 className="text-xl font-extrabold text-white mb-2">AI (12 )</h3>
<p className="text-blue-200/60 text-sm mb-5">
, , , , , <br />
GPT-4o가 .
</p>
<PaymentButton
productId="saju_detail"
returnUrl={currentUrl}
className="inline-flex items-center gap-2 bg-gradient-to-r from-amber-500 to-amber-400 hover:from-amber-400 hover:to-amber-300 text-[#04102b] font-bold px-7 py-3 rounded-xl transition-all shadow-lg"
>
AI · 4,900
</PaymentButton>
</div>
</div>
);
}
// AI 생성 중
if (status === 'loading') {
return (
<div className="bg-white rounded-2xl border border-[#dbe8ff] p-8 text-center">
<div className="w-10 h-10 border-2 border-violet-600 border-t-transparent rounded-full animate-spin mx-auto mb-4" />
<p className="text-slate-500 text-sm font-medium">AI가 ...</p>
<p className="text-slate-400 text-xs mt-1"> 20~30 </p>
</div>
);
}
// 오류
if (status === 'error') {
return (
<div className="bg-white rounded-2xl border border-red-200 p-6 text-center">
<p className="text-red-500 text-sm font-medium mb-3">AI .</p>
<button
onClick={() => { called.current = false; setStatus('idle'); }}
className="text-xs text-blue-600 underline"
>
</button>
</div>
);
}
// AI 해석 완료
return (
<div className="bg-white rounded-2xl border border-[#dbe8ff] p-6">
<div className="flex items-center gap-2 mb-5 pb-4 border-b border-slate-100">
<div className="w-7 h-7 rounded-lg bg-gradient-to-br from-violet-500 to-amber-500 flex items-center justify-center">
<svg className="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z" />
</svg>
</div>
<h2 className="text-lg font-extrabold text-[#04102b]">AI </h2>
<span className="ml-auto text-xs bg-emerald-50 border border-emerald-200 text-emerald-700 font-bold px-2 py-0.5 rounded-full">
</span>
</div>
<div className="prose prose-sm max-w-none text-slate-700 leading-relaxed whitespace-pre-wrap">
{interpretation}
</div>
</div>
);
}