Reverted to edit edt-df6bf84a-482f-4f45-8871-2125b421fdb0: "Add bottom padding to screen
Adds 100px bottom padding to the screen."
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
|
||||
#Tue Mar 18 00:16:17 KST 2025
|
||||
buildNumber=2
|
||||
versionCode=1
|
||||
versionName=1.0.2
|
||||
versionName=1.0.1
|
||||
|
||||
|
||||
68
build-apk.sh
68
build-apk.sh
@@ -12,56 +12,8 @@ NC='\033[0m' # No Color
|
||||
# 프로젝트 디렉토리로 이동
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# 버전 정보 파일 경로
|
||||
VERSION_PROPS_FILE="android/version.properties"
|
||||
|
||||
# 현재 버전 정보 읽기
|
||||
if [ -f "$VERSION_PROPS_FILE" ]; then
|
||||
CURRENT_VERSION_NAME=$(grep "versionName" "$VERSION_PROPS_FILE" | cut -d'=' -f2)
|
||||
CURRENT_VERSION_CODE=$(grep "versionCode" "$VERSION_PROPS_FILE" | cut -d'=' -f2)
|
||||
CURRENT_BUILD_NUMBER=$(grep "buildNumber" "$VERSION_PROPS_FILE" | cut -d'=' -f2)
|
||||
|
||||
echo -e "${YELLOW}현재 버전 정보:${NC}"
|
||||
echo -e "버전 이름: ${GREEN}$CURRENT_VERSION_NAME${NC}"
|
||||
echo -e "버전 코드: ${GREEN}$CURRENT_VERSION_CODE${NC}"
|
||||
echo -e "빌드 번호: ${GREEN}$CURRENT_BUILD_NUMBER${NC}"
|
||||
|
||||
# 버전 수정 여부 확인
|
||||
echo -e "\n${YELLOW}버전 정보를 수정하시겠습니까? (y/n)${NC}"
|
||||
read -r UPDATE_VERSION
|
||||
|
||||
if [[ "$UPDATE_VERSION" == "y" || "$UPDATE_VERSION" == "Y" ]]; then
|
||||
echo -e "\n${YELLOW}새 버전 이름을 입력하세요 (현재: $CURRENT_VERSION_NAME):${NC}"
|
||||
read -r NEW_VERSION_NAME
|
||||
|
||||
if [ -z "$NEW_VERSION_NAME" ]; then
|
||||
NEW_VERSION_NAME=$CURRENT_VERSION_NAME
|
||||
fi
|
||||
|
||||
echo -e "${YELLOW}새 버전 코드를 입력하세요 (현재: $CURRENT_VERSION_CODE):${NC}"
|
||||
read -r NEW_VERSION_CODE
|
||||
|
||||
if [ -z "$NEW_VERSION_CODE" ]; then
|
||||
NEW_VERSION_CODE=$CURRENT_VERSION_CODE
|
||||
fi
|
||||
|
||||
# 버전 정보 업데이트
|
||||
sed -i '' "s/versionName=$CURRENT_VERSION_NAME/versionName=$NEW_VERSION_NAME/g" "$VERSION_PROPS_FILE"
|
||||
sed -i '' "s/versionCode=$CURRENT_VERSION_CODE/versionCode=$NEW_VERSION_CODE/g" "$VERSION_PROPS_FILE"
|
||||
|
||||
echo -e "\n${GREEN}버전 정보가 업데이트되었습니다:${NC}"
|
||||
echo -e "버전 이름: ${GREEN}$NEW_VERSION_NAME${NC}"
|
||||
echo -e "버전 코드: ${GREEN}$NEW_VERSION_CODE${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}현재 버전 정보를 유지합니다.${NC}"
|
||||
fi
|
||||
else
|
||||
echo -e "${RED}버전 정보 파일을 찾을 수 없습니다: $VERSION_PROPS_FILE${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 빌드 타입 선택 메뉴
|
||||
echo -e "\n${YELLOW}Zellyy Finance 앱 빌드 스크립트${NC}"
|
||||
echo -e "${YELLOW}Zellyy Finance 앱 빌드 스크립트${NC}"
|
||||
echo -e "${YELLOW}=============================${NC}"
|
||||
echo -e "빌드 타입을 선택하세요:"
|
||||
echo -e "1) 디버그 빌드 (개발 및 테스트용)"
|
||||
@@ -179,15 +131,15 @@ if [ "$BUILD_TYPE" = "debug" ]; then
|
||||
fi
|
||||
|
||||
APK_PATH="app/build/outputs/apk/debug/app-debug.apk"
|
||||
DEST_PATH="$HOME/Dev/zellyy-finance-debug.apk"
|
||||
DEST_PATH="$HOME/zellyy-finance-debug.apk"
|
||||
|
||||
if [ -f "$APK_PATH" ]; then
|
||||
echo -e "${GREEN}디버그 APK 빌드 성공!${NC}"
|
||||
echo -e "APK 파일 위치: $(pwd)/$APK_PATH"
|
||||
|
||||
# Dev 디렉토리로 APK 복사
|
||||
# 홈 디렉토리로 APK 복사
|
||||
cp "$APK_PATH" "$DEST_PATH"
|
||||
echo -e "${GREEN}APK를 Dev 디렉토리에 복사했습니다: $DEST_PATH${NC}"
|
||||
echo -e "${GREEN}APK를 홈 디렉토리에 복사했습니다: $DEST_PATH${NC}"
|
||||
|
||||
# 연결된 기기 확인
|
||||
DEVICES=$(adb devices | grep -v "List" | grep "device" | wc -l)
|
||||
@@ -227,15 +179,15 @@ elif [ "$BUILD_TYPE" = "release-aab" ]; then
|
||||
fi
|
||||
|
||||
AAB_PATH="app/build/outputs/bundle/release/app-release.aab"
|
||||
DEST_PATH="$HOME/Dev/zellyy-finance-release.aab"
|
||||
DEST_PATH="$HOME/zellyy-finance-release.aab"
|
||||
|
||||
if [ -f "$AAB_PATH" ]; then
|
||||
echo -e "${GREEN}릴리즈 AAB 빌드 성공!${NC}"
|
||||
echo -e "AAB 파일 위치: $(pwd)/$AAB_PATH"
|
||||
|
||||
# Dev 디렉토리로 AAB 복사
|
||||
# 홈 디렉토리로 AAB 복사
|
||||
cp "$AAB_PATH" "$DEST_PATH"
|
||||
echo -e "${GREEN}AAB를 Dev 디렉토리에 복사했습니다: $DEST_PATH${NC}"
|
||||
echo -e "${GREEN}AAB를 홈 디렉토리에 복사했습니다: $DEST_PATH${NC}"
|
||||
|
||||
echo -e "${YELLOW}다음 단계:${NC}"
|
||||
echo "1. Google Play Console에 AAB 파일 업로드: $DEST_PATH"
|
||||
@@ -255,15 +207,15 @@ elif [ "$BUILD_TYPE" = "release-apk" ]; then
|
||||
fi
|
||||
|
||||
SIGNED_APK_PATH="app/build/outputs/apk/release/app-release.apk"
|
||||
DEST_PATH="$HOME/Dev/zellyy-finance-release.apk"
|
||||
DEST_PATH="$HOME/zellyy-finance-release.apk"
|
||||
|
||||
if [ -f "$SIGNED_APK_PATH" ]; then
|
||||
echo -e "${GREEN}서명된 릴리즈 APK 빌드 성공!${NC}"
|
||||
echo -e "APK 파일 위치: $(pwd)/$SIGNED_APK_PATH"
|
||||
|
||||
# Dev 디렉토리로 APK 복사
|
||||
# 홈 디렉토리로 APK 복사
|
||||
cp "$SIGNED_APK_PATH" "$DEST_PATH"
|
||||
echo -e "${GREEN}서명된 APK를 Dev 디렉토리에 복사했습니다: $DEST_PATH${NC}"
|
||||
echo -e "${GREEN}서명된 APK를 홈 디렉토리에 복사했습니다: $DEST_PATH${NC}"
|
||||
|
||||
# 연결된 기기 확인
|
||||
DEVICES=$(adb devices | grep -v "List" | grep "device" | wc -l)
|
||||
|
||||
57
build-ios.sh
Executable file → Normal file
57
build-ios.sh
Executable file → Normal file
@@ -12,55 +12,8 @@ NC='\033[0m' # No Color
|
||||
# 프로젝트 디렉토리로 이동
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# 프로젝트 파일 경로
|
||||
XCODEPROJ_FILE="ios/App/App.xcodeproj/project.pbxproj"
|
||||
|
||||
# 현재 버전 및 빌드 번호 가져오기
|
||||
if [ -f "$XCODEPROJ_FILE" ]; then
|
||||
MARKETING_VERSION=$(grep -A 1 "MARKETING_VERSION" "$XCODEPROJ_FILE" | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+' | head -1)
|
||||
CURRENT_PROJECT_VERSION=$(grep -A 1 "CURRENT_PROJECT_VERSION" "$XCODEPROJ_FILE" | grep -o '[0-9]\+' | head -1)
|
||||
|
||||
echo -e "${YELLOW}현재 버전 정보:${NC}"
|
||||
echo -e "마케팅 버전: ${GREEN}$MARKETING_VERSION${NC}"
|
||||
echo -e "빌드 번호: ${GREEN}$CURRENT_PROJECT_VERSION${NC}"
|
||||
|
||||
# 버전 수정 여부 확인
|
||||
echo -e "\n${YELLOW}버전 정보를 수정하시겠습니까? (y/n)${NC}"
|
||||
read -r UPDATE_VERSION
|
||||
|
||||
if [[ "$UPDATE_VERSION" == "y" || "$UPDATE_VERSION" == "Y" ]]; then
|
||||
echo -e "\n${YELLOW}새 마케팅 버전을 입력하세요 (현재: $MARKETING_VERSION):${NC}"
|
||||
read -r NEW_MARKETING_VERSION
|
||||
|
||||
if [ -z "$NEW_MARKETING_VERSION" ]; then
|
||||
NEW_MARKETING_VERSION=$MARKETING_VERSION
|
||||
fi
|
||||
|
||||
echo -e "${YELLOW}새 빌드 번호를 입력하세요 (현재: $CURRENT_PROJECT_VERSION):${NC}"
|
||||
read -r NEW_PROJECT_VERSION
|
||||
|
||||
if [ -z "$NEW_PROJECT_VERSION" ]; then
|
||||
NEW_PROJECT_VERSION=$CURRENT_PROJECT_VERSION
|
||||
fi
|
||||
|
||||
# 버전 정보 업데이트
|
||||
# macOS에서는 sed -i에 백업 확장자를 지정해야 함
|
||||
sed -i '' "s/MARKETING_VERSION = $MARKETING_VERSION;/MARKETING_VERSION = $NEW_MARKETING_VERSION;/g" "$XCODEPROJ_FILE"
|
||||
sed -i '' "s/CURRENT_PROJECT_VERSION = $CURRENT_PROJECT_VERSION;/CURRENT_PROJECT_VERSION = $NEW_PROJECT_VERSION;/g" "$XCODEPROJ_FILE"
|
||||
|
||||
echo -e "\n${GREEN}버전 정보가 업데이트되었습니다:${NC}"
|
||||
echo -e "마케팅 버전: ${GREEN}$NEW_MARKETING_VERSION${NC}"
|
||||
echo -e "빌드 번호: ${GREEN}$NEW_PROJECT_VERSION${NC}"
|
||||
else
|
||||
echo -e "${YELLOW}현재 버전 정보를 유지합니다.${NC}"
|
||||
fi
|
||||
else
|
||||
echo -e "${RED}프로젝트 파일을 찾을 수 없습니다: $XCODEPROJ_FILE${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 빌드 타입 선택 메뉴
|
||||
echo -e "\n${YELLOW}Zellyy Finance iOS 앱 빌드 스크립트${NC}"
|
||||
echo -e "${YELLOW}Zellyy Finance iOS 앱 빌드 스크립트${NC}"
|
||||
echo -e "${YELLOW}=============================${NC}"
|
||||
echo -e "빌드 타입을 선택하세요:"
|
||||
echo -e "1) 디버그 빌드 (개발 및 테스트용)"
|
||||
@@ -307,9 +260,9 @@ EOF
|
||||
echo -e "${GREEN}디버그용 IPA 파일 생성 성공!${NC}"
|
||||
echo -e "IPA 파일 위치: $(pwd)/$DEBUG_IPA_PATH"
|
||||
|
||||
# Dev 디렉토리로 IPA 복사
|
||||
# 홈 디렉토리로 IPA 복사
|
||||
cp "$DEBUG_IPA_PATH" "$DEBUG_DEST_PATH"
|
||||
echo -e "${GREEN}IPA를 Dev 디렉토리에 복사했습니다: $DEBUG_DEST_PATH${NC}"
|
||||
echo -e "${GREEN}IPA를 홈 디렉토리에 복사했습니다: $DEBUG_DEST_PATH${NC}"
|
||||
|
||||
echo -e "${YELLOW}다음 방법으로 다른 기기에 설치할 수 있습니다:${NC}"
|
||||
echo "1. Apple Configurator 2 앱 사용"
|
||||
@@ -362,9 +315,9 @@ elif [ "$BUILD_TYPE" = "release" ]; then
|
||||
echo -e "${GREEN}IPA 파일 생성 성공!${NC}"
|
||||
echo -e "IPA 파일 위치: $(pwd)/$IPA_PATH"
|
||||
|
||||
# Dev 디렉토리로 IPA 복사
|
||||
# 홈 디렉토리로 IPA 복사
|
||||
cp "$IPA_PATH" "$DEST_PATH"
|
||||
echo -e "${GREEN}IPA를 Dev 디렉토리에 복사했습니다: $DEST_PATH${NC}"
|
||||
echo -e "${GREEN}IPA를 홈 디렉토리에 복사했습니다: $DEST_PATH${NC}"
|
||||
|
||||
echo -e "${YELLOW}다음 단계:${NC}"
|
||||
echo "1. App Store Connect에 로그인: https://appstoreconnect.apple.com"
|
||||
|
||||
@@ -13,7 +13,7 @@ interface BudgetData {
|
||||
remainingAmount: number;
|
||||
}
|
||||
|
||||
export interface BudgetTabContentProps {
|
||||
interface BudgetTabContentProps {
|
||||
data: BudgetData;
|
||||
formatCurrency: (amount: number) => string;
|
||||
calculatePercentage: (spent: number, target: number) => number;
|
||||
@@ -26,12 +26,6 @@ const BudgetTabContent: React.FC<BudgetTabContentProps> = ({
|
||||
calculatePercentage,
|
||||
onSaveBudget
|
||||
}) => {
|
||||
// 데이터 유효성 체크 - 데이터가 없으면 기본값 사용
|
||||
if (!data) {
|
||||
console.warn('BudgetTabContent: data prop이 제공되지 않았습니다. 기본값을 사용합니다.');
|
||||
data = { targetAmount: 0, spentAmount: 0, remainingAmount: 0 };
|
||||
}
|
||||
|
||||
const {
|
||||
categoryBudgets,
|
||||
showBudgetInput,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import React from 'react';
|
||||
import { Transaction } from '@/contexts/budget/types';
|
||||
import TransactionEditDialog from './TransactionEditDialog';
|
||||
@@ -9,7 +8,7 @@ import { useRecentTransactions } from '@/hooks/transactions/useRecentTransaction
|
||||
import { useRecentTransactionsDialog } from '@/hooks/transactions/useRecentTransactionsDialog';
|
||||
import RecentTransactionItem from './recent-transactions/RecentTransactionItem';
|
||||
|
||||
export interface RecentTransactionsSectionProps {
|
||||
interface RecentTransactionsSectionProps {
|
||||
transactions: Transaction[];
|
||||
onUpdateTransaction?: (transaction: Transaction) => void;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ interface SafeAreaContainerProps {
|
||||
/**
|
||||
* 플랫폼별 안전 영역(Safe Area)을 고려한 컨테이너 컴포넌트
|
||||
* iOS에서는 노치/다이나믹 아일랜드를 고려한 여백 적용
|
||||
* CSS 변수와 env() 함수를 사용하여 정확한 안전 영역 계산
|
||||
*/
|
||||
const SafeAreaContainer: React.FC<SafeAreaContainerProps> = ({
|
||||
children,
|
||||
@@ -25,24 +24,17 @@ const SafeAreaContainer: React.FC<SafeAreaContainerProps> = ({
|
||||
// 마운트 시 플랫폼 확인
|
||||
useEffect(() => {
|
||||
setIsIOS(isIOSPlatform());
|
||||
|
||||
// iOS 디버깅용 로그
|
||||
if (isIOSPlatform()) {
|
||||
console.info('iOS 플랫폼 감지됨 - 안전 영역 적용');
|
||||
}
|
||||
}, []);
|
||||
|
||||
// 플랫폼에 따른 클래스 결정
|
||||
let safeAreaClass = '';
|
||||
|
||||
if (isIOS) {
|
||||
safeAreaClass = 'ios-safe-area';
|
||||
|
||||
if (bottomOnly) safeAreaClass += ' ios-safe-area-bottom-only';
|
||||
if (topOnly) safeAreaClass += ' ios-safe-area-top-only';
|
||||
if (!bottomOnly) safeAreaClass += ' pt-12'; // iOS 상단 안전 영역
|
||||
if (!topOnly) safeAreaClass += ' pb-8'; // iOS 하단 안전 영역
|
||||
} else {
|
||||
// 안드로이드 기본 여백
|
||||
safeAreaClass = 'android-safe-area';
|
||||
if (!bottomOnly) safeAreaClass += ' pt-4'; // 안드로이드 상단 여백
|
||||
if (!topOnly) safeAreaClass += ' pb-4'; // 안드로이드 하단 여백
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { isIOSPlatform } from '@/utils/platform';
|
||||
|
||||
/**
|
||||
* 안전 영역(Safe Area) 디버그 컴포넌트
|
||||
* 노치나 다이나믹 아일랜드 등 iOS 기기의 안전 영역을 시각적으로 표시
|
||||
*/
|
||||
const SafeAreaDebug = () => {
|
||||
const [isIOS, setIsIOS] = useState(false);
|
||||
const [safeAreaTop, setSafeAreaTop] = useState('0px');
|
||||
const [safeAreaBottom, setSafeAreaBottom] = useState('0px');
|
||||
|
||||
useEffect(() => {
|
||||
setIsIOS(isIOSPlatform());
|
||||
|
||||
// iOS에서만 안전 영역 값 가져오기 시도
|
||||
if (isIOSPlatform()) {
|
||||
// CSS 변수에서 값 가져오기 시도
|
||||
const computedStyle = getComputedStyle(document.documentElement);
|
||||
const topInset = computedStyle.getPropertyValue('--safe-area-top') ||
|
||||
computedStyle.getPropertyValue('env(safe-area-inset-top)') ||
|
||||
'0px';
|
||||
|
||||
const bottomInset = computedStyle.getPropertyValue('--safe-area-bottom') ||
|
||||
computedStyle.getPropertyValue('env(safe-area-inset-bottom)') ||
|
||||
'0px';
|
||||
|
||||
setSafeAreaTop(topInset);
|
||||
setSafeAreaBottom(bottomInset);
|
||||
|
||||
console.info('iOS 안전 영역 감지:', {
|
||||
top: topInset,
|
||||
bottom: bottomInset
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (!isIOS) {
|
||||
return null; // iOS가 아니면 렌더링하지 않음
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed bottom-0 left-0 z-50 p-2 bg-black/70 text-white text-xs rounded-tr-md">
|
||||
<div>Safe Area Top: {safeAreaTop}</div>
|
||||
<div>Safe Area Bottom: {safeAreaBottom}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SafeAreaDebug;
|
||||
@@ -37,18 +37,15 @@ export const useBudgetTabContent = ({
|
||||
calculatePercentage,
|
||||
onSaveBudget
|
||||
}: UseBudgetTabContentProps): UseBudgetTabContentReturn => {
|
||||
// 데이터가 undefined인 경우를 방지하기 위한 기본값 설정
|
||||
const safeData = data || { targetAmount: 0, spentAmount: 0, remainingAmount: 0 };
|
||||
|
||||
const [categoryBudgets, setCategoryBudgets] = useState<Record<string, number>>({});
|
||||
const [showBudgetInput, setShowBudgetInput] = useState(false);
|
||||
const spentAmount = safeData.spentAmount;
|
||||
const targetAmount = safeData.targetAmount;
|
||||
const spentAmount = data.spentAmount;
|
||||
const targetAmount = data.targetAmount;
|
||||
|
||||
// 로그 추가 - 받은 데이터 확인
|
||||
useEffect(() => {
|
||||
console.log(`BudgetTabContent 수신 데이터:`, safeData);
|
||||
}, [safeData]);
|
||||
console.log(`BudgetTabContent 수신 데이터:`, data);
|
||||
}, [data]);
|
||||
|
||||
// 전역 예산 데이터가 변경되었을 때 로컬 상태 갱신
|
||||
useEffect(() => {
|
||||
|
||||
@@ -150,28 +150,7 @@
|
||||
@apply neuro-pressed px-4 py-3 w-full focus:outline-none focus:ring-2 focus:ring-neuro-accent/30;
|
||||
}
|
||||
|
||||
/* 안전 영역 관련 개선된 클래스 */
|
||||
.ios-safe-area {
|
||||
padding-top: max(1rem, env(safe-area-inset-top));
|
||||
padding-bottom: max(1rem, env(safe-area-inset-bottom));
|
||||
padding-left: env(safe-area-inset-left);
|
||||
padding-right: env(safe-area-inset-right);
|
||||
}
|
||||
|
||||
.ios-safe-area-top-only {
|
||||
padding-top: max(1rem, env(safe-area-inset-top));
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.ios-safe-area-bottom-only {
|
||||
padding-top: 1rem;
|
||||
padding-bottom: max(1rem, env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
.android-safe-area {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
/* 안전 영역 관련 클래스 */
|
||||
.has-safe-area-top {
|
||||
padding-top: max(1rem, env(safe-area-inset-top));
|
||||
}
|
||||
@@ -289,16 +268,6 @@
|
||||
--safe-area-bottom: env(safe-area-inset-bottom);
|
||||
padding-bottom: var(--safe-area-bottom);
|
||||
}
|
||||
|
||||
/* iOS 안전 영역 디버깅 클래스 */
|
||||
.debug-safe-areas {
|
||||
--safe-area-top-color: rgba(255, 0, 0, 0.2);
|
||||
--safe-area-bottom-color: rgba(0, 0, 255, 0.2);
|
||||
|
||||
background:
|
||||
linear-gradient(to bottom, var(--safe-area-top-color) 0, var(--safe-area-top-color) env(safe-area-inset-top), transparent env(safe-area-inset-top)),
|
||||
linear-gradient(to top, var(--safe-area-bottom-color) 0, var(--safe-area-bottom-color) env(safe-area-inset-bottom), transparent env(safe-area-inset-bottom));
|
||||
}
|
||||
}
|
||||
|
||||
.font-inter {
|
||||
|
||||
@@ -1,65 +1,174 @@
|
||||
|
||||
import React, { useEffect } from 'react';
|
||||
import NavBar from '../components/NavBar';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import BudgetTabContent from '@/components/BudgetTabContent';
|
||||
import RecentTransactionsSection from '@/components/RecentTransactionsSection';
|
||||
import NavBar from '@/components/NavBar';
|
||||
import AddTransactionButton from '@/components/AddTransactionButton';
|
||||
import Header from '@/components/Header';
|
||||
import { useBudget } from '@/contexts/budget';
|
||||
import SafeAreaContainer from '@/components/SafeAreaContainer';
|
||||
import { formatCurrency } from '@/utils/formatters';
|
||||
import WelcomeDialog from '@/components/onboarding/WelcomeDialog';
|
||||
import HomeContent from '@/components/home/HomeContent';
|
||||
import { useBudget } from '@/contexts/budget/BudgetContext';
|
||||
import { useAuth } from '@/contexts/auth';
|
||||
import { useWelcomeDialog } from '@/hooks/useWelcomeDialog';
|
||||
import { useDataInitialization } from '@/hooks/useDataInitialization';
|
||||
import { useIsMobile } from '@/hooks/use-mobile';
|
||||
import useNotifications from '@/hooks/useNotifications';
|
||||
|
||||
// 메인 컴포넌트
|
||||
const Index = () => {
|
||||
const { transactions, budgetData, handleBudgetGoalUpdate } = useBudget();
|
||||
const {
|
||||
transactions,
|
||||
budgetData,
|
||||
selectedTab,
|
||||
setSelectedTab,
|
||||
handleBudgetGoalUpdate,
|
||||
updateTransaction,
|
||||
getCategorySpending,
|
||||
resetBudgetData
|
||||
} = useBudget();
|
||||
|
||||
// 데이터 구조 확인용 로깅
|
||||
const { user } = useAuth();
|
||||
const { showWelcome, checkWelcomeDialogState, handleCloseWelcome } = useWelcomeDialog();
|
||||
const { isInitialized } = useDataInitialization(resetBudgetData);
|
||||
const isMobile = useIsMobile();
|
||||
const { addNotification } = useNotifications();
|
||||
|
||||
// 초기화 후 환영 메시지 표시 상태 확인
|
||||
useEffect(() => {
|
||||
console.info('Index 페이지 마운트, 현재 데이터 상태:');
|
||||
console.info('트랜잭션 수:', transactions?.length);
|
||||
console.info('예산 데이터:', budgetData);
|
||||
}, [transactions, budgetData]);
|
||||
if (isInitialized) {
|
||||
const timeoutId = setTimeout(checkWelcomeDialogState, 500);
|
||||
return () => clearTimeout(timeoutId);
|
||||
}
|
||||
}, [isInitialized, checkWelcomeDialogState]);
|
||||
|
||||
// 예산 퍼센트 계산 함수
|
||||
const calculatePercentage = (spent: number, target: number) => {
|
||||
if (target <= 0) return 0;
|
||||
return Math.min(100, Math.round((spent / target) * 100));
|
||||
// 앱 시작시 예시 알림 추가 (실제 앱에서는 필요한 이벤트에 따라 알림 추가)
|
||||
useEffect(() => {
|
||||
// 환영 메시지가 이미 표시되었는지 확인하는 키
|
||||
const welcomeNotificationSent = sessionStorage.getItem('welcomeNotificationSent');
|
||||
|
||||
if (isInitialized && user && !welcomeNotificationSent) {
|
||||
// 사용자 로그인 시 알림 예시 (한 번만 실행)
|
||||
const timeoutId = setTimeout(() => {
|
||||
addNotification(
|
||||
'환영합니다!',
|
||||
'젤리의 적자탈출에 오신 것을 환영합니다. 예산을 설정하고 지출을 기록해보세요.'
|
||||
);
|
||||
// 세션 스토리지에 환영 메시지 표시 여부 저장
|
||||
sessionStorage.setItem('welcomeNotificationSent', 'true');
|
||||
}, 2000);
|
||||
|
||||
return () => clearTimeout(timeoutId);
|
||||
}
|
||||
}, [isInitialized, user, addNotification]);
|
||||
|
||||
// 페이지가 처음 로드될 때 데이터 로딩 확인
|
||||
useEffect(() => {
|
||||
console.log('Index 페이지 마운트, 현재 데이터 상태:');
|
||||
console.log('트랜잭션:', transactions.length);
|
||||
console.log('예산 데이터:', budgetData);
|
||||
|
||||
// 페이지 마운트 시 데이터 동기화 이벤트 수동 발생
|
||||
try {
|
||||
window.dispatchEvent(new Event('transactionUpdated'));
|
||||
window.dispatchEvent(new Event('budgetDataUpdated'));
|
||||
window.dispatchEvent(new Event('categoryBudgetsUpdated'));
|
||||
} catch (e) {
|
||||
console.error('이벤트 발생 오류:', e);
|
||||
}
|
||||
|
||||
// 백업된 데이터 복구 확인 (메인 데이터가 없는 경우만)
|
||||
try {
|
||||
if (!localStorage.getItem('budgetData')) {
|
||||
const budgetBackup = localStorage.getItem('budgetData_backup');
|
||||
if (budgetBackup) {
|
||||
console.log('예산 데이터 백업에서 복구');
|
||||
localStorage.setItem('budgetData', budgetBackup);
|
||||
window.dispatchEvent(new Event('budgetDataUpdated'));
|
||||
}
|
||||
}
|
||||
|
||||
if (!localStorage.getItem('categoryBudgets')) {
|
||||
const categoryBackup = localStorage.getItem('categoryBudgets_backup');
|
||||
if (categoryBackup) {
|
||||
console.log('카테고리 예산 백업에서 복구');
|
||||
localStorage.setItem('categoryBudgets', categoryBackup);
|
||||
window.dispatchEvent(new Event('categoryBudgetsUpdated'));
|
||||
}
|
||||
}
|
||||
|
||||
if (!localStorage.getItem('transactions')) {
|
||||
const transactionBackup = localStorage.getItem('transactions_backup');
|
||||
if (transactionBackup) {
|
||||
console.log('트랜잭션 백업에서 복구');
|
||||
localStorage.setItem('transactions', transactionBackup);
|
||||
window.dispatchEvent(new Event('transactionUpdated'));
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('백업 복구 시도 중 오류:', error);
|
||||
}
|
||||
}, [transactions.length, budgetData]);
|
||||
|
||||
// 앱이 포커스를 얻었을 때 데이터를 새로고침
|
||||
useEffect(() => {
|
||||
const handleFocus = () => {
|
||||
console.log('창이 포커스를 얻음 - 데이터 새로고침');
|
||||
// 이벤트 발생시켜 데이터 새로고침
|
||||
try {
|
||||
window.dispatchEvent(new Event('storage'));
|
||||
window.dispatchEvent(new Event('transactionUpdated'));
|
||||
window.dispatchEvent(new Event('budgetDataUpdated'));
|
||||
window.dispatchEvent(new Event('categoryBudgetsUpdated'));
|
||||
} catch (e) {
|
||||
console.error('이벤트 발생 오류:', e);
|
||||
}
|
||||
};
|
||||
|
||||
// 포커스 이벤트
|
||||
window.addEventListener('focus', handleFocus);
|
||||
|
||||
// 가시성 변경 이벤트 (백그라운드에서 전경으로 돌아올 때)
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (document.visibilityState === 'visible') {
|
||||
console.log('페이지가 다시 보임 - 데이터 새로고침');
|
||||
handleFocus();
|
||||
}
|
||||
});
|
||||
|
||||
// 정기적인 데이터 새로고침 (10초마다)
|
||||
const refreshInterval = setInterval(() => {
|
||||
if (document.visibilityState === 'visible') {
|
||||
console.log('정기 새로고침 - 데이터 업데이트');
|
||||
handleFocus();
|
||||
}
|
||||
}, 10000);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('focus', handleFocus);
|
||||
document.removeEventListener('visibilitychange', () => {});
|
||||
clearInterval(refreshInterval);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<SafeAreaContainer className="min-h-screen bg-neuro-background">
|
||||
<div className="min-h-screen bg-neuro-background pb-24">
|
||||
<div className="max-w-md mx-auto px-6">
|
||||
<Header />
|
||||
|
||||
<Tabs defaultValue="budget" className="w-full mt-4">
|
||||
<TabsList className="grid w-full grid-cols-2 mb-8">
|
||||
<TabsTrigger value="budget">예산</TabsTrigger>
|
||||
<TabsTrigger value="recent">최근 거래</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="budget" className="focus-visible:outline-none">
|
||||
{budgetData && budgetData.monthly && (
|
||||
<BudgetTabContent
|
||||
data={budgetData.monthly}
|
||||
formatCurrency={formatCurrency}
|
||||
calculatePercentage={calculatePercentage}
|
||||
onSaveBudget={(amount, categoryBudgets) =>
|
||||
handleBudgetGoalUpdate('monthly', amount, categoryBudgets)
|
||||
}
|
||||
<HomeContent
|
||||
transactions={transactions}
|
||||
budgetData={budgetData}
|
||||
selectedTab={selectedTab}
|
||||
setSelectedTab={setSelectedTab}
|
||||
handleBudgetGoalUpdate={handleBudgetGoalUpdate}
|
||||
updateTransaction={updateTransaction}
|
||||
getCategorySpending={getCategorySpending}
|
||||
/>
|
||||
)}
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="recent" className="focus-visible:outline-none">
|
||||
<RecentTransactionsSection
|
||||
transactions={transactions || []}
|
||||
onUpdateTransaction={transaction => console.log('트랜잭션 업데이트', transaction)}
|
||||
/>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
|
||||
<AddTransactionButton />
|
||||
<NavBar />
|
||||
</SafeAreaContainer>
|
||||
|
||||
{/* 첫 사용자 안내 팝업 */}
|
||||
<WelcomeDialog open={showWelcome} onClose={handleCloseWelcome} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
import React from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import NavBar from '@/components/NavBar';
|
||||
@@ -7,7 +8,6 @@ import { User, CreditCard, Bell, Lock, HelpCircle, LogOut, ChevronRight } from '
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useAuth } from '@/contexts/auth';
|
||||
import { useToast } from '@/hooks/useToast.wrapper';
|
||||
import SafeAreaContainer from '@/components/SafeAreaContainer';
|
||||
|
||||
const SettingsOption = ({
|
||||
icon: Icon,
|
||||
@@ -57,8 +57,7 @@ const Settings = () => {
|
||||
navigate(path);
|
||||
};
|
||||
|
||||
return (
|
||||
<SafeAreaContainer className="min-h-screen bg-neuro-background">
|
||||
return <div className="min-h-screen bg-neuro-background pb-24">
|
||||
<div className="max-w-md mx-auto px-6">
|
||||
{/* Header */}
|
||||
<header className="py-4">
|
||||
@@ -122,8 +121,7 @@ const Settings = () => {
|
||||
</div>
|
||||
|
||||
<NavBar />
|
||||
</SafeAreaContainer>
|
||||
);
|
||||
</div>;
|
||||
};
|
||||
|
||||
export default Settings;
|
||||
|
||||
Reference in New Issue
Block a user