'use client'; import { useState, useEffect, useRef } from 'react'; import { Box, Container, Typography, Card, CardContent, Button, Grid, Chip, TextField, Radio, RadioGroup, FormControlLabel, IconButton, CircularProgress, Alert, } from '@mui/material'; import { ArrowBack, Edit, Insights } from '@mui/icons-material'; import { eventApi } from '@/shared/api'; import type { AiRecommendationResult, EventRecommendation } from '@/shared/api/eventApi'; // 디자인 시스템 색상 const colors = { pink: '#F472B6', purple: '#C084FC', purpleLight: '#E9D5FF', blue: '#60A5FA', mint: '#34D399', orange: '#FB923C', yellow: '#FBBF24', gray: { 900: '#1A1A1A', 700: '#4A4A4A', 500: '#9E9E9E', 300: '#D9D9D9', 100: '#F5F5F5', }, }; interface RecommendationStepProps { eventId?: string; // 이전 단계에서 생성된 eventId onNext: (data: { recommendation: EventRecommendation; eventId: string }) => void; onBack: () => void; } // 쿠키에서 값 가져오기 const getCookie = (name: string): string | null => { if (typeof document === 'undefined') return null; const value = `; ${document.cookie}`; const parts = value.split(`; ${name}=`); if (parts.length === 2) { return parts.pop()?.split(';').shift() || null; } return null; }; export default function RecommendationStep({ eventId: initialEventId, onNext, onBack, }: RecommendationStepProps) { const [eventId, setEventId] = useState(initialEventId || null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [aiResult, setAiResult] = useState(null); const [selected, setSelected] = useState(null); const [editedData, setEditedData] = useState< Record >({}); // 중복 호출 방지를 위한 ref const requestedEventIdRef = useRef(null); // 컴포넌트 마운트 시 AI 추천 결과 조회 useEffect(() => { // props에서만 eventId를 받음 if (initialEventId) { // 이미 요청한 eventId면 중복 요청하지 않음 if (requestedEventIdRef.current === initialEventId) { console.log('⚠️ 이미 요청한 eventId입니다. 중복 요청 방지:', initialEventId); return; } requestedEventIdRef.current = initialEventId; setEventId(initialEventId); console.log('✅ RecommendationStep - eventId 설정:', initialEventId); // eventId가 있으면 바로 AI 추천 결과 조회 fetchAIRecommendations(initialEventId); } else { console.error('❌ eventId가 없습니다. ObjectiveStep으로 돌아가세요.'); setError('이벤트 ID가 없습니다. 이전 단계로 돌아가서 다시 시도하세요.'); } }, [initialEventId]); const fetchAIRecommendations = async (evtId: string) => { try { setLoading(true); setError(null); console.log('📡 AI 추천 요청 시작, eventId:', evtId); // POST /events/{eventId}/ai-recommendations 엔드포인트로 AI 추천 요청 const recommendations = await eventApi.requestAiRecommendations(evtId); console.log('✅ AI 추천 요청 성공:', recommendations); setAiResult(recommendations); setLoading(false); } catch (err: any) { console.error('❌ AI 추천 요청 실패:', err); const errorMessage = err.response?.data?.message || err.response?.data?.error || 'AI 추천을 생성하는데 실패했습니다'; setError(errorMessage); setLoading(false); } }; const handleNext = async () => { if (selected === null || !aiResult || !eventId) return; const selectedRec = aiResult.recommendations[selected - 1]; const edited = editedData[selected]; try { setLoading(true); // AI 추천 선택 API 호출 await eventApi.selectRecommendation(eventId, { recommendationId: `${eventId}-opt${selected}`, customizations: { eventName: edited?.title || selectedRec.title, description: edited?.description || selectedRec.description, }, }); // 다음 단계로 이동 onNext({ recommendation: { ...selectedRec, title: edited?.title || selectedRec.title, description: edited?.description || selectedRec.description, }, eventId, }); } catch (err: any) { console.error('추천 선택 실패:', err); setError(err.response?.data?.message || '추천 선택에 실패했습니다'); } finally { setLoading(false); } }; const handleEditTitle = (optionNumber: number, title: string) => { setEditedData((prev) => ({ ...prev, [optionNumber]: { ...prev[optionNumber], title, }, })); }; const handleEditDescription = (optionNumber: number, description: string) => { setEditedData((prev) => ({ ...prev, [optionNumber]: { ...prev[optionNumber], description, }, })); }; // 로딩 상태 표시 if (loading) { return ( AI 이벤트 추천 AI가 최적의 이벤트를 생성하고 있습니다... 업종, 지역, 시즌 트렌드를 분석하여 맞춤형 이벤트를 추천합니다 ); } // 에러 상태 표시 if (error) { return ( AI 이벤트 추천 {error} ); } // AI 결과가 없으면 로딩 표시 if (!aiResult) { return ( ); } return ( {/* Header */} AI 이벤트 추천 {/* Trends Analysis */} AI 트렌드 분석 📍 업종 트렌드 {aiResult.trendAnalysis.industryTrends.slice(0, 3).map((trend, idx) => ( • {trend.description} ))} 🗺️ 지역 트렌드 {aiResult.trendAnalysis.regionalTrends.slice(0, 3).map((trend, idx) => ( • {trend.description} ))} ☀️ 시즌 트렌드 {aiResult.trendAnalysis.seasonalTrends.slice(0, 3).map((trend, idx) => ( • {trend.description} ))} {/* AI Recommendations */} AI 추천 이벤트 ({aiResult.recommendations.length}가지 옵션) 각 옵션은 차별화된 컨셉으로 구성되어 있습니다. 원하시는 옵션을 선택하고 수정할 수 있습니다. {/* Recommendations */} setSelected(Number(e.target.value))}> {aiResult.recommendations.map((rec) => ( setSelected(rec.optionNumber)} > } label="" sx={{ m: 0 }} /> handleEditTitle(rec.optionNumber, e.target.value)} onClick={(e) => e.stopPropagation()} sx={{ mb: { xs: 2, sm: 4 } }} InputProps={{ endAdornment: , sx: { fontSize: { xs: '0.9375rem', sm: '1.1rem' }, fontWeight: 600, py: { xs: 1.5, sm: 2 } }, }} /> handleEditDescription(rec.optionNumber, e.target.value)} onClick={(e) => e.stopPropagation()} sx={{ mb: { xs: 2, sm: 4 } }} InputProps={{ sx: { fontSize: { xs: '0.875rem', sm: '1rem' } }, }} /> 타겟 고객 {rec.targetAudience} 예상 비용 {(rec.estimatedCost.min / 10000).toFixed(0)}~ {(rec.estimatedCost.max / 10000).toFixed(0)}만원 예상 신규 고객 {rec.expectedMetrics.newCustomers.min}~ {rec.expectedMetrics.newCustomers.max}명 ROI {rec.expectedMetrics.roi.min}~{rec.expectedMetrics.roi.max}% 차별점 {rec.differentiator} ))} {/* Action Buttons */} ); }