The application was displaying duplicate toast notifications due to events being triggered multiple times. This commit prevents duplicate notifications.
131 lines
4.5 KiB
TypeScript
131 lines
4.5 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { PlusIcon } from 'lucide-react';
|
|
import { Dialog, DialogContent, DialogHeader, DialogTitle } from './ui/dialog';
|
|
import { toast } from '@/hooks/useToast.wrapper'; // 래퍼 사용
|
|
import { useBudget } from '@/contexts/BudgetContext';
|
|
import { supabase } from '@/lib/supabase';
|
|
import { isSyncEnabled } from '@/utils/syncUtils';
|
|
import ExpenseForm, { ExpenseFormValues } from './expenses/ExpenseForm';
|
|
import { Transaction } from '@/components/TransactionCard';
|
|
|
|
const AddTransactionButton = () => {
|
|
const [showExpenseDialog, setShowExpenseDialog] = useState(false);
|
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
const { addTransaction } = useBudget();
|
|
|
|
// 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'
|
|
};
|
|
|
|
console.log('새 지출 추가:', newExpense);
|
|
|
|
// BudgetContext를 통해 지출 추가
|
|
addTransaction(newExpense);
|
|
|
|
try {
|
|
const { data: { user } } = await supabase.auth.getUser();
|
|
|
|
if (isSyncEnabled() && user) {
|
|
const { error } = await supabase.from('transactions').insert({
|
|
user_id: user.id,
|
|
title: data.title,
|
|
amount: parseInt(numericAmount),
|
|
date: formattedDate,
|
|
category: data.category,
|
|
type: 'expense',
|
|
transaction_id: newExpense.id
|
|
});
|
|
|
|
if (error) throw error;
|
|
}
|
|
} catch (error) {
|
|
console.error('Supabase에 지출 추가 실패:', error);
|
|
// 실패해도 로컬에는 저장되어 있으므로 사용자에게 알리지 않음
|
|
}
|
|
|
|
// 다이얼로그를 닫습니다
|
|
setShowExpenseDialog(false);
|
|
|
|
// 이벤트 발생 처리 - 단일 이벤트로 통합
|
|
window.dispatchEvent(new CustomEvent('transactionChanged', {
|
|
detail: { type: 'add', transaction: newExpense }
|
|
}));
|
|
|
|
// 토스트는 한 번만 표시 (지연 제거하여 래퍼에서 처리되도록)
|
|
toast({
|
|
title: "지출이 추가되었습니다",
|
|
description: `${data.title} 항목이 ${formatWithCommas(numericAmount)}원으로 등록되었습니다.`,
|
|
duration: 3000
|
|
});
|
|
} catch (error) {
|
|
console.error('지출 추가 중 오류 발생:', error);
|
|
toast({
|
|
title: "지출 추가 실패",
|
|
description: "지출을 추가하는 도중 오류가 발생했습니다.",
|
|
variant: "destructive",
|
|
duration: 4000
|
|
});
|
|
} finally {
|
|
setIsSubmitting(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<div className="fixed bottom-24 right-6 z-20">
|
|
<button
|
|
className="p-4 rounded-full transition-all duration-300 bg-neuro-income shadow-neuro-flat hover:shadow-neuro-convex text-white animate-pulse-subtle"
|
|
onClick={() => setShowExpenseDialog(true)}
|
|
aria-label="지출 추가"
|
|
disabled={isSubmitting}
|
|
>
|
|
<PlusIcon size={24} />
|
|
</button>
|
|
</div>
|
|
|
|
<Dialog open={showExpenseDialog} onOpenChange={(open) => {
|
|
if (!isSubmitting) setShowExpenseDialog(open);
|
|
}}>
|
|
<DialogContent className="w-[90%] max-w-sm mx-auto">
|
|
<DialogHeader>
|
|
<DialogTitle>지출 추가</DialogTitle>
|
|
</DialogHeader>
|
|
<ExpenseForm
|
|
onSubmit={onSubmit}
|
|
onCancel={() => !isSubmitting && setShowExpenseDialog(false)}
|
|
isSubmitting={isSubmitting}
|
|
/>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default AddTransactionButton;
|