diff --git a/src/components/modals/ExerciseModal.tsx b/src/components/modals/ExerciseModal.tsx index 56feaac..5185eb2 100644 --- a/src/components/modals/ExerciseModal.tsx +++ b/src/components/modals/ExerciseModal.tsx @@ -600,6 +600,7 @@ const ExerciseModal: React.FC = ({ const hasNextSequence = hasSequenceControls && sequenceIndex! < sequenceLength - 1; const isLastSequence = hasSequenceControls && sequenceIndex === sequenceLength - 1; + const canNextExercise = hasNextSequence && allSetsCompleted; const canFinishWorkout = isLastSequence && allSetsCompleted; const isExerciseSelected = (exercise: any) => { @@ -2053,6 +2054,16 @@ const getExerciseDisplayName = React.useCallback( + + + + + + {sets.map((set) => ( ))} - - {allSetsCompleted && (() => { - const currentExerciseName = getExerciseDisplayName( - selectedExercise || exerciseData || { name: "" } - ); - const currentFeedback = exerciseFeedbacks[currentExerciseName] || { - intensity: null, - feedback: null, - }; - - const handleIntensityClick = (value: "heavy" | "light") => { - setExerciseFeedbacks((prev) => { - const newFeedback = { - ...prev[currentExerciseName], - intensity: - prev[currentExerciseName]?.intensity === value - ? null - : value, - feedback: prev[currentExerciseName]?.feedback || null, - }; - const updated = { - ...prev, - [currentExerciseName]: newFeedback, - }; - // ExerciseScreen에 피드백 업데이트 전달 - if (onFeedbackUpdate) { - onFeedbackUpdate(currentExerciseName, newFeedback); - } - return updated; - }); - }; - - const handleFeedbackClick = (value: "like" | "dislike") => { - setExerciseFeedbacks((prev) => { - const newFeedback = { - ...prev[currentExerciseName], - intensity: prev[currentExerciseName]?.intensity || null, - feedback: - prev[currentExerciseName]?.feedback === value - ? null - : value, - }; - const updated = { - ...prev, - [currentExerciseName]: newFeedback, - }; - // ExerciseScreen에 피드백 업데이트 전달 - if (onFeedbackUpdate) { - onFeedbackUpdate(currentExerciseName, newFeedback); - } - return updated; - }); - }; - - if (isCompleted) { - return null; - } - if (isCompleted) { - return null; - } - return ( - - 이 운동 어땠나요? - - handleIntensityClick("heavy")} - > - - 무거워요 - - - handleIntensityClick("light")} - > - - 가벼워요 - - - handleFeedbackClick("like")} - > - - 좋아요 - - - handleFeedbackClick("dislike")} - > - - 싫어요 - - - - - ); - })()} - - - - - - @@ -2237,6 +2098,7 @@ const getExerciseDisplayName = React.useCallback( style={[ styles.footer, hasSequenceControls && styles.footerExtended, + allSetsCompleted && !isCompleted && styles.footerWithFeedback, ]} > {hasSequenceControls && ( @@ -2277,15 +2139,15 @@ const getExerciseDisplayName = React.useCallback( handleSequenceNavigatePress("next")} - disabled={!hasNextSequence} + disabled={!canNextExercise} > 다음 운동 @@ -2310,6 +2172,139 @@ const getExerciseDisplayName = React.useCallback( 운동 끝내기 + {allSetsCompleted && !isCompleted && (() => { + const currentExerciseName = getExerciseDisplayName( + selectedExercise || exerciseData || { name: "" } + ); + const currentFeedback = exerciseFeedbacks[currentExerciseName] || { + intensity: null, + feedback: null, + }; + + const handleIntensityClick = (value: "heavy" | "light") => { + setExerciseFeedbacks((prev) => { + const newFeedback = { + ...prev[currentExerciseName], + intensity: + prev[currentExerciseName]?.intensity === value + ? null + : value, + feedback: prev[currentExerciseName]?.feedback || null, + }; + const updated = { + ...prev, + [currentExerciseName]: newFeedback, + }; + // ExerciseScreen에 피드백 업데이트 전달 + if (onFeedbackUpdate) { + onFeedbackUpdate(currentExerciseName, newFeedback); + } + return updated; + }); + }; + + const handleFeedbackClick = (value: "like" | "dislike") => { + setExerciseFeedbacks((prev) => { + const newFeedback = { + ...prev[currentExerciseName], + intensity: prev[currentExerciseName]?.intensity || null, + feedback: + prev[currentExerciseName]?.feedback === value + ? null + : value, + }; + const updated = { + ...prev, + [currentExerciseName]: newFeedback, + }; + // ExerciseScreen에 피드백 업데이트 전달 + if (onFeedbackUpdate) { + onFeedbackUpdate(currentExerciseName, newFeedback); + } + return updated; + }); + }; + + return ( + + 이 운동 어땠나요? + + handleIntensityClick("heavy")} + > + + 무거워요 + + + handleIntensityClick("light")} + > + + 가벼워요 + + + handleFeedbackClick("like")} + > + + 좋아요 + + + handleFeedbackClick("dislike")} + > + + 싫어요 + + + + + ); + })()} )} @@ -2633,6 +2628,16 @@ const getExerciseDisplayName = React.useCallback( + + + + + + {sets.map((set) => ( ))} + + + - {allSetsCompleted && (() => { - const currentExerciseName = getExerciseDisplayName( - selectedExercise || exerciseData || { name: "" } - ); - const currentFeedback = exerciseFeedbacks[currentExerciseName] || { - intensity: null, - feedback: null, - }; - - const handleIntensityClick = (value: "heavy" | "light") => { - setExerciseFeedbacks((prev) => { - const newFeedback = { - ...prev[currentExerciseName], - intensity: - prev[currentExerciseName]?.intensity === value - ? null - : value, - feedback: prev[currentExerciseName]?.feedback || null, - }; - const updated = { - ...prev, - [currentExerciseName]: newFeedback, - }; - // ExerciseScreen에 피드백 업데이트 전달 - if (onFeedbackUpdate) { - onFeedbackUpdate(currentExerciseName, newFeedback); - } - return updated; - }); - }; - - const handleFeedbackClick = (value: "like" | "dislike") => { - setExerciseFeedbacks((prev) => { - const newFeedback = { - ...prev[currentExerciseName], - intensity: prev[currentExerciseName]?.intensity || null, - feedback: - prev[currentExerciseName]?.feedback === value - ? null - : value, - }; - const updated = { - ...prev, - [currentExerciseName]: newFeedback, - }; - // ExerciseScreen에 피드백 업데이트 전달 - if (onFeedbackUpdate) { - onFeedbackUpdate(currentExerciseName, newFeedback); - } - return updated; - }); - }; - - return ( + + + + 운동 끝내기 + + + {allSetsCompleted && !isCompleted && (() => { + const currentExerciseName = getExerciseDisplayName( + selectedExercise || exerciseData || { name: "" } + ); + const currentFeedback = exerciseFeedbacks[currentExerciseName] || { + intensity: null, + feedback: null, + }; + + const handleIntensityClick = (value: "heavy" | "light") => { + setExerciseFeedbacks((prev) => { + const newFeedback = { + ...prev[currentExerciseName], + intensity: + prev[currentExerciseName]?.intensity === value + ? null + : value, + feedback: prev[currentExerciseName]?.feedback || null, + }; + const updated = { + ...prev, + [currentExerciseName]: newFeedback, + }; + // ExerciseScreen에 피드백 업데이트 전달 + if (onFeedbackUpdate) { + onFeedbackUpdate(currentExerciseName, newFeedback); + } + return updated; + }); + }; + + const handleFeedbackClick = (value: "like" | "dislike") => { + setExerciseFeedbacks((prev) => { + const newFeedback = { + ...prev[currentExerciseName], + intensity: prev[currentExerciseName]?.intensity || null, + feedback: + prev[currentExerciseName]?.feedback === value + ? null + : value, + }; + const updated = { + ...prev, + [currentExerciseName]: newFeedback, + }; + // ExerciseScreen에 피드백 업데이트 전달 + if (onFeedbackUpdate) { + onFeedbackUpdate(currentExerciseName, newFeedback); + } + return updated; + }); + }; + + return ( 이 운동 어땠나요? - handleIntensityClick("heavy")} + > + handleIntensityClick("heavy")} > - - 무거워요 - + 무거워요 + - handleIntensityClick("light")} + > + handleIntensityClick("light")} > - - 가벼워요 - + 가벼워요 + - handleFeedbackClick("like")} + > + handleFeedbackClick("like")} > - - 좋아요 - + 좋아요 + - handleFeedbackClick("dislike")} + > + handleFeedbackClick("dislike")} > - - 싫어요 - + 싫어요 + - ); - })()} - - - - - - - - - - - - - - 운동 끝내기 - - + ); + })()} )} @@ -3203,13 +3202,13 @@ const styles = StyleSheet.create({ }, addSetButtonWrapper: { alignItems: "center", - marginTop: 12, - marginBottom: 8, + marginTop: 0, + marginBottom: 12, }, addSetCircleButton: { - width: 36, - height: 36, - borderRadius: 18, + width: 32, + height: 32, + borderRadius: 16, backgroundColor: "#e3ff7c", justifyContent: "center", alignItems: "center", @@ -3220,9 +3219,9 @@ const styles = StyleSheet.create({ elevation: 3, }, feedbackSection: { - marginTop: 24, - marginHorizontal: 20, - marginBottom: 24, + marginTop: 12, + marginHorizontal: 0, + marginBottom: 0, }, feedbackTitle: { fontSize: 18, @@ -3388,7 +3387,7 @@ const styles = StyleSheet.create({ }, endWorkoutBtn: { backgroundColor: "#404040", - paddingVertical: 16, + paddingVertical: 10, borderRadius: 12, alignItems: "center", }, @@ -3828,13 +3827,16 @@ const styles = StyleSheet.create({ }, footer: { paddingHorizontal: 20, - paddingBottom: 20, - paddingTop: 14, + paddingBottom: 8, + paddingTop: 8, backgroundColor: "#2a2a2a", }, footerExtended: { flexDirection: "column", - gap: 12, + gap: 6, + }, + footerWithFeedback: { + paddingBottom: 16, }, saveExerciseBtn: { backgroundColor: "#e3ff7c", @@ -3893,7 +3895,7 @@ const styles = StyleSheet.create({ flex: 1, borderRadius: 12, backgroundColor: "#272727", - paddingVertical: 14, + paddingVertical: 8, alignItems: "center", justifyContent: "center", }, diff --git a/src/screens/analysis/AnalysisScreen.tsx b/src/screens/analysis/AnalysisScreen.tsx index 67ff54c..18a2b60 100644 --- a/src/screens/analysis/AnalysisScreen.tsx +++ b/src/screens/analysis/AnalysisScreen.tsx @@ -272,6 +272,10 @@ const AnalysisScreen = ({ navigation }: any) => { // 프리미엄 여부 state const [isPremium, setIsPremium] = useState(false); + // 코멘트 state + const [scoreComment, setScoreComment] = useState(null); + const [scoreCommentLoading, setScoreCommentLoading] = useState(false); + const displayName = useMemo( () => (userName ? `${userName}님` : "회원님"), [userName] @@ -1889,6 +1893,38 @@ const AnalysisScreen = ({ navigation }: any) => { } }, []); + // 랜덤 코멘트 로드 + const loadRandomScoreComment = useCallback(async () => { + try { + setScoreCommentLoading(true); + setScoreComment(null); + + // 3개 API 중 랜덤으로 하나 선택 + const commentTypes = [ + healthScoreAPI.getDailyComment, + healthScoreAPI.getWeeklyComment, + healthScoreAPI.getMonthlyComment, + ]; + const randomIndex = Math.floor(Math.random() * commentTypes.length); + const selectedCommentAPI = commentTypes[randomIndex]; + + console.log("[ANALYSIS] 코멘트 API 선택:", randomIndex === 0 ? "daily" : randomIndex === 1 ? "weekly" : "monthly"); + + const comment = await selectedCommentAPI(); + if (comment) { + setScoreComment(comment); + console.log("[ANALYSIS] 코멘트 로드 성공:", comment); + } else { + console.log("[ANALYSIS] 코멘트 없음"); + } + } catch (error) { + console.error("[ANALYSIS] 코멘트 로드 실패:", error); + setScoreComment(null); + } finally { + setScoreCommentLoading(false); + } + }, []); + useEffect(() => { loadUserId(); }, [loadUserId]); @@ -1916,6 +1952,7 @@ const AnalysisScreen = ({ navigation }: any) => { // 그래프는 userId useEffect에서만 로드 (중복 방지) loadHealthScore(); + loadRandomScoreComment(); }, [ checkPremium, loadWorkoutHistory, @@ -1924,6 +1961,7 @@ const AnalysisScreen = ({ navigation }: any) => { loadUserName, loadLocalCompletions, loadHealthScore, + loadRandomScoreComment, userId, ]) ); @@ -2118,6 +2156,21 @@ const AnalysisScreen = ({ navigation }: any) => { style={styles.content} contentContainerStyle={styles.contentContainer} > + {/* 건강점수 코멘트 섹션 */} + {scoreCommentLoading ? ( + + + 분석 중... + + ) : scoreComment ? ( + + + + {scoreComment} + + + ) : null} + {/* 건강점수 섹션 */} => { + try { + const userId = await getUserId(); + const response = await requestAI<{ comment: string }>( + `/score/comment/daily/${userId}`, + { method: "GET" } + ); + return response?.comment || null; + } catch (error: any) { + if (error.status === 404) { + return null; + } + console.error("[HEALTH_SCORE] 일일 코멘트 로드 실패:", error.message); + return null; + } + }, + + // 주간 코멘트 + getWeeklyComment: async (): Promise => { + try { + const userId = await getUserId(); + const response = await requestAI<{ comment: string }>( + `/score/comment/weekly/${userId}`, + { method: "GET" } + ); + return response?.comment || null; + } catch (error: any) { + if (error.status === 404) { + return null; + } + console.error("[HEALTH_SCORE] 주간 코멘트 로드 실패:", error.message); + return null; + } + }, + + // 월간 코멘트 + getMonthlyComment: async (): Promise => { + try { + const userId = await getUserId(); + const response = await requestAI<{ comment: string }>( + `/score/comment/monthly/${userId}`, + { method: "GET" } + ); + return response?.comment || null; + } catch (error: any) { + if (error.status === 404) { + return null; + } + console.error("[HEALTH_SCORE] 월간 코멘트 로드 실패:", error.message); + return null; + } + }, }; \ No newline at end of file