From ef1e2d1248969fc9c80352fb19530c42d70b9b4a Mon Sep 17 00:00:00 2001 From: donggyu Date: Mon, 15 Dec 2025 04:01:11 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=EB=AC=B4=EB=A3=8C=20=EC=8B=9D=EB=8B=A8=20?= =?UTF-8?q?=EC=B6=94=EC=B2=9C=20=EC=84=A0=ED=98=B8=20=EC=8B=9D=EC=9E=AC?= =?UTF-8?q?=EB=A3=8C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/screens/diet/TempMealRecommendScreen.tsx | 142 ++++++++++++++++--- 1 file changed, 120 insertions(+), 22 deletions(-) diff --git a/src/screens/diet/TempMealRecommendScreen.tsx b/src/screens/diet/TempMealRecommendScreen.tsx index 219f1fc..572cc03 100644 --- a/src/screens/diet/TempMealRecommendScreen.tsx +++ b/src/screens/diet/TempMealRecommendScreen.tsx @@ -343,7 +343,7 @@ const TempMealRecommendScreen: React.FC = () => { protein: 0, fat: 0, }); - + const [selectedFoods, setSelectedFoods] = useState>(new Set()); const fadeAnim = useRef(new Animated.Value(0)).current; useEffect(() => { @@ -414,6 +414,18 @@ const TempMealRecommendScreen: React.FC = () => { } }; + const toggleFoodPreference = (foodName: string) => { + setSelectedFoods((prev) => { + const newSet = new Set(prev); + if (newSet.has(foodName)) { + newSet.delete(foodName); + } else { + newSet.add(foodName); + } + return newSet; + }); + }; + const handleCancelLoading = () => { Alert.alert("요청 취소", "식단 추천 요청을 취소하시겠습니까?", [ { text: "계속 기다리기", style: "cancel" }, @@ -499,7 +511,7 @@ const TempMealRecommendScreen: React.FC = () => { protein: Math.round(totalProt), fat: Math.round(totalFat), }); - + setSelectedFoods(new Set()); setScreen("result"); setCurrentDayTab(0); @@ -531,11 +543,28 @@ const TempMealRecommendScreen: React.FC = () => { try { setLoading(true); console.log("💾 식단 저장 요청 (Server Commit)"); + + // ✅ 1. 식단 저장 await recommendedMealAPI.saveTempMealPlan(); + // ✅ 2. 선호 음식이 있으면 추가 + if (selectedFoods.size > 0 && userId) { + try { + const foodsArray = Array.from(selectedFoods); + console.log("💚 선호 음식 저장:", foodsArray); + await userPreferencesAPI.addPreferences(userId, foodsArray); + console.log("✅ 선호 음식 저장 완료"); + } catch (prefError) { + console.error("선호 음식 저장 실패:", prefError); + // 선호 음식 저장 실패해도 식단 저장은 성공했으므로 계속 진행 + } + } + Alert.alert( "저장 완료! 🎉", - "AI 추천 식단이 기록되었습니다.\n기록하기 화면에서 확인하세요.", + selectedFoods.size > 0 + ? `AI 추천 식단이 기록되었습니다.\n${selectedFoods.size}개의 선호 음식도 추가되었습니다.` + : "AI 추천 식단이 기록되었습니다.\n기록하기 화면에서 확인하세요.", [ { text: "확인", @@ -943,7 +972,6 @@ const TempMealRecommendScreen: React.FC = () => { ); } - return ( { + {/* 상단 요일 탭 */} { showsVerticalScrollIndicator={false} contentContainerStyle={styles.resultContainer} > + {/* 영양 정보 카드 */} { + {/* 식단 리스트 렌더링 */} {recommendedMeals.map((meal, index) => ( { + {/* 음식 목록 */} - {meal.foods.map((food, fIdx) => ( - - { + const isSelected = selectedFoods.has(food.foodName); + + return ( + toggleFoodPreference(food.foodName)} + activeOpacity={0.7} > - - {food.foodName} {food.servingSize}g - - - ({food.calories}kcal) - - - - ))} + + {/* 1. 음식 이름 & 용량 */} + + {food.foodName} {food.servingSize}g + + + {/* 2. 칼로리 */} + + ({food.calories}kcal) + + + + + + ); + })} ))} + {/* 선호 음식 정보 표시 (리스트 하단) */} + {selectedFoods.size > 0 && ( + + + + + {selectedFoods.size}개의 음식을 선호 식단으로 선택했습니다 + + + + )} + + {/* 하단 버튼 액션 */} { style={styles.saveButtonGradient} > - 이 식단 저장하기 + + {selectedFoods.size > 0 + ? `이 식단 저장하기 (선호 음식 ${selectedFoods.size}개)` + : "이 식단 저장하기"} + @@ -1337,6 +1415,26 @@ const styles = StyleSheet.create({ contentWrapper: { flex: 1 }, contentContainer: { paddingHorizontal: 20, paddingBottom: 40 }, resultContainer: { paddingHorizontal: 20, paddingBottom: 40, paddingTop: 10 }, + // ✅ 선호 음식 안내 스타일 추가 + selectedFoodsInfo: { + marginBottom: 16, + borderRadius: 12, + overflow: "hidden", + }, + selectedFoodsInfoGradient: { + flexDirection: "row", + alignItems: "center", + padding: 12, + gap: 8, + borderWidth: 1, + borderColor: "rgba(227,255,124,0.3)", + borderRadius: 12, + }, + selectedFoodsText: { + fontSize: 14, + fontWeight: "600", + color: "#e3ff7c", + }, header: { flexDirection: "row", From 58fa340b2ed91b1a055569af467da773108992cf Mon Sep 17 00:00:00 2001 From: donggyu Date: Mon, 15 Dec 2025 04:36:59 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=EB=B6=84=EC=84=9D=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../analysis/HealthScoreTrendScreen.tsx | 899 +++++----- src/screens/main/MyPageScreen.tsx | 1480 ++++++++--------- 2 files changed, 1229 insertions(+), 1150 deletions(-) diff --git a/src/screens/analysis/HealthScoreTrendScreen.tsx b/src/screens/analysis/HealthScoreTrendScreen.tsx index dc7ad99..5fb7f32 100644 --- a/src/screens/analysis/HealthScoreTrendScreen.tsx +++ b/src/screens/analysis/HealthScoreTrendScreen.tsx @@ -1,393 +1,506 @@ -// src/screens/HealthScoreTrendScreen.tsx -import React, { useState, useEffect } from "react"; -import { - View, - Text, - StyleSheet, - ScrollView, - ActivityIndicator, - TouchableOpacity, - Dimensions, -} from "react-native"; -import { SafeAreaView } from "react-native-safe-area-context"; -import { Ionicons as Icon } from "@expo/vector-icons"; -import { LineChart } from "react-native-chart-kit"; -import { healthScoreAPI, ScoreTrendItem } from "../../services"; -import { colors } from "../../theme/colors"; - -type PeriodType = "daily" | "weekly" | "monthly"; - -const HealthScoreTrendScreen = ({ navigation }: any) => { - const [period, setPeriod] = useState("daily"); - const [trendData, setTrendData] = useState([]); - const [loading, setLoading] = useState(true); - - useEffect(() => { - loadTrendData(); - }, [period]); - - const loadTrendData = async () => { - try { - setLoading(true); - let data: ScoreTrendItem[] = []; - - if (period === "daily") { - data = await healthScoreAPI.getDailyTrend(); - } else if (period === "weekly") { - data = await healthScoreAPI.getWeeklyTrend(); - } else { - data = await healthScoreAPI.getMonthlyTrend(); - } - - console.log("🎯 [SCREEN] 받아온 데이터:", data); - console.log("🎯 [SCREEN] 데이터 길이:", data.length); - console.log("🎯 [SCREEN] 첫 번째 아이템:", data[0]); - - // 유효한 데이터만 필터링 - const validData = data.filter( - (item) => - item && - typeof item.total === "number" && - !isNaN(item.total) && - item.date - ); - - console.log("✅ [SCREEN] 유효한 데이터:", validData); - console.log("✅ [SCREEN] 유효한 데이터 길이:", validData.length); - - setTrendData(validData); - } catch (error) { - console.error("건강점수 트렌드 로드 실패:", error); - setTrendData([]); - } finally { - setLoading(false); - } - }; - - // 차트 데이터 생성 시 추가 검증 - const chartData = { - labels: - trendData.length > 0 - ? trendData.map((item) => { - const date = new Date(item.date); - return period === "monthly" - ? `${date.getMonth() + 1}월` - : `${date.getMonth() + 1}/${date.getDate()}`; - }) - : [""], - datasets: [ - { - data: - trendData.length > 0 - ? trendData.map((item) => { - const value = Number(item.total); - return isNaN(value) ? 0 : value; - }) - : [0], - color: (opacity = 1) => `rgba(227, 255, 124, ${opacity})`, - strokeWidth: 3, - }, - ], - }; - - // 최근 점수 계산 시 검증 - const latestScore = - trendData.length > 0 - ? Math.round(trendData[trendData.length - 1].total || 0) - : 0; - - // 평균 점수 계산 시 검증 - const averageScore = - trendData.length > 0 - ? Math.round( - trendData.reduce((sum, item) => { - const value = Number(item.total); - return sum + (isNaN(value) ? 0 : value); - }, 0) / trendData.length - ) - : 0; - - return ( - - {/* 헤더 */} - - navigation.goBack()} - > - - - 건강점수 추이 - - - - - {/* 기간 선택 탭 */} - - setPeriod("daily")} - > - - 일간 - - - setPeriod("weekly")} - > - - 주간 - - - setPeriod("monthly")} - > - - 월간 - - - - - {loading ? ( - - - 불러오는 중... - - ) : trendData.length === 0 ? ( - - - - 식단과 운동기록이 없어 건강점수를 찾을 수 없습니다 - - - 식단과 운동을 기록하면 점수가 생성됩니다 - - - ) : ( - <> - {/* 요약 카드 */} - - - 최근 점수 - {latestScore} - - - 평균 점수 - {averageScore} - - - - {/* 그래프 */} - - `rgba(227, 255, 124, ${opacity})`, - labelColor: (opacity = 1) => - `rgba(255, 255, 255, ${opacity})`, - style: { - borderRadius: 16, - }, - propsForDots: { - r: "4", - strokeWidth: "2", - stroke: "#E3FF7C", - }, - }} - bezier - style={styles.chart} - /> - - - {/* 점수 히스토리 */} - - 점수 기록 - {trendData - .slice() - .reverse() - .map((item, index) => { - const score = Math.round(item.total || 0); // ⭐ NaN 방지 - return ( - - {item.date} - {score}점 - - ); - })} - - - )} - - - ); -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: "#1c1c1c", - }, - header: { - flexDirection: "row", - alignItems: "center", - justifyContent: "space-between", - paddingHorizontal: 20, - paddingVertical: 16, - }, - backButton: { - width: 40, - height: 40, - justifyContent: "center", - }, - headerTitle: { - fontSize: 18, - fontWeight: "600", - color: "#ffffff", - }, - headerRight: { - width: 40, - }, - content: { - flex: 1, - paddingHorizontal: 20, - }, - periodTabs: { - flexDirection: "row", - backgroundColor: "#2a2a2a", - borderRadius: 12, - padding: 4, - marginBottom: 20, - }, - periodTab: { - flex: 1, - paddingVertical: 10, - alignItems: "center", - borderRadius: 8, - }, - periodTabActive: { - backgroundColor: "#E3FF7C", - }, - periodTabText: { - fontSize: 14, - fontWeight: "600", - color: "#666", - }, - periodTabTextActive: { - color: "#000", - }, - loadingContainer: { - paddingVertical: 60, - alignItems: "center", - gap: 12, - }, - loadingText: { - fontSize: 14, - color: "#aaa", - }, - emptyContainer: { - paddingVertical: 60, - alignItems: "center", - gap: 12, - }, - emptyText: { - fontSize: 16, - fontWeight: "600", - color: "#ffffff", - }, - emptySubText: { - fontSize: 14, - color: "#aaa", - }, - summaryCards: { - flexDirection: "row", - gap: 12, - marginBottom: 20, - }, - summaryCard: { - flex: 1, - backgroundColor: "#2a2a2a", - borderRadius: 12, - padding: 16, - alignItems: "center", - }, - summaryLabel: { - fontSize: 12, - color: "#aaa", - marginBottom: 8, - }, - summaryValue: { - fontSize: 32, - fontWeight: "700", - color: "#E3FF7C", - }, - chartContainer: { - alignItems: "center", - marginBottom: 20, - }, - chart: { - borderRadius: 16, - }, - historySection: { - backgroundColor: "#2a2a2a", - borderRadius: 12, - padding: 16, - marginBottom: 20, - }, - historyTitle: { - fontSize: 16, - fontWeight: "600", - color: "#ffffff", - marginBottom: 16, - }, - historyItem: { - flexDirection: "row", - justifyContent: "space-between", - alignItems: "center", - paddingVertical: 12, - borderBottomWidth: 1, - borderBottomColor: "#333", - }, - historyDate: { - fontSize: 14, - color: "#aaa", - }, - historyScore: { - fontSize: 16, - fontWeight: "600", - color: "#E3FF7C", - }, -}); - -export default HealthScoreTrendScreen; +// src/screens/HealthScoreTrendScreen.tsx +import React, { useState, useEffect } from "react"; +import { + View, + Text, + StyleSheet, + ScrollView, + ActivityIndicator, + TouchableOpacity, + Dimensions, +} from "react-native"; +import { SafeAreaView } from "react-native-safe-area-context"; +import { Ionicons as Icon } from "@expo/vector-icons"; +import { LineChart } from "react-native-chart-kit"; +import { healthScoreAPI, ScoreTrendItem } from "../../services"; +import { colors } from "../../theme/colors"; + +type PeriodType = "daily" | "weekly" | "monthly"; + +const HealthScoreTrendScreen = ({ navigation }: any) => { + const [period, setPeriod] = useState("daily"); + const [trendData, setTrendData] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + loadTrendData(); + }, [period]); + + const loadTrendData = async () => { + try { + setLoading(true); + let data: ScoreTrendItem[] = []; + + if (period === "daily") { + data = await healthScoreAPI.getDailyTrend(); + } else if (period === "weekly") { + data = await healthScoreAPI.getWeeklyTrend(); + } else { + data = await healthScoreAPI.getMonthlyTrend(); + } + + console.log(`🎯 [SCREEN] ${period} 데이터 로드:`, data); + + // 유효한 데이터만 필터링 + const validData = data.filter( + (item) => + item && + typeof item.total === "number" && + !isNaN(item.total) && + item.date + ); + + // 날짜순 오름차순 정렬 (과거 -> 미래) + validData.sort( + (a, b) => new Date(a.date).getTime() - new Date(b.date).getTime() + ); + + setTrendData(validData); + } catch (error) { + console.error("건강점수 트렌드 로드 실패:", error); + setTrendData([]); + } finally { + setLoading(false); + } + }; + + // ✅ [통합 수정] 기간별 고정 축 데이터 생성 함수 (오늘 기준) + // 데이터가 없어도 날짜 축을 고정해서 보여줍니다. + const getProcessedGraphData = () => { + const today = new Date(); + const result = []; + + // 🛠️ 로컬 시간 기준 날짜 문자열 변환 헬퍼 (YYYY-MM-DD) + const getLocalDateString = (date: Date) => { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, "0"); + const day = String(date.getDate()).padStart(2, "0"); + return `${year}-${month}-${day}`; + }; + + if (period === "daily") { + // 🟢 일간: 오늘 포함 최근 5일 + for (let i = 4; i >= 0; i--) { + const d = new Date(today); + d.setDate(today.getDate() - i); + + // 🚨 수정됨: ISOString 대신 로컬 시간 사용 + const dateKey = getLocalDateString(d); + + // 날짜가 정확히 일치하는지 확인 + const foundData = trendData.find((item) => item.date === dateKey); + + result.push({ + date: d, + score: foundData ? Number(foundData.total) : 0, + }); + } + } else if (period === "weekly") { + // 🟢 주간: 이번 주 포함 최근 5주 + for (let i = 4; i >= 0; i--) { + const d = new Date(today); + d.setDate(today.getDate() - i * 7); + + // 해당 주차의 시작일과 종료일 계산 (대략적 범위) + const weekEnd = new Date(d); + weekEnd.setHours(23, 59, 59, 999); + + const weekStart = new Date(d); + weekStart.setDate(d.getDate() - 6); + weekStart.setHours(0, 0, 0, 0); + + // 해당 주간 범위(Start ~ End)에 포함되는 데이터 중 가장 최신 것 찾기 + // (또는 해당 주간의 평균을 내고 싶다면 로직 변경 가능) + const foundData = trendData.find((item) => { + const itemDate = new Date(item.date); + // 날짜 비교 시 시간 간섭을 피하기 위해 날짜 문자열로 비교 권장되나, + // 여기서는 범위 체크를 위해 Date 객체 비교 사용 + return itemDate >= weekStart && itemDate <= weekEnd; + }); + + result.push({ + date: d, + score: foundData ? Number(foundData.total) : 0, + }); + } + } else { + // 🟢 월간: 이번 달 포함 최근 6개월 + for (let i = 5; i >= 0; i--) { + const d = new Date(today); + d.setDate(1); // 1일로 설정하여 월 계산 오차 방지 + d.setMonth(today.getMonth() - i); + + const year = d.getFullYear(); + const month = String(d.getMonth() + 1).padStart(2, "0"); + const monthKey = `${year}-${month}`; // YYYY-MM + + // 해당 월(YYYY-MM)로 시작하는 데이터 찾기 + // 월간 데이터가 여러 개라면 그 중 하나(보통 월말 결산)를 가져오거나 평균을 내야 함 + // 현재는 해당 월의 데이터가 있으면 가져오는 방식 + const foundData = trendData.find((item) => + item.date.startsWith(monthKey) + ); + + result.push({ + date: d, + score: foundData ? Number(foundData.total) : 0, + }); + } + } + + return result; + }; + + const graphData = getProcessedGraphData(); + + // 차트 데이터 생성 + const chartData = { + labels: + graphData.length > 0 + ? graphData.map((item) => { + const date = item.date; + + if (period === "monthly") { + return `${date.getMonth() + 1}월`; + } else if (period === "weekly") { + return `${date.getMonth() + 1}/${date.getDate()}`; + } else { + return `${date.getMonth() + 1}/${date.getDate()}`; + } + }) + : [""], + datasets: [ + { + data: + graphData.length > 0 + ? graphData.map((item) => { + const value = item.score; + return isNaN(value) ? 0 : value; + }) + : [0], + color: (opacity = 1) => `rgba(227, 255, 124, ${opacity})`, + strokeWidth: 3, + }, + ], + }; + + // 최근 점수 (데이터가 있는 가장 마지막 날짜 기준) + const latestScore = + trendData.length > 0 + ? Math.round(trendData[trendData.length - 1].total || 0) + : 0; + + // 평균 점수 (전체 기록 기준) + const averageScore = + trendData.length > 0 + ? Math.round( + trendData.reduce((sum, item) => { + const value = Number(item.total); + return sum + (isNaN(value) ? 0 : value); + }, 0) / trendData.length + ) + : 0; + + return ( + + {/* 헤더 */} + + navigation.goBack()} + > + + + 건강점수 추이 + + + + + {/* 기간 선택 탭 */} + + setPeriod("daily")} + > + + 일간 + + + setPeriod("weekly")} + > + + 주간 + + + setPeriod("monthly")} + > + + 월간 + + + + + {loading ? ( + + + 불러오는 중... + + ) : ( + /* 데이터 유무와 상관없이 로딩이 끝나면 그래프를 보여줍니다 (0점 처리됨) */ + <> + {/* 요약 카드 */} + + + 최근 점수 + {latestScore} + + + 평균 점수 + {averageScore} + + + + {/* 그래프 */} + + `rgba(227, 255, 124, ${opacity})`, + labelColor: (opacity = 1) => + `rgba(255, 255, 255, ${opacity})`, + style: { + borderRadius: 16, + }, + propsForDots: { + r: "4", + strokeWidth: "2", + stroke: "#E3FF7C", + }, + }} + bezier + style={styles.chart} + fromZero={true} + /> + + + {/* 점수 히스토리 (전체 기록) */} + + 점수 기록 + {trendData.length > 0 ? ( + trendData + .slice() + .reverse() + .map((item, index) => { + const score = Math.round(item.total || 0); + return ( + + {item.date} + {score}점 + + ); + }) + ) : ( + + + 아직 기록된 점수가 없습니다. + + + 오늘의 식단과 운동을 기록해보세요! + + + )} + + + )} + + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: "#1c1c1c", + }, + header: { + flexDirection: "row", + alignItems: "center", + justifyContent: "space-between", + paddingHorizontal: 20, + paddingVertical: 16, + }, + backButton: { + width: 40, + height: 40, + justifyContent: "center", + }, + headerTitle: { + fontSize: 18, + fontWeight: "600", + color: "#ffffff", + }, + headerRight: { + width: 40, + }, + content: { + flex: 1, + paddingHorizontal: 20, + }, + periodTabs: { + flexDirection: "row", + backgroundColor: "#2a2a2a", + borderRadius: 12, + padding: 4, + marginBottom: 20, + }, + periodTab: { + flex: 1, + paddingVertical: 10, + alignItems: "center", + borderRadius: 8, + }, + periodTabActive: { + backgroundColor: "#E3FF7C", + }, + periodTabText: { + fontSize: 14, + fontWeight: "600", + color: "#666", + }, + periodTabTextActive: { + color: "#000", + }, + loadingContainer: { + paddingVertical: 60, + alignItems: "center", + gap: 12, + }, + loadingText: { + fontSize: 14, + color: "#aaa", + }, + emptyContainer: { + paddingVertical: 60, + alignItems: "center", + gap: 12, + }, + emptyText: { + fontSize: 16, + fontWeight: "600", + color: "#ffffff", + }, + emptySubText: { + fontSize: 14, + color: "#aaa", + }, + summaryCards: { + flexDirection: "row", + gap: 12, + marginBottom: 20, + }, + summaryCard: { + flex: 1, + backgroundColor: "#2a2a2a", + borderRadius: 12, + padding: 16, + alignItems: "center", + }, + summaryLabel: { + fontSize: 12, + color: "#aaa", + marginBottom: 8, + }, + summaryValue: { + fontSize: 32, + fontWeight: "700", + color: "#E3FF7C", + }, + chartContainer: { + alignItems: "center", + marginBottom: 20, + }, + chart: { + borderRadius: 16, + }, + historySection: { + backgroundColor: "#2a2a2a", + borderRadius: 12, + padding: 16, + marginBottom: 20, + }, + historyTitle: { + fontSize: 16, + fontWeight: "600", + color: "#ffffff", + marginBottom: 16, + }, + historyItem: { + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + paddingVertical: 12, + borderBottomWidth: 1, + borderBottomColor: "#333", + }, + historyDate: { + fontSize: 14, + color: "#aaa", + }, + historyScore: { + fontSize: 16, + fontWeight: "600", + color: "#E3FF7C", + }, + emptyHistoryContainer: { + paddingVertical: 20, + alignItems: "center", + gap: 4, + }, + historyEmptyText: { + color: "#fff", + fontSize: 14, + fontWeight: "600", + textAlign: "center", + }, + historyEmptySubText: { + color: "#666", + fontSize: 12, + textAlign: "center", + }, +}); + +export default HealthScoreTrendScreen; diff --git a/src/screens/main/MyPageScreen.tsx b/src/screens/main/MyPageScreen.tsx index fc6ad29..73f3760 100644 --- a/src/screens/main/MyPageScreen.tsx +++ b/src/screens/main/MyPageScreen.tsx @@ -1,757 +1,723 @@ -// src/screens/main/MyPageScreen.tsx - -import React, { useState, useEffect } from "react"; -import { - View, - Text, - TouchableOpacity, - StyleSheet, - ScrollView, - Alert, - ActivityIndicator, -} from "react-native"; -import { SafeAreaView } from "react-native-safe-area-context"; -import { Ionicons as Icon } from "@expo/vector-icons"; -import AsyncStorage from "@react-native-async-storage/async-storage"; -import { authAPI } from "../../services"; -import AIAnalysisModal from "../../components/modals/AIAnalysisModal"; -import MyPlanModal from "../../components/modals/MyPlanModal"; -import PaymentMethodModal from "../../components/modals/PaymentMethodModal"; -import ProfileEditModal from "../../components/modals/ProfileEditModal"; -import DeleteAccountModal from "../../components/modals/DeleteAccountModal"; -import PremiumModal from "../../components/modals/PremiumModal"; -import { useRoute } from "@react-navigation/native"; -import { useFocusEffect } from "@react-navigation/native"; -import { getProfile } from "@react-native-seoul/kakao-login"; - -const MyPageScreen = ({ navigation }: any) => { - const route = useRoute(); - - const [profileData, setProfileData] = useState(null); - const [loading, setLoading] = useState(true); - const [currentMembershipType, setCurrentMembershipType] = useState< - "FREE" | "PREMIUM" - >("FREE"); - - // 모달 상태 - const [isAIAnalysisModalOpen, setIsAIAnalysisModalOpen] = useState(false); - const [isMyPlanModalOpen, setIsMyPlanModalOpen] = useState(false); - const [isPaymentMethodModalOpen, setIsPaymentMethodModalOpen] = - useState(false); - const [isProfileEditModalOpen, setIsProfileEditModalOpen] = useState(false); - const [isRoutineRecommendModalOpen, setIsRoutineRecommendModalOpen] = - useState(false); - const [isMealRecommendModalOpen, setIsMealRecommendModalOpen] = - useState(false); - const [isDeleteAccountModalOpen, setIsDeleteAccountModalOpen] = - useState(false); - const [isPremiumModalOpen, setIsPremiumModalOpen] = useState(false); - - // 1. 외부에서 넘어온 파라미터 감지 (내 플랜 모달 열기) - useEffect(() => { - if (route.params?.openPremiumModal) { - console.log("🔓 마이페이지 진입: 프리미엄 모달 자동 오픈"); - setIsPremiumModalOpen(true); - navigation.setParams({ openPremiumModal: undefined }); - } - }, [route.params]); - - // 2. 초기 데이터 로드 - useFocusEffect( - React.useCallback(() => { - console.log("🔄 마이페이지 포커스 - 데이터 새로고침"); - fetchProfile(); - loadMembershipType(); - }, []) - ); - - const fetchProfile = async () => { - try { - setLoading(true); - const data = await authAPI.getProfile(); - setProfileData(data); - } catch (error: any) { - console.error("프로필 로드 실패:", error); - if (error.status === 401) navigation.replace("Login"); - } finally { - setLoading(false); - } - }; - - // 멤버십 타입 로드 - const loadMembershipType = async () => { - try { - const membershipType = await AsyncStorage.getItem("membershipType"); - if (membershipType) { - setCurrentMembershipType(membershipType as "FREE" | "PREMIUM"); - } - } catch (error) { - console.error("멤버십 타입 로드 실패:", error); - } - }; - - // 테스트용 멤버십 전환 함수 - const handleToggleMembership = async () => { - const newType = currentMembershipType === "FREE" ? "PREMIUM" : "FREE"; - - Alert.alert( - "멤버십 전환", - `${ - newType === "PREMIUM" ? "프리미엄" : "무료" - } 플랜으로 전환하시겠습니까?`, - [ - { text: "취소", style: "cancel" }, - { - text: "전환", - onPress: async () => { - try { - console.log( - "🔄 멤버십 전환 시작:", - currentMembershipType, - "→", - newType - ); - - // 1. 멤버십 전환 - const result = await authAPI.toggleMembership(); - - // ✅ 2. 프리미엄으로 전환한 경우 토큰 초기화 (무제한 활성화) - if (result.newType === "PREMIUM") { - try { - await authAPI.resetTokens(); - console.log("✅ 프리미엄 전환 + 토큰 초기화 완료"); - } catch (tokenError) { - console.warn("⚠️ 토큰 초기화 실패 (무시):", tokenError); - } - } - - setCurrentMembershipType(result.newType); - - // AsyncStorage에 멤버십 타입 업데이트 (다른 화면에서도 반영되도록) - await AsyncStorage.setItem("membershipType", result.newType); - console.log( - "✅ AsyncStorage에 멤버십 타입 업데이트:", - result.newType - ); - - Alert.alert( - "전환 완료 ✅", - result.message + - (result.newType === "PREMIUM" ? "\n\n 전환 완료" : ""), - [ - { - text: "확인", - onPress: () => { - loadMembershipType(); - }, - }, - ] - ); - - console.log("✅ 멤버십 전환 완료:", result); - } catch (error: any) { - console.error("❌ 멤버십 전환 실패:", error); - Alert.alert( - "오류", - error.message || "멤버십 전환에 실패했습니다." - ); - } - }, - }, - ] - ); - }; - - // ✅ 로그아웃 실행 함수 (즉시 실행용) - const executeLogout = async () => { - try { - await authAPI.logout(); - navigation.replace("Login"); - } catch (e) { - console.error(e); - navigation.replace("Login"); // 에러나도 일단 화면 이동 - } - }; - - // 버튼 클릭 시 확인창 띄우는 로그아웃 핸들러 - const handleLogoutPress = () => { - Alert.alert("로그아웃", "정말로 로그아웃하시겠습니까?", [ - { text: "취소", style: "cancel" }, - { text: "확인", onPress: executeLogout }, - ]); - }; - - const handleDeleteAccount = async () => { - // 프로필 데이터에서 카카오 사용자 여부 확인 - const isKakaoUser = - profileData && - ((profileData as any).loginType === "KAKAO" || - (profileData as any).provider === "KAKAO" || - (profileData as any).kakaoId !== undefined); - - if (isKakaoUser) { - // 카카오 사용자: unlink API 호출 - try { - await authAPI.kakaoUnlink(); - - // 성공하면 카카오 사용자 → 탈퇴 완료 - Alert.alert( - "탈퇴 완료", - "카카오 연결이 해제되었습니다. 그동안 이용해주셔서 감사합니다.", - [ - { - text: "확인", - onPress: async () => { - // 로컬 데이터 삭제 - await AsyncStorage.removeItem("access_token"); - await AsyncStorage.removeItem("refresh_token"); - await AsyncStorage.removeItem("userId"); - await AsyncStorage.removeItem("userName"); - await AsyncStorage.removeItem("membershipType"); - await AsyncStorage.removeItem("chatbot_tokens"); - navigation.replace("Login"); - }, - }, - ] - ); - } catch (error: any) { - console.error("카카오 unlink 실패:", error); - Alert.alert( - "오류", - error.message || "카카오 연결 해제에 실패했습니다." - ); - } - } else { - // 일반 사용자: 회원탈퇴 모달 열기 - setIsDeleteAccountModalOpen(true); - } - }; - - const getMembershipTypeText = (type: string) => { - switch (type) { - case "FREE": - return "무료 회원"; - case "PREMIUM": - return "프리미엄 회원"; - case "VIP": - return "풀업의 신"; - default: - return "무료 회원"; - } - }; - - if (loading) { - return ( - - - - 프로필을 불러오는 중... - - - ); - } - - if (!profileData) { - return ( - - - 프로필 정보를 불러올 수 없습니다 - - 다시 시도 - - - - ); - } - - return ( - - - 마이페이지 - - - - {/* 프로필 카드 */} - - - - - - - - - {profileData?.name || "사용자"}님 - - - - - {getMembershipTypeText(currentMembershipType)} - - {/* 현재 멤버십 상태 표시 */} - - - - - - - - setIsProfileEditModalOpen(true)} - > - - - - - - - {/* 테스트 섹션 */} - - 🧪 개발 테스트 (개발 전용) - - {/* 멤버십 전환 버튼 */} - - - - {currentMembershipType === "FREE" ? "🆓 → 💎" : "💎 → 🆓"}{" "} - 무료/유료 전환 - - - - {currentMembershipType === "FREE" ? "FREE" : "PREMIUM"} - - - - - - - - - navigation.navigate("PaymentSuccess")} - > - - ✅ 결제 성공 화면 - - - - - - - navigation.navigate("PaymentFail")} - > - - ❌ 결제 실패 화면 - - - - - - - navigation.navigate("PaymentCancel")} - > - - 🚫 결제 취소 화면 - - - - - - - - - {/* 구독/결제 */} - - 💳 구독/결제 - - setIsPremiumModalOpen(true)} - > - 구독 하기 - - - - - - setIsPaymentMethodModalOpen(true)} - > - 내 플랜 보기 - - - - - - - - {/* 추천 내역 */} - - 🌟 추천 내역 - - - - navigation.navigate("RoutineRecommend")} - > - 운동 추천 내역 - - - - - - navigation.navigate("MealRecommendHistory")} - > - 식단 추천 내역 - - - - - - - - {/* 계정 관리 */} - - ⚙️ 계정 관리 - - {/* ✅ 버튼에 확인창 있는 로그아웃 연결 */} - - 로그아웃 - - - - - - - 회원탈퇴 - - - - - - - {/* 모달들 */} - - {/* ✅ 1. 구독 정보 모달 (로그아웃 기능 연결됨) */} - setIsPaymentMethodModalOpen(false)} - onCancelSuccess={executeLogout} - /> - - setIsAIAnalysisModalOpen(false)} - /> - - setIsMyPlanModalOpen(false)} - navigation={navigation} - /> - - setIsProfileEditModalOpen(false)} - profileData={profileData} - onProfileUpdate={fetchProfile} - /> - - setIsDeleteAccountModalOpen(false)} - onDeleteSuccess={() => navigation.replace("Login")} - /> - setIsPremiumModalOpen(false)} - /> - - ); -}; - -const NEW_COLORS = { - background: "#1a1a1a", - text: "#f0f0f0", - text_secondary: "#a0a0a0", - accent: "#e3ff7c", - card_bg: "#252525", - separator: "#3a3a3a", - delete_color: "#ff6b6b", -}; - -const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: NEW_COLORS.background, - }, - centerContent: { - justifyContent: "center", - alignItems: "center", - }, - loadingText: { - marginTop: 16, - fontSize: 16, - color: NEW_COLORS.text, - }, - errorText: { - fontSize: 16, - color: NEW_COLORS.delete_color, - marginBottom: 16, - }, - retryBtn: { - paddingHorizontal: 24, - paddingVertical: 12, - backgroundColor: NEW_COLORS.separator, - borderRadius: 8, - }, - retryBtnText: { - color: NEW_COLORS.text, - fontSize: 16, - fontWeight: "600", - }, - header: { - paddingVertical: 16, - paddingHorizontal: 20, - alignItems: "center", - justifyContent: "center", - }, - headerTitle: { - fontSize: 20, - fontWeight: "700", - color: NEW_COLORS.text, - }, - content: { - flex: 1, - paddingHorizontal: 20, - }, - profileCard: { - marginTop: 16, - marginBottom: 30, - flexDirection: "row", - justifyContent: "space-between", - alignItems: "center", - padding: 20, - backgroundColor: NEW_COLORS.card_bg, - borderRadius: 15, - }, - profileInfo: { - flexDirection: "row", - alignItems: "center", - gap: 16, - flex: 1, - }, - profileAvatar: { - width: 60, - height: 60, - borderRadius: 30, - backgroundColor: NEW_COLORS.separator, - justifyContent: "center", - alignItems: "center", - }, - profileDetails: { - flex: 1, - }, - username: { - flexDirection: "row", - alignItems: "center", - gap: 6, - marginBottom: 4, - }, - usernameText: { - fontSize: 20, - fontWeight: "bold", - color: NEW_COLORS.text, - }, - membershipBadgeContainer: { - flexDirection: "row", - alignItems: "center", - gap: 8, - }, - userTitle: { - fontSize: 14, - color: NEW_COLORS.accent, - fontWeight: "500", - }, - membershipStatusBadge: { - paddingHorizontal: 6, - paddingVertical: 2, - borderRadius: 8, - backgroundColor: NEW_COLORS.separator, - }, - premiumStatusBadge: { - backgroundColor: "#FFD70020", - }, - editProfileButton: { - padding: 8, - backgroundColor: NEW_COLORS.separator, - borderRadius: 10, - }, - section: { - paddingVertical: 16, - marginBottom: 8, - }, - separator: { - height: 1, - backgroundColor: NEW_COLORS.separator, - marginVertical: 10, - }, - subSeparator: { - height: 1, - backgroundColor: NEW_COLORS.separator, - marginVertical: 4, - marginLeft: 10, - }, - sectionTitle: { - fontSize: 18, - fontWeight: "600", - color: NEW_COLORS.text, - }, - sectionLinks: { - backgroundColor: NEW_COLORS.card_bg, - borderRadius: 15, - paddingHorizontal: 15, - paddingVertical: 5, - marginTop: 8, - }, - linkItem: { - flexDirection: "row", - justifyContent: "space-between", - alignItems: "center", - paddingVertical: 14, - }, - linkText: { - fontSize: 16, - color: NEW_COLORS.text, - fontWeight: "400", - }, - logoutText: { - color: NEW_COLORS.accent, - fontWeight: "500", - }, - deleteText: { - color: NEW_COLORS.delete_color, - fontWeight: "500", - }, - linkItemWithBadge: { - flexDirection: "row", - alignItems: "center", - gap: 8, - }, - newBadge: { - backgroundColor: NEW_COLORS.accent, - paddingHorizontal: 8, - paddingVertical: 2, - borderRadius: 8, - }, - newBadgeText: { - fontSize: 10, - fontWeight: "700", - color: "#000", - }, - statusBadge: { - backgroundColor: NEW_COLORS.separator, - paddingHorizontal: 10, - paddingVertical: 3, - borderRadius: 10, - }, - premiumBadge: { - backgroundColor: "#FFD70020", - }, - statusBadgeText: { - fontSize: 11, - fontWeight: "700", - color: NEW_COLORS.accent, - }, -}); - -export default MyPageScreen; +// src/screens/main/MyPageScreen.tsx + +import React, { useState, useEffect } from "react"; +import { + View, + Text, + TouchableOpacity, + StyleSheet, + ScrollView, + Alert, + ActivityIndicator, +} from "react-native"; +import { SafeAreaView } from "react-native-safe-area-context"; +import { Ionicons as Icon } from "@expo/vector-icons"; +import AsyncStorage from "@react-native-async-storage/async-storage"; +import { authAPI } from "../../services"; +import AIAnalysisModal from "../../components/modals/AIAnalysisModal"; +import MyPlanModal from "../../components/modals/MyPlanModal"; +import PaymentMethodModal from "../../components/modals/PaymentMethodModal"; +import ProfileEditModal from "../../components/modals/ProfileEditModal"; +import DeleteAccountModal from "../../components/modals/DeleteAccountModal"; +import PremiumModal from "../../components/modals/PremiumModal"; +import { useRoute } from "@react-navigation/native"; +import { useFocusEffect } from "@react-navigation/native"; +import { getProfile } from "@react-native-seoul/kakao-login"; + +const MyPageScreen = ({ navigation }: any) => { + const route = useRoute(); + + const [profileData, setProfileData] = useState(null); + const [loading, setLoading] = useState(true); + const [currentMembershipType, setCurrentMembershipType] = useState< + "FREE" | "PREMIUM" + >("FREE"); + + // 모달 상태 + const [isAIAnalysisModalOpen, setIsAIAnalysisModalOpen] = useState(false); + const [isMyPlanModalOpen, setIsMyPlanModalOpen] = useState(false); + const [isPaymentMethodModalOpen, setIsPaymentMethodModalOpen] = + useState(false); + const [isProfileEditModalOpen, setIsProfileEditModalOpen] = useState(false); + const [isRoutineRecommendModalOpen, setIsRoutineRecommendModalOpen] = + useState(false); + const [isMealRecommendModalOpen, setIsMealRecommendModalOpen] = + useState(false); + const [isDeleteAccountModalOpen, setIsDeleteAccountModalOpen] = + useState(false); + const [isPremiumModalOpen, setIsPremiumModalOpen] = useState(false); + + // 1. 외부에서 넘어온 파라미터 감지 (내 플랜 모달 열기) + useEffect(() => { + if (route.params?.openPremiumModal) { + console.log("🔓 마이페이지 진입: 프리미엄 모달 자동 오픈"); + setIsPremiumModalOpen(true); + navigation.setParams({ openPremiumModal: undefined }); + } + }, [route.params]); + + // 2. 초기 데이터 로드 + useFocusEffect( + React.useCallback(() => { + console.log("🔄 마이페이지 포커스 - 데이터 새로고침"); + fetchProfile(); + loadMembershipType(); + }, []) + ); + + const fetchProfile = async () => { + try { + setLoading(true); + const data = await authAPI.getProfile(); + setProfileData(data); + } catch (error: any) { + console.error("프로필 로드 실패:", error); + if (error.status === 401) navigation.replace("Login"); + } finally { + setLoading(false); + } + }; + + // 멤버십 타입 로드 + const loadMembershipType = async () => { + try { + const membershipType = await AsyncStorage.getItem("membershipType"); + if (membershipType) { + setCurrentMembershipType(membershipType as "FREE" | "PREMIUM"); + } + } catch (error) { + console.error("멤버십 타입 로드 실패:", error); + } + }; + + // 테스트용 멤버십 전환 함수 + const handleToggleMembership = async () => { + const newType = currentMembershipType === "FREE" ? "PREMIUM" : "FREE"; + + Alert.alert( + "멤버십 전환", + `${ + newType === "PREMIUM" ? "프리미엄" : "무료" + } 플랜으로 전환하시겠습니까?`, + [ + { text: "취소", style: "cancel" }, + { + text: "전환", + onPress: async () => { + try { + console.log( + "🔄 멤버십 전환 시작:", + currentMembershipType, + "→", + newType + ); + + // 1. 멤버십 전환 + const result = await authAPI.toggleMembership(); + + // ✅ 2. 프리미엄으로 전환한 경우 토큰 초기화 (무제한 활성화) + if (result.newType === "PREMIUM") { + try { + await authAPI.resetTokens(); + console.log("✅ 프리미엄 전환 + 토큰 초기화 완료"); + } catch (tokenError) { + console.warn("⚠️ 토큰 초기화 실패 (무시):", tokenError); + } + } + + setCurrentMembershipType(result.newType); + + // AsyncStorage에 멤버십 타입 업데이트 (다른 화면에서도 반영되도록) + await AsyncStorage.setItem("membershipType", result.newType); + console.log( + "✅ AsyncStorage에 멤버십 타입 업데이트:", + result.newType + ); + + Alert.alert( + "전환 완료 ✅", + result.message + + (result.newType === "PREMIUM" ? "\n\n 전환 완료" : ""), + [ + { + text: "확인", + onPress: () => { + loadMembershipType(); + }, + }, + ] + ); + + console.log("✅ 멤버십 전환 완료:", result); + } catch (error: any) { + console.error("❌ 멤버십 전환 실패:", error); + Alert.alert( + "오류", + error.message || "멤버십 전환에 실패했습니다." + ); + } + }, + }, + ] + ); + }; + + // ✅ 로그아웃 실행 함수 (즉시 실행용) + const executeLogout = async () => { + try { + await authAPI.logout(); + navigation.replace("Login"); + } catch (e) { + console.error(e); + navigation.replace("Login"); // 에러나도 일단 화면 이동 + } + }; + + // 버튼 클릭 시 확인창 띄우는 로그아웃 핸들러 + const handleLogoutPress = () => { + Alert.alert("로그아웃", "정말로 로그아웃하시겠습니까?", [ + { text: "취소", style: "cancel" }, + { text: "확인", onPress: executeLogout }, + ]); + }; + + const handleDeleteAccount = async () => { + // 프로필 데이터에서 카카오 사용자 여부 확인 + const isKakaoUser = + profileData && + ((profileData as any).loginType === "KAKAO" || + (profileData as any).provider === "KAKAO" || + (profileData as any).kakaoId !== undefined); + + if (isKakaoUser) { + // 카카오 사용자: unlink API 호출 + try { + await authAPI.kakaoUnlink(); + + // 성공하면 카카오 사용자 → 탈퇴 완료 + Alert.alert( + "탈퇴 완료", + "카카오 연결이 해제되었습니다. 그동안 이용해주셔서 감사합니다.", + [ + { + text: "확인", + onPress: async () => { + // 로컬 데이터 삭제 + await AsyncStorage.removeItem("access_token"); + await AsyncStorage.removeItem("refresh_token"); + await AsyncStorage.removeItem("userId"); + await AsyncStorage.removeItem("userName"); + await AsyncStorage.removeItem("membershipType"); + await AsyncStorage.removeItem("chatbot_tokens"); + navigation.replace("Login"); + }, + }, + ] + ); + } catch (error: any) { + console.error("카카오 unlink 실패:", error); + Alert.alert( + "오류", + error.message || "카카오 연결 해제에 실패했습니다." + ); + } + } else { + // 일반 사용자: 회원탈퇴 모달 열기 + setIsDeleteAccountModalOpen(true); + } + }; + + const getMembershipTypeText = (type: string) => { + switch (type) { + case "FREE": + return "무료 회원"; + case "PREMIUM": + return "프리미엄 회원"; + case "VIP": + return "풀업의 신"; + default: + return "무료 회원"; + } + }; + + if (loading) { + return ( + + + + 프로필을 불러오는 중... + + + ); + } + + if (!profileData) { + return ( + + + 프로필 정보를 불러올 수 없습니다 + + 다시 시도 + + + + ); + } + + return ( + + + 마이페이지 + + + + {/* 프로필 카드 */} + + + + + + + + + {profileData?.name || "사용자"}님 + + + + + {getMembershipTypeText(currentMembershipType)} + + {/* 현재 멤버십 상태 표시 */} + + + + + + + + setIsProfileEditModalOpen(true)} + > + + + + + + + {/* 테스트 섹션 */} + + 🧪 개발 테스트 (개발 전용) + + {/* 멤버십 전환 버튼 */} + + + + {currentMembershipType === "FREE" ? "🆓 → 💎" : "💎 → 🆓"}{" "} + 무료/유료 전환 + + + + {currentMembershipType === "FREE" ? "FREE" : "PREMIUM"} + + + + + + + + + + + + + {/* 구독/결제 */} + + 💳 구독/결제 + + setIsPremiumModalOpen(true)} + > + 구독 하기 + + + + + + setIsPaymentMethodModalOpen(true)} + > + 내 플랜 보기 + + + + + + + + {/* 추천 내역 */} + + 🌟 추천 내역 + + + + navigation.navigate("RoutineRecommend")} + > + 운동 추천 내역 + + + + + + navigation.navigate("MealRecommendHistory")} + > + 식단 추천 내역 + + + + + + + + {/* 계정 관리 */} + + ⚙️ 계정 관리 + + {/* ✅ 버튼에 확인창 있는 로그아웃 연결 */} + + 로그아웃 + + + + + + + 회원탈퇴 + + + + + + + {/* 모달들 */} + + {/* ✅ 1. 구독 정보 모달 (로그아웃 기능 연결됨) */} + setIsPaymentMethodModalOpen(false)} + onCancelSuccess={executeLogout} + /> + + setIsAIAnalysisModalOpen(false)} + /> + + setIsMyPlanModalOpen(false)} + navigation={navigation} + /> + + setIsProfileEditModalOpen(false)} + profileData={profileData} + onProfileUpdate={fetchProfile} + /> + + setIsDeleteAccountModalOpen(false)} + onDeleteSuccess={() => navigation.replace("Login")} + /> + setIsPremiumModalOpen(false)} + /> + + ); +}; + +const NEW_COLORS = { + background: "#1a1a1a", + text: "#f0f0f0", + text_secondary: "#a0a0a0", + accent: "#e3ff7c", + card_bg: "#252525", + separator: "#3a3a3a", + delete_color: "#ff6b6b", +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: NEW_COLORS.background, + }, + centerContent: { + justifyContent: "center", + alignItems: "center", + }, + loadingText: { + marginTop: 16, + fontSize: 16, + color: NEW_COLORS.text, + }, + errorText: { + fontSize: 16, + color: NEW_COLORS.delete_color, + marginBottom: 16, + }, + retryBtn: { + paddingHorizontal: 24, + paddingVertical: 12, + backgroundColor: NEW_COLORS.separator, + borderRadius: 8, + }, + retryBtnText: { + color: NEW_COLORS.text, + fontSize: 16, + fontWeight: "600", + }, + header: { + paddingVertical: 16, + paddingHorizontal: 20, + alignItems: "center", + justifyContent: "center", + }, + headerTitle: { + fontSize: 20, + fontWeight: "700", + color: NEW_COLORS.text, + }, + content: { + flex: 1, + paddingHorizontal: 20, + }, + profileCard: { + marginTop: 16, + marginBottom: 30, + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + padding: 20, + backgroundColor: NEW_COLORS.card_bg, + borderRadius: 15, + }, + profileInfo: { + flexDirection: "row", + alignItems: "center", + gap: 16, + flex: 1, + }, + profileAvatar: { + width: 60, + height: 60, + borderRadius: 30, + backgroundColor: NEW_COLORS.separator, + justifyContent: "center", + alignItems: "center", + }, + profileDetails: { + flex: 1, + }, + username: { + flexDirection: "row", + alignItems: "center", + gap: 6, + marginBottom: 4, + }, + usernameText: { + fontSize: 20, + fontWeight: "bold", + color: NEW_COLORS.text, + }, + membershipBadgeContainer: { + flexDirection: "row", + alignItems: "center", + gap: 8, + }, + userTitle: { + fontSize: 14, + color: NEW_COLORS.accent, + fontWeight: "500", + }, + membershipStatusBadge: { + paddingHorizontal: 6, + paddingVertical: 2, + borderRadius: 8, + backgroundColor: NEW_COLORS.separator, + }, + premiumStatusBadge: { + backgroundColor: "#FFD70020", + }, + editProfileButton: { + padding: 8, + backgroundColor: NEW_COLORS.separator, + borderRadius: 10, + }, + section: { + paddingVertical: 16, + marginBottom: 8, + }, + separator: { + height: 1, + backgroundColor: NEW_COLORS.separator, + marginVertical: 10, + }, + subSeparator: { + height: 1, + backgroundColor: NEW_COLORS.separator, + marginVertical: 4, + marginLeft: 10, + }, + sectionTitle: { + fontSize: 18, + fontWeight: "600", + color: NEW_COLORS.text, + }, + sectionLinks: { + backgroundColor: NEW_COLORS.card_bg, + borderRadius: 15, + paddingHorizontal: 15, + paddingVertical: 5, + marginTop: 8, + }, + linkItem: { + flexDirection: "row", + justifyContent: "space-between", + alignItems: "center", + paddingVertical: 14, + }, + linkText: { + fontSize: 16, + color: NEW_COLORS.text, + fontWeight: "400", + }, + logoutText: { + color: NEW_COLORS.accent, + fontWeight: "500", + }, + deleteText: { + color: NEW_COLORS.delete_color, + fontWeight: "500", + }, + linkItemWithBadge: { + flexDirection: "row", + alignItems: "center", + gap: 8, + }, + newBadge: { + backgroundColor: NEW_COLORS.accent, + paddingHorizontal: 8, + paddingVertical: 2, + borderRadius: 8, + }, + newBadgeText: { + fontSize: 10, + fontWeight: "700", + color: "#000", + }, + statusBadge: { + backgroundColor: NEW_COLORS.separator, + paddingHorizontal: 10, + paddingVertical: 3, + borderRadius: 10, + }, + premiumBadge: { + backgroundColor: "#FFD70020", + }, + statusBadgeText: { + fontSize: 11, + fontWeight: "700", + color: NEW_COLORS.accent, + }, +}); + +export default MyPageScreen;