Refactor TransactionCard component
Refactor TransactionCard component into smaller, more manageable components without changing functionality.
This commit is contained in:
@@ -1,8 +1,10 @@
|
|||||||
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { ArrowDownIcon, Coffee, Home, Car } from 'lucide-react';
|
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import TransactionEditDialog from './TransactionEditDialog';
|
import TransactionEditDialog from './TransactionEditDialog';
|
||||||
|
import TransactionIcon from './transaction/TransactionIcon';
|
||||||
|
import TransactionDetails from './transaction/TransactionDetails';
|
||||||
|
import TransactionAmount from './transaction/TransactionAmount';
|
||||||
|
|
||||||
export type Transaction = {
|
export type Transaction = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -13,12 +15,6 @@ export type Transaction = {
|
|||||||
type: 'expense' | 'income';
|
type: 'expense' | 'income';
|
||||||
};
|
};
|
||||||
|
|
||||||
const categoryIcons: Record<string, React.ReactNode> = {
|
|
||||||
식비: <Coffee size={18} />,
|
|
||||||
생활비: <Home size={18} />,
|
|
||||||
교통비: <Car size={18} />,
|
|
||||||
};
|
|
||||||
|
|
||||||
interface TransactionCardProps {
|
interface TransactionCardProps {
|
||||||
transaction: Transaction;
|
transaction: Transaction;
|
||||||
onUpdate?: (updatedTransaction: Transaction) => void;
|
onUpdate?: (updatedTransaction: Transaction) => void;
|
||||||
@@ -31,12 +27,6 @@ const TransactionCard: React.FC<TransactionCardProps> = ({
|
|||||||
const [isEditDialogOpen, setIsEditDialogOpen] = useState(false);
|
const [isEditDialogOpen, setIsEditDialogOpen] = useState(false);
|
||||||
const { title, amount, date, category, type } = transaction;
|
const { title, amount, date, category, type } = transaction;
|
||||||
|
|
||||||
const formattedAmount = new Intl.NumberFormat('ko-KR', {
|
|
||||||
style: 'currency',
|
|
||||||
currency: 'KRW',
|
|
||||||
maximumFractionDigits: 0
|
|
||||||
}).format(amount);
|
|
||||||
|
|
||||||
const handleSaveTransaction = (updatedTransaction: Transaction) => {
|
const handleSaveTransaction = (updatedTransaction: Transaction) => {
|
||||||
if (onUpdate) {
|
if (onUpdate) {
|
||||||
onUpdate(updatedTransaction);
|
onUpdate(updatedTransaction);
|
||||||
@@ -51,22 +41,11 @@ const TransactionCard: React.FC<TransactionCardProps> = ({
|
|||||||
>
|
>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className="p-2 rounded-full bg-neuro-income/10 text-neuro-income">
|
<TransactionIcon category={category} />
|
||||||
{categoryIcons[category] || <Coffee size={18} />}
|
<TransactionDetails title={title} date={date} />
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h3 className="text-sm font-medium">{title}</h3>
|
|
||||||
<p className="text-xs text-gray-500">{date}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-1">
|
<TransactionAmount amount={amount} />
|
||||||
<ArrowDownIcon size={12} className="text-neuro-income" />
|
|
||||||
<span className="font-medium text-neuro-income">
|
|
||||||
{formattedAmount}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ import { Form } from '@/components/ui/form';
|
|||||||
import { toast } from '@/components/ui/use-toast';
|
import { toast } from '@/components/ui/use-toast';
|
||||||
import TransactionFormFields, {
|
import TransactionFormFields, {
|
||||||
transactionFormSchema,
|
transactionFormSchema,
|
||||||
formatWithCommas
|
formatWithCommas,
|
||||||
|
TransactionFormValues
|
||||||
} from './transaction/TransactionFormFields';
|
} from './transaction/TransactionFormFields';
|
||||||
import TransactionDeleteAlert from './transaction/TransactionDeleteAlert';
|
import TransactionDeleteAlert from './transaction/TransactionDeleteAlert';
|
||||||
|
|
||||||
@@ -35,16 +36,16 @@ const TransactionEditDialog: React.FC<TransactionEditDialogProps> = ({
|
|||||||
onSave,
|
onSave,
|
||||||
onDelete
|
onDelete
|
||||||
}) => {
|
}) => {
|
||||||
const form = useForm({
|
const form = useForm<TransactionFormValues>({
|
||||||
resolver: zodResolver(transactionFormSchema),
|
resolver: zodResolver(transactionFormSchema),
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
title: transaction.title,
|
title: transaction.title,
|
||||||
amount: formatWithCommas(transaction.amount.toString()),
|
amount: formatWithCommas(transaction.amount.toString()),
|
||||||
category: transaction.category as any,
|
category: transaction.category as '식비' | '생활비' | '교통비',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleSubmit = (values: any) => {
|
const handleSubmit = (values: TransactionFormValues) => {
|
||||||
// Remove commas from amount string and convert to number
|
// Remove commas from amount string and convert to number
|
||||||
const cleanAmount = values.amount.replace(/,/g, '');
|
const cleanAmount = values.amount.replace(/,/g, '');
|
||||||
|
|
||||||
|
|||||||
26
src/components/transaction/TransactionAmount.tsx
Normal file
26
src/components/transaction/TransactionAmount.tsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { ArrowDownIcon } from 'lucide-react';
|
||||||
|
|
||||||
|
interface TransactionAmountProps {
|
||||||
|
amount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TransactionAmount: React.FC<TransactionAmountProps> = ({ amount }) => {
|
||||||
|
const formattedAmount = new Intl.NumberFormat('ko-KR', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'KRW',
|
||||||
|
maximumFractionDigits: 0
|
||||||
|
}).format(amount);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<ArrowDownIcon size={12} className="text-neuro-income" />
|
||||||
|
<span className="font-medium text-neuro-income">
|
||||||
|
{formattedAmount}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TransactionAmount;
|
||||||
18
src/components/transaction/TransactionDetails.tsx
Normal file
18
src/components/transaction/TransactionDetails.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
interface TransactionDetailsProps {
|
||||||
|
title: string;
|
||||||
|
date: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TransactionDetails: React.FC<TransactionDetailsProps> = ({ title, date }) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h3 className="text-sm font-medium">{title}</h3>
|
||||||
|
<p className="text-xs text-gray-500">{date}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TransactionDetails;
|
||||||
23
src/components/transaction/TransactionIcon.tsx
Normal file
23
src/components/transaction/TransactionIcon.tsx
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { Coffee, Home, Car } from 'lucide-react';
|
||||||
|
|
||||||
|
export const categoryIcons: Record<string, React.ReactNode> = {
|
||||||
|
식비: <Coffee size={18} />,
|
||||||
|
생활비: <Home size={18} />,
|
||||||
|
교통비: <Car size={18} />,
|
||||||
|
};
|
||||||
|
|
||||||
|
interface TransactionIconProps {
|
||||||
|
category: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TransactionIcon: React.FC<TransactionIconProps> = ({ category }) => {
|
||||||
|
return (
|
||||||
|
<div className="p-2 rounded-full bg-neuro-income/10 text-neuro-income">
|
||||||
|
{categoryIcons[category] || <Coffee size={18} />}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TransactionIcon;
|
||||||
Reference in New Issue
Block a user