Update budget card
Update the budget card to allow users to input daily, weekly, and monthly goals. Change the title of the second card to "목표 입력".
This commit is contained in:
122
src/components/BudgetInputCard.tsx
Normal file
122
src/components/BudgetInputCard.tsx
Normal file
@@ -0,0 +1,122 @@
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Check } from 'lucide-react';
|
||||
|
||||
interface BudgetGoalProps {
|
||||
initialBudgets: {
|
||||
daily: number;
|
||||
weekly: number;
|
||||
monthly: number;
|
||||
};
|
||||
onSave: (type: 'daily' | 'weekly' | 'monthly', amount: number) => void;
|
||||
}
|
||||
|
||||
const BudgetInputCard: React.FC<BudgetGoalProps> = ({ initialBudgets, onSave }) => {
|
||||
const [selectedTab, setSelectedTab] = useState<'daily' | 'weekly' | 'monthly'>('daily');
|
||||
const [budgetInputs, setBudgetInputs] = useState({
|
||||
daily: initialBudgets.daily.toString(),
|
||||
weekly: initialBudgets.weekly.toString(),
|
||||
monthly: initialBudgets.monthly.toString(),
|
||||
});
|
||||
|
||||
// Format for display without commas
|
||||
const formatForInput = (amount: number) => {
|
||||
return amount.toString();
|
||||
};
|
||||
|
||||
// Format with commas for display
|
||||
const formatWithCommas = (amount: string) => {
|
||||
return amount.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setBudgetInputs({
|
||||
daily: formatForInput(initialBudgets.daily),
|
||||
weekly: formatForInput(initialBudgets.weekly),
|
||||
monthly: formatForInput(initialBudgets.monthly),
|
||||
});
|
||||
}, [initialBudgets]);
|
||||
|
||||
const handleInputChange = (value: string, type: 'daily' | 'weekly' | 'monthly') => {
|
||||
// Remove all non-numeric characters
|
||||
const numericValue = value.replace(/[^0-9]/g, '');
|
||||
|
||||
setBudgetInputs(prev => ({
|
||||
...prev,
|
||||
[type]: numericValue
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
const amount = parseInt(budgetInputs[selectedTab], 10) || 0;
|
||||
onSave(selectedTab, amount);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="neuro-card">
|
||||
<Tabs defaultValue="daily" value={selectedTab} onValueChange={(value) => setSelectedTab(value as 'daily' | 'weekly' | 'monthly')} className="w-full">
|
||||
<TabsList className="grid grid-cols-3 mb-4 bg-transparent">
|
||||
<TabsTrigger value="daily" className="data-[state=active]:shadow-neuro-pressed data-[state=active]:bg-transparent">
|
||||
일일 목표
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="weekly" className="data-[state=active]:shadow-neuro-pressed data-[state=active]:bg-transparent">
|
||||
주간 목표
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="monthly" className="data-[state=active]:shadow-neuro-pressed data-[state=active]:bg-transparent">
|
||||
월간 목표
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="daily" className="space-y-4 mt-0">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Input
|
||||
value={budgetInputs.daily}
|
||||
onChange={(e) => handleInputChange(e.target.value, 'daily')}
|
||||
placeholder="목표 금액 입력"
|
||||
className="neuro-pressed"
|
||||
/>
|
||||
<Button onClick={handleSave} size="icon" className="neuro-flat">
|
||||
<Check size={18} />
|
||||
</Button>
|
||||
</div>
|
||||
<p className="text-xs text-gray-500">현재 일일 목표: {formatWithCommas(budgetInputs.daily)}원</p>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="weekly" className="space-y-4 mt-0">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Input
|
||||
value={budgetInputs.weekly}
|
||||
onChange={(e) => handleInputChange(e.target.value, 'weekly')}
|
||||
placeholder="목표 금액 입력"
|
||||
className="neuro-pressed"
|
||||
/>
|
||||
<Button onClick={handleSave} size="icon" className="neuro-flat">
|
||||
<Check size={18} />
|
||||
</Button>
|
||||
</div>
|
||||
<p className="text-xs text-gray-500">현재 주간 목표: {formatWithCommas(budgetInputs.weekly)}원</p>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="monthly" className="space-y-4 mt-0">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Input
|
||||
value={budgetInputs.monthly}
|
||||
onChange={(e) => handleInputChange(e.target.value, 'monthly')}
|
||||
placeholder="목표 금액 입력"
|
||||
className="neuro-pressed"
|
||||
/>
|
||||
<Button onClick={handleSave} size="icon" className="neuro-flat">
|
||||
<Check size={18} />
|
||||
</Button>
|
||||
</div>
|
||||
<p className="text-xs text-gray-500">현재 월간 목표: {formatWithCommas(budgetInputs.monthly)}원</p>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BudgetInputCard;
|
||||
@@ -1,15 +1,19 @@
|
||||
import React, { useState } from 'react';
|
||||
import NavBar from '@/components/NavBar';
|
||||
import BudgetCard from '@/components/BudgetCard';
|
||||
import BudgetInputCard from '@/components/BudgetInputCard';
|
||||
import TransactionCard, { Transaction } from '@/components/TransactionCard';
|
||||
import AddTransactionButton from '@/components/AddTransactionButton';
|
||||
import { Wallet, TrendingUp, Bell } from 'lucide-react';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { toast } from '@/components/ui/use-toast';
|
||||
|
||||
const Index = () => {
|
||||
const [selectedTab, setSelectedTab] = useState("daily");
|
||||
|
||||
// Sample data - in a real app, this would come from a data source
|
||||
const transactions: Transaction[] = [{
|
||||
const transactions: Transaction[] = [
|
||||
{
|
||||
id: '1',
|
||||
title: '식료품 구매',
|
||||
amount: 25000,
|
||||
@@ -30,10 +34,11 @@ const Index = () => {
|
||||
date: '2일전, 9:00 AM',
|
||||
category: 'income',
|
||||
type: 'income'
|
||||
}];
|
||||
}
|
||||
];
|
||||
|
||||
// 예산 데이터 - 실제 앱에서는 백엔드에서 가져와야 함
|
||||
const budgetData = {
|
||||
const [budgetData, setBudgetData] = useState({
|
||||
daily: {
|
||||
targetAmount: 30000,
|
||||
spentAmount: 15000,
|
||||
@@ -49,7 +54,8 @@ const Index = () => {
|
||||
spentAmount: 750000,
|
||||
remainingAmount: 450000
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const formatCurrency = (amount: number) => {
|
||||
return new Intl.NumberFormat('ko-KR', {
|
||||
style: 'currency',
|
||||
@@ -62,6 +68,28 @@ const Index = () => {
|
||||
const calculatePercentage = (spent: number, target: number) => {
|
||||
return Math.min(Math.round(spent / target * 100), 100);
|
||||
};
|
||||
|
||||
// 예산 목표 업데이트 함수
|
||||
const handleBudgetGoalUpdate = (type: 'daily' | 'weekly' | 'monthly', amount: number) => {
|
||||
setBudgetData(prev => {
|
||||
const remainingAmount = Math.max(0, amount - prev[type].spentAmount);
|
||||
|
||||
return {
|
||||
...prev,
|
||||
[type]: {
|
||||
...prev[type],
|
||||
targetAmount: amount,
|
||||
remainingAmount: remainingAmount
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
toast({
|
||||
title: "목표 업데이트 완료",
|
||||
description: `${type === 'daily' ? '일일' : type === 'weekly' ? '주간' : '월간'} 목표가 ${amount.toLocaleString()}원으로 설정되었습니다.`
|
||||
});
|
||||
};
|
||||
|
||||
return <div className="min-h-screen bg-neuro-background pb-24">
|
||||
<div className="max-w-md mx-auto px-6">
|
||||
{/* Header */}
|
||||
@@ -182,6 +210,17 @@ const Index = () => {
|
||||
</Tabs>
|
||||
</div>
|
||||
|
||||
{/* 목표 입력 */}
|
||||
<h2 className="text-lg font-semibold mb-3 mt-8">목표 입력</h2>
|
||||
<BudgetInputCard
|
||||
initialBudgets={{
|
||||
daily: budgetData.daily.targetAmount,
|
||||
weekly: budgetData.weekly.targetAmount,
|
||||
monthly: budgetData.monthly.targetAmount
|
||||
}}
|
||||
onSave={handleBudgetGoalUpdate}
|
||||
/>
|
||||
|
||||
{/* Budget Progress */}
|
||||
<h2 className="text-lg font-semibold mb-3 mt-8">예산 현황</h2>
|
||||
<div className="grid gap-4 mb-8">
|
||||
|
||||
Reference in New Issue
Block a user