import React, { useState, useEffect } from "react"; import { logger } from "@/utils/logger"; import { PlusIcon } from "lucide-react"; import { Dialog, DialogContent, DialogHeader, DialogTitle } from "./ui/dialog"; import { toast } from "@/hooks/useToast.wrapper"; // 래퍼 사용 import { useBudget } from "@/contexts/budget/BudgetContext"; import { supabase } from "@/archive/lib/supabase"; import { isSyncEnabled, setLastSyncTime, trySyncAllData, } from "@/utils/syncUtils"; import ExpenseForm, { ExpenseFormValues } from "./expenses/ExpenseForm"; import { Transaction } from "@/contexts/budget/types"; import { normalizeDate } from "@/utils/sync/transaction/dateUtils"; import useNotifications from "@/hooks/useNotifications"; import { checkNetworkStatus } from "@/utils/network/checker"; import { manageTitleSuggestions } from "@/utils/userTitlePreferences"; // 새로운 제목 관리 추가 const AddTransactionButton = () => { const [showExpenseDialog, setShowExpenseDialog] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); const { addTransaction } = useBudget(); const { addNotification } = useNotifications(); // Format number with commas const formatWithCommas = (value: string): string => { // Remove commas first to avoid duplicates when typing const numericValue = value.replace(/[^0-9]/g, ""); return numericValue.replace(/\B(?=(\d{3})+(?!\d))/g, ","); }; const onSubmit = async (data: ExpenseFormValues) => { // 중복 제출 방지 if (isSubmitting) { return; } try { setIsSubmitting(true); // Remove commas before processing the amount const numericAmount = data.amount.replace(/,/g, ""); // 현재 날짜와 시간을 가져옵니다 const now = new Date(); const formattedDate = `오늘, ${now.getHours()}:${now.getMinutes() < 10 ? "0" + now.getMinutes() : now.getMinutes()} ${now.getHours() >= 12 ? "PM" : "AM"}`; const newExpense: Transaction = { id: Date.now().toString(), title: data.title, amount: parseInt(numericAmount), date: formattedDate, category: data.category, type: "expense", paymentMethod: data.paymentMethod, // 지출 방법 필드 추가 }; logger.info("새 지출 추가:", newExpense); // BudgetContext를 통해 지출 추가 addTransaction(newExpense); // 제목 추천 관리 로직 호출 (새로운 함수) manageTitleSuggestions(newExpense); // 다이얼로그를 닫습니다 setShowExpenseDialog(false); // 토스트는 한 번만 표시 (지연 제거하여 래퍼에서 처리되도록) toast({ title: "지출이 추가되었습니다", description: `${data.title} 항목이 ${formatWithCommas(numericAmount)}원으로 등록되었습니다.`, duration: 3000, }); // 네트워크 상태 확인 후 Supabase 동기화 시도 const isOnline = await checkNetworkStatus(); if (isSyncEnabled() && isOnline) { try { const { data: { user }, } = await supabase.auth.getUser(); if (user) { // ISO 형식으로 날짜 변환 const isoDate = normalizeDate(formattedDate); logger.info("Supabase에 지출 추가 시도 중..."); const { error } = await supabase.from("transactions").insert({ user_id: user.id, title: data.title, amount: parseInt(numericAmount), date: isoDate, // ISO 형식 사용 category: data.category, type: "expense", transaction_id: newExpense.id, payment_method: data.paymentMethod, // Supabase에 필드 추가 }); if (error) { logger.error("Supabase 데이터 저장 오류:", error); throw error; } // 지출 추가 후 자동 동기화 실행 logger.info("지출 추가 후 자동 동기화 시작"); const syncResult = await trySyncAllData(user.id); if (syncResult.success) { // 동기화 성공 시 마지막 동기화 시간 업데이트 const currentTime = new Date().toISOString(); logger.info("자동 동기화 성공, 시간 업데이트:", currentTime); setLastSyncTime(currentTime); // 동기화 성공 알림 추가 addNotification( "동기화 완료", "방금 추가하신 지출 데이터가 클라우드에 동기화되었습니다." ); } } } catch (error) { logger.error("Supabase에 지출 추가 실패:", error); // 실패해도 조용히 처리 (나중에 자동으로 재시도될 것임) logger.info("로컬 데이터는 저장됨, 다음 동기화에서 재시도할 예정"); } } else if (isSyncEnabled() && !isOnline) { logger.info( "네트워크 연결이 없어 로컬에만 저장되었습니다. 다음 동기화에서 업로드될 예정입니다." ); } // 이벤트 발생 처리 - 단일 이벤트로 통합 window.dispatchEvent( new CustomEvent("transactionChanged", { detail: { type: "add", transaction: newExpense }, }) ); } catch (error) { logger.error("지출 추가 중 오류 발생:", error); toast({ title: "지출 추가 실패", description: "지출을 추가하는 도중 오류가 발생했습니다.", variant: "destructive", duration: 4000, }); } finally { setIsSubmitting(false); } }; return ( <>
{ if (!isSubmitting) { setShowExpenseDialog(open); } }} > 지출 입력 !isSubmitting && setShowExpenseDialog(false)} isSubmitting={isSubmitting} /> ); }; export default AddTransactionButton;