diff --git a/src/App.tsx b/src/App.tsx index 00f637c..bf499c5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,6 +4,7 @@ import { Toaster as Sonner } from "@/components/ui/sonner"; import { TooltipProvider } from "@/components/ui/tooltip"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { BrowserRouter, Routes, Route } from "react-router-dom"; +import { useState, useEffect } from "react"; import Index from "./pages/Index"; import Transactions from "./pages/Transactions"; import Analytics from "./pages/Analytics"; @@ -18,32 +19,100 @@ import Login from "./pages/Login"; import Register from "./pages/Register"; import ForgotPassword from "./pages/ForgotPassword"; -const queryClient = new QueryClient(); +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: 1, + refetchOnWindowFocus: false, + }, + }, +}); -const App = () => ( - - - - - - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - - - -); +const App = () => { + const [isLoaded, setIsLoaded] = useState(false); + + useEffect(() => { + // 애플리케이션 초기화 및 로딩 상태 관리 + try { + // 기본 예산 데이터 초기화 + if (!localStorage.getItem('budgetData')) { + const defaultBudgetData = { + daily: { + targetAmount: 40000, + spentAmount: 0, + remainingAmount: 40000 + }, + weekly: { + targetAmount: 280000, + spentAmount: 0, + remainingAmount: 280000 + }, + monthly: { + targetAmount: 1200000, + spentAmount: 0, + remainingAmount: 1200000 + } + }; + localStorage.setItem('budgetData', JSON.stringify(defaultBudgetData)); + } + + // 기본 카테고리 예산 설정 + if (!localStorage.getItem('categoryBudgets')) { + const defaultCategoryBudgets = { + 식비: 400000, + 생활비: 600000, + 교통비: 200000 + }; + localStorage.setItem('categoryBudgets', JSON.stringify(defaultCategoryBudgets)); + } + + // 빈 트랜잭션 배열 초기화 + if (!localStorage.getItem('transactions')) { + localStorage.setItem('transactions', JSON.stringify([])); + } + + setIsLoaded(true); + } catch (error) { + console.error('앱 초기화 중 오류 발생:', error); + // 오류가 발생해도 앱 로딩 + setIsLoaded(true); + } + }, []); + + if (!isLoaded) { + return
+
+
+

앱을 로딩 중입니다...

+
+
; + } + + return ( + + + + + + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + + + + ); +}; export default App; diff --git a/src/components/SyncSettings.tsx b/src/components/SyncSettings.tsx index fa15b34..c06cb47 100644 --- a/src/components/SyncSettings.tsx +++ b/src/components/SyncSettings.tsx @@ -1,121 +1,128 @@ import React, { useState, useEffect } from 'react'; -import { Button } from '@/components/ui/button'; -import { Switch } from '@/components/ui/switch'; -import { Label } from '@/components/ui/label'; -import { Loader2, CloudUpload } from 'lucide-react'; -import { isSyncEnabled, setSyncEnabled, syncAllData } from '@/utils/syncUtils'; -import { supabase } from '@/lib/supabase'; -import { toast } from '@/components/ui/use-toast'; +import { Switch } from "@/components/ui/switch"; +import { Label } from "@/components/ui/label"; +import { CloudUpload, RefreshCw } from "lucide-react"; +import { isSyncEnabled, setSyncEnabled, syncAllData, getLastSyncTime } from "@/utils/syncUtils"; +import { supabase } from "@/lib/supabase"; +import { toast } from "@/components/ui/use-toast"; const SyncSettings = () => { - const [syncEnabled, setSyncEnabledState] = useState(isSyncEnabled()); - const [loading, setLoading] = useState(false); - const [userId, setUserId] = useState(null); - + const [enabled, setEnabled] = useState(isSyncEnabled()); + const [user, setUser] = useState(null); + const [syncing, setSyncing] = useState(false); + const [lastSync, setLastSync] = useState(getLastSyncTime()); + useEffect(() => { - // 현재 로그인한 사용자 가져오기 + // 사용자 정보 가져오기 const getUser = async () => { - const { data: { user } } = await supabase.auth.getUser(); - setUserId(user?.id || null); + const { data } = await supabase.auth.getUser(); + setUser(data.user); }; - getUser(); + + // 마지막 동기화 시간 업데이트 + setLastSync(getLastSyncTime()); }, []); - - const handleSyncToggle = (checked: boolean) => { - setSyncEnabledState(checked); + + const handleSyncToggle = async (checked: boolean) => { + setEnabled(checked); setSyncEnabled(checked); - if (checked) { - toast({ - title: "동기화 활성화됨", - description: "데이터가 클라우드에 자동으로 백업됩니다.", - }); - } else { - toast({ - title: "동기화 비활성화됨", - description: "데이터가 이 기기에만 저장됩니다.", - }); + if (checked && user) { + // 동기화 활성화 시 즉시 동기화 실행 + try { + setSyncing(true); + await syncAllData(user.id); + toast({ + title: "동기화 설정 완료", + description: "모든 데이터가 클라우드에 동기화되었습니다.", + }); + setLastSync(getLastSyncTime()); + } catch (error) { + toast({ + title: "동기화 오류", + description: "동기화 중 문제가 발생했습니다. 다시 시도해주세요.", + variant: "destructive" + }); + } finally { + setSyncing(false); + } } }; - - const handleSyncNow = async () => { - if (!userId) { + + const handleManualSync = async () => { + if (!user) { toast({ - title: "로그인이 필요합니다", - description: "동기화를 사용하려면 먼저 로그인해주세요.", + title: "로그인 필요", + description: "데이터 동기화를 위해 로그인이 필요합니다.", variant: "destructive" }); return; } - setLoading(true); try { - await syncAllData(userId); + setSyncing(true); + await syncAllData(user.id); toast({ title: "동기화 완료", - description: "모든 데이터가 성공적으로 동기화되었습니다." + description: "모든 데이터가 클라우드에 동기화되었습니다.", }); + setLastSync(getLastSyncTime()); } catch (error) { - console.error("동기화 오류:", error); toast({ - title: "동기화 실패", - description: "데이터 동기화 중 오류가 발생했습니다.", + title: "동기화 오류", + description: "동기화 중 문제가 발생했습니다. 다시 시도해주세요.", variant: "destructive" }); } finally { - setLoading(false); + setSyncing(false); } }; + const formatLastSyncTime = () => { + if (!lastSync) return "아직 동기화된 적 없음"; + + const date = new Date(lastSync); + return `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`; + }; + return ( -
+
-
-

데이터 동기화

-

- 여러 기기에서 데이터를 동기화하고 백업합니다 +

+
+ + +
+

+ 여러 기기에서 예산 및 지출 데이터를 동기화합니다.

- {syncEnabled && ( -
- -

- 마지막 동기화: {new Date().toLocaleString('ko-KR')} -

+ {enabled && ( +
+
+ 마지막 동기화: {formatLastSyncTime()} + +
)} - - {!userId && ( -

- 동기화를 사용하려면 로그인이 필요합니다 -

- )}
); }; diff --git a/src/utils/sync/syncSettings.ts b/src/utils/sync/syncSettings.ts index f9de300..1fbfb61 100644 --- a/src/utils/sync/syncSettings.ts +++ b/src/utils/sync/syncSettings.ts @@ -14,3 +14,13 @@ export const isSyncEnabled = (): boolean => { export const setSyncEnabled = (enabled: boolean): void => { localStorage.setItem('syncEnabled', enabled.toString()); }; + +// 최종 동기화 시간 관리 +export const getLastSyncTime = (): string | null => { + return localStorage.getItem('lastSyncTime'); +}; + +export const setLastSyncTime = (): void => { + const now = new Date().toISOString(); + localStorage.setItem('lastSyncTime', now); +}; diff --git a/src/utils/syncUtils.ts b/src/utils/syncUtils.ts index 5ecc153..9c7e8cc 100644 --- a/src/utils/syncUtils.ts +++ b/src/utils/syncUtils.ts @@ -1,5 +1,5 @@ -import { isSyncEnabled, setSyncEnabled } from './sync/syncSettings'; +import { isSyncEnabled, setSyncEnabled, getLastSyncTime, setLastSyncTime } from './sync/syncSettings'; import { uploadTransactions, downloadTransactions } from './sync/transactionSync'; import { uploadBudgets, downloadBudgets } from './sync/budgetSync'; @@ -10,7 +10,9 @@ export { uploadTransactions, downloadTransactions, uploadBudgets, - downloadBudgets + downloadBudgets, + getLastSyncTime, + setLastSyncTime }; /** @@ -19,8 +21,17 @@ export { export const syncAllData = async (userId: string): Promise => { if (!userId || !isSyncEnabled()) return; - await downloadTransactions(userId); - await downloadBudgets(userId); - await uploadTransactions(userId); - await uploadBudgets(userId); + try { + console.log('데이터 동기화 시작...'); + await downloadTransactions(userId); + await downloadBudgets(userId); + await uploadTransactions(userId); + await uploadBudgets(userId); + + // 동기화 시간 업데이트 + setLastSyncTime(); + console.log('데이터 동기화 완료!'); + } catch (error) { + console.error('동기화 중 오류 발생:', error); + } };