From b14a32ac62d84258944d7e9578c8736f5eb3e0f7 Mon Sep 17 00:00:00 2001 From: GSB0203 Date: Sat, 19 Jul 2025 23:57:41 +0900 Subject: [PATCH 1/8] =?UTF-8?q?feat=20::=20=ED=8C=9D=EC=97=85=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/components/EmotionResultPopup.tsx | 209 ++++++++++++++++++++++++ app/insert-after/page.tsx | 1 + app/insert-after/reason/page.tsx | 226 +++++++++++--------------- app/insert/reason/page.tsx | 25 ++- 4 files changed, 332 insertions(+), 129 deletions(-) create mode 100644 app/components/EmotionResultPopup.tsx diff --git a/app/components/EmotionResultPopup.tsx b/app/components/EmotionResultPopup.tsx new file mode 100644 index 0000000..44cbedc --- /dev/null +++ b/app/components/EmotionResultPopup.tsx @@ -0,0 +1,209 @@ +'use client' + +import React, { useState } from 'react'; +import { motion, AnimatePresence } from 'framer-motion'; + +interface EmotionData { + step: string; + emotion: { + id: number; + name: string; + type: string; + temp: number; + image: string; + }; + category: string; + memo?: string; +} + +interface EmotionResultPopupProps { + isVisible: boolean; + onClose: () => void; + emotions: EmotionData[]; +} + +const TIME_PERIODS = { + morning: { label: '오전', text: '오전의 감정은' }, + afternoon: { label: '오후', text: '오후의 감정은' }, + evening: { label: '저녁', text: '저녁의 감정은' } +}; + +const getEmotionColor = (type: string) => { + switch (type) { + case 'positive': + return 'bg-red-500'; + case 'negative': + return 'bg-blue-500'; + default: + return 'bg-gray-500'; + } +}; + +const getTemperatureColor = (temp: number) => { + if (temp >= 0) return 'text-red-600'; + return 'text-blue-600'; +}; + +export default function EmotionResultPopup({ isVisible, onClose, emotions }: EmotionResultPopupProps) { + const [currentIndex, setCurrentIndex] = useState(0); + + const nextSlide = () => { + setCurrentIndex((prev) => (prev + 1) % emotions.length); + }; + + const prevSlide = () => { + setCurrentIndex((prev) => (prev - 1 + emotions.length) % emotions.length); + }; + + const currentEmotion = emotions[currentIndex]; + const step = currentEmotion?.step as keyof typeof TIME_PERIODS; + + if (!isVisible || !currentEmotion) return null; + + return ( + + + e.stopPropagation()} + > + {/* 닫기 버튼 */} + + + {/* 슬라이드 컨테이너 */} +
+ + {/* 시간대 표시 */} +
+ + {TIME_PERIODS[step]?.label} + +
+ + {/* 감정 카드 */} +
+
+ {/* 온도 */} +
+ + {currentEmotion.emotion.temp > 0 ? '+' : ''}{currentEmotion.emotion.temp}° + +
+ + {/* 감정 아이콘 */} +
+
+ {currentEmotion.emotion.name} +
+
+
+ + {/* 감정 설명 */} +
+

+ {TIME_PERIODS[step]?.text} {currentEmotion.emotion.name}으로 {currentEmotion.emotion.temp > 0 ? '+' : ''}{currentEmotion.emotion.temp}° 예정이에요. +

+
+
+ + {/* 메모 (있는 경우) */} + {currentEmotion.memo && ( +
+

+ "{currentEmotion.memo}" +

+
+ )} + + {/* 감정 카테고리 */} +
+ + {currentEmotion.category} + +
+
+
+ + {/* 네비게이션 */} + {emotions.length > 1 && ( +
+ + + {/* 인디케이터 */} +
+ {emotions.map((_, index) => ( +
+ + +
+ )} + + {/* 완료 버튼 */} +
+ +
+
+
+
+ ); +} \ No newline at end of file diff --git a/app/insert-after/page.tsx b/app/insert-after/page.tsx index fe1128a..01d8fde 100644 --- a/app/insert-after/page.tsx +++ b/app/insert-after/page.tsx @@ -7,6 +7,7 @@ import { useChild } from "../contexts/ChildContext"; import { EmotionType, TIME_PERIODS, TimeSlot } from "../types/common"; import { getCurrentDate } from "../utils/dateUtils"; import EmotionSelector from "../components/EmotionSelector"; +import EmotionResultPopup from "../components/EmotionResultPopup"; function InsertAfterPageContent() { const { selectedChild } = useChild(); diff --git a/app/insert-after/reason/page.tsx b/app/insert-after/reason/page.tsx index 4bf9885..a3dbbf7 100644 --- a/app/insert-after/reason/page.tsx +++ b/app/insert-after/reason/page.tsx @@ -5,6 +5,7 @@ import { motion } from "framer-motion"; import { useRouter, useSearchParams } from "next/navigation"; import Button from "../../components/Button"; import { useChild } from "../../contexts/ChildContext"; +import EmotionResultPopup from "../../components/EmotionResultPopup"; const apiBaseUrl = process.env.NEXT_PUBLIC_API_BASE_URL; @@ -33,7 +34,8 @@ function ReasonPageContent() { const [reason, setReason] = useState(''); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); - const [showCompletionModal, setShowCompletionModal] = useState(false); + const [showResultPopup, setShowResultPopup] = useState(false); + const [allEmotions, setAllEmotions] = useState([]); const router = useRouter(); const searchParams = useSearchParams(); const forecastId = searchParams.get('forecastId'); @@ -48,7 +50,7 @@ function ReasonPageContent() { if (typeof window !== 'undefined') { const savedEmotions = localStorage.getItem('forecastRecordEmotions'); if (savedEmotions) { - try { + try { const emotions = JSON.parse(savedEmotions); console.log('저장된 감정 데이터:', emotions); @@ -63,11 +65,11 @@ function ReasonPageContent() { console.error('현재 단계의 감정 데이터를 찾을 수 없습니다.'); setError('감정 데이터를 찾을 수 없습니다.'); } - } catch (error) { - console.error('감정 데이터 파싱 오류:', error); + } catch (error) { + console.error('감정 데이터 파싱 오류:', error); setError('감정 데이터를 불러오는데 실패했습니다.'); - } - } else { + } + } else { console.error('저장된 감정 데이터가 없습니다.'); setError('감정 데이터를 찾을 수 없습니다.'); } @@ -75,7 +77,15 @@ function ReasonPageContent() { }, [searchParams]); const handleBack = () => { - window.history.back(); + const steps = ['morning', 'afternoon', 'evening']; + const currentIndex = steps.indexOf(currentStep); + const prevStep = steps[currentIndex - 1]; + + if (prevStep) { + router.push(`/insert-after?step=${prevStep}&forecastId=${searchParams.get('forecastId')}&date=${searchParams.get('date')}&timeZone=${searchParams.get('timeZone')}`); + } else { + window.history.back(); + } }; const handleNext = async () => { @@ -88,26 +98,17 @@ function ReasonPageContent() { setIsLoading(true); setError(null); - const forecastDate = searchParams.get('date') || new Date().toISOString().split('T')[0]; - const originalTimeZone = searchParams.get('timeZone'); - const currentTimeZone = TIME_PERIODS[currentStep].label; - - console.log('디버깅 정보:', { - currentStep, - originalTimeZone, - currentTimeZone, - forecastDate, - forecastId - }); - - // 현재 단계의 예보 기록 생성 + const date = searchParams.get('date'); + const timeZone = searchParams.get('timeZone'); + + // 예보 기록 생성 const recordData = { - forecastId: Number(forecastId), - childId: selectedChild.id, + forecastId: parseInt(forecastId), emotionTypeId: currentEmotion.emotion.id, - date: forecastDate, - timeZone: originalTimeZone || currentTimeZone, - memo: reason.trim() + memo: reason.trim(), + childId: selectedChild.id, + date: date, + timeZone: timeZone }; console.log(`${currentStep} 예보 기록 생성:`, recordData); @@ -132,31 +133,17 @@ function ReasonPageContent() { const nextStep = steps[currentIndex + 1]; if (nextStep) { - // localStorage에서 모든 forecastId 가져오기 - let allForecastIds = {}; - if (typeof window !== 'undefined') { - const savedForecastIds = localStorage.getItem('allForecastIds'); - if (savedForecastIds) { - allForecastIds = JSON.parse(savedForecastIds); - } - } - - // 다음 단계의 올바른 forecastId 사용 - const nextForecastId = allForecastIds[nextStep as keyof typeof allForecastIds] || forecastId; - const nextTimeZone = TIME_PERIODS[nextStep as keyof typeof TIME_PERIODS].label; - - console.log('다음 단계 이동:', { - currentStep, - nextStep, - currentForecastId: forecastId, - nextForecastId, - nextTimeZone - }); - - router.push(`/insert-after?step=${nextStep}&forecastId=${nextForecastId}&date=${searchParams.get('date')}&timeZone=${nextTimeZone}`); + router.push(`/insert-after?step=${nextStep}&forecastId=${forecastId}&date=${date}&timeZone=${timeZone}`); } else { - // 모든 단계 완료 - setShowCompletionModal(true); + // 모든 단계 완료 - 결과 팝업 표시 + const savedEmotions = localStorage.getItem('forecastRecordEmotions'); + if (savedEmotions) { + const emotions = JSON.parse(savedEmotions); + setAllEmotions(emotions); + setShowResultPopup(true); + } else { + router.push('/home'); + } } } catch (error) { console.error('예보 기록 생성 실패:', error); @@ -186,7 +173,7 @@ function ReasonPageContent() {

감정 데이터를 찾을 수 없습니다.

-
- -
-
- {searchParams.get('date') || new Date().toISOString().split('T')[0]} {TIME_PERIODS[currentStep].label} -
-
- {TIME_PERIODS[currentStep].text}{`\n`}느꼈나요? -
- - {error && ( -
- {error} -
- )} - -
-
-