Refactor budget input card
The budget input card will now be a collapsible element, only showing its title until expanded. This change is intended to reduce visual clutter on the main screen.
This commit is contained in:
@@ -1,8 +1,11 @@
|
|||||||
|
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Check } from 'lucide-react';
|
import { Check, ChevronDown, ChevronUp } from 'lucide-react';
|
||||||
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
|
||||||
|
|
||||||
interface BudgetGoalProps {
|
interface BudgetGoalProps {
|
||||||
initialBudgets: {
|
initialBudgets: {
|
||||||
daily: number;
|
daily: number;
|
||||||
@@ -11,6 +14,7 @@ interface BudgetGoalProps {
|
|||||||
};
|
};
|
||||||
onSave: (type: 'daily' | 'weekly' | 'monthly', amount: number) => void;
|
onSave: (type: 'daily' | 'weekly' | 'monthly', amount: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BudgetInputCard: React.FC<BudgetGoalProps> = ({
|
const BudgetInputCard: React.FC<BudgetGoalProps> = ({
|
||||||
initialBudgets,
|
initialBudgets,
|
||||||
onSave
|
onSave
|
||||||
@@ -21,6 +25,7 @@ const BudgetInputCard: React.FC<BudgetGoalProps> = ({
|
|||||||
weekly: initialBudgets.weekly.toString(),
|
weekly: initialBudgets.weekly.toString(),
|
||||||
monthly: initialBudgets.monthly.toString()
|
monthly: initialBudgets.monthly.toString()
|
||||||
});
|
});
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
|
||||||
// Format for display without commas
|
// Format for display without commas
|
||||||
const formatForInput = (amount: number) => {
|
const formatForInput = (amount: number) => {
|
||||||
@@ -31,6 +36,7 @@ const BudgetInputCard: React.FC<BudgetGoalProps> = ({
|
|||||||
const formatWithCommas = (amount: string) => {
|
const formatWithCommas = (amount: string) => {
|
||||||
return amount.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
return amount.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setBudgetInputs({
|
setBudgetInputs({
|
||||||
daily: formatForInput(initialBudgets.daily),
|
daily: formatForInput(initialBudgets.daily),
|
||||||
@@ -38,6 +44,7 @@ const BudgetInputCard: React.FC<BudgetGoalProps> = ({
|
|||||||
monthly: formatForInput(initialBudgets.monthly)
|
monthly: formatForInput(initialBudgets.monthly)
|
||||||
});
|
});
|
||||||
}, [initialBudgets]);
|
}, [initialBudgets]);
|
||||||
|
|
||||||
const handleInputChange = (value: string, type: 'daily' | 'weekly' | 'monthly') => {
|
const handleInputChange = (value: string, type: 'daily' | 'weekly' | 'monthly') => {
|
||||||
// Remove all non-numeric characters
|
// Remove all non-numeric characters
|
||||||
const numericValue = value.replace(/[^0-9]/g, '');
|
const numericValue = value.replace(/[^0-9]/g, '');
|
||||||
@@ -46,48 +53,70 @@ const BudgetInputCard: React.FC<BudgetGoalProps> = ({
|
|||||||
[type]: numericValue
|
[type]: numericValue
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
const amount = parseInt(budgetInputs[selectedTab], 10) || 0;
|
const amount = parseInt(budgetInputs[selectedTab], 10) || 0;
|
||||||
onSave(selectedTab, amount);
|
onSave(selectedTab, amount);
|
||||||
|
// Close the collapsible after saving
|
||||||
|
setIsOpen(false);
|
||||||
};
|
};
|
||||||
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">
|
return (
|
||||||
<div className="flex items-center space-x-2">
|
<Collapsible
|
||||||
<Input value={budgetInputs.daily} onChange={e => handleInputChange(e.target.value, 'daily')} placeholder="목표 금액 입력" className="neuro-pressed" />
|
open={isOpen}
|
||||||
<Button onClick={handleSave} size="icon" className="neuro-flat text-slate-50 bg-slate-400 hover:bg-slate-300">
|
onOpenChange={setIsOpen}
|
||||||
<Check size={18} />
|
className="neuro-card"
|
||||||
</Button>
|
>
|
||||||
</div>
|
<CollapsibleTrigger className="flex items-center justify-between w-full p-4">
|
||||||
<p className="text-xs text-gray-500">현재 일일 목표: {formatWithCommas(budgetInputs.daily)}원</p>
|
<span className="text-sm font-medium">예산 목표 설정하기</span>
|
||||||
</TabsContent>
|
{isOpen ? (
|
||||||
|
<ChevronUp size={18} className="text-gray-500" />
|
||||||
|
) : (
|
||||||
|
<ChevronDown size={18} className="text-gray-500" />
|
||||||
|
)}
|
||||||
|
</CollapsibleTrigger>
|
||||||
|
|
||||||
|
<CollapsibleContent className="px-4 pb-4">
|
||||||
|
<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="weekly" className="space-y-4 mt-0">
|
<TabsContent value="daily" className="space-y-4 mt-0">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Input value={budgetInputs.weekly} onChange={e => handleInputChange(e.target.value, 'weekly')} placeholder="목표 금액 입력" className="neuro-pressed" />
|
<Input value={budgetInputs.daily} onChange={e => handleInputChange(e.target.value, 'daily')} placeholder="목표 금액 입력" className="neuro-pressed" />
|
||||||
<Button onClick={handleSave} size="icon" className="neuro-flat bg-slate-400 hover:bg-slate-300">
|
<Button onClick={handleSave} size="icon" className="neuro-flat text-slate-50 bg-slate-400 hover:bg-slate-300">
|
||||||
<Check size={18} />
|
<Check size={18} />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-gray-500">현재 주간 목표: {formatWithCommas(budgetInputs.weekly)}원</p>
|
<p className="text-xs text-gray-500">현재 일일 목표: {formatWithCommas(budgetInputs.daily)}원</p>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
<TabsContent value="monthly" className="space-y-4 mt-0">
|
<TabsContent value="weekly" className="space-y-4 mt-0">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<Input value={budgetInputs.monthly} onChange={e => handleInputChange(e.target.value, 'monthly')} placeholder="목표 금액 입력" className="neuro-pressed" />
|
<Input value={budgetInputs.weekly} onChange={e => handleInputChange(e.target.value, 'weekly')} placeholder="목표 금액 입력" className="neuro-pressed" />
|
||||||
<Button onClick={handleSave} size="icon" className="neuro-flat bg-slate-400 hover:bg-slate-300">
|
<Button onClick={handleSave} size="icon" className="neuro-flat bg-slate-400 hover:bg-slate-300">
|
||||||
<Check size={18} />
|
<Check size={18} />
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-gray-500">현재 월간 목표: {formatWithCommas(budgetInputs.monthly)}원</p>
|
<p className="text-xs text-gray-500">현재 주간 목표: {formatWithCommas(budgetInputs.weekly)}원</p>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</Tabs>
|
|
||||||
</div>;
|
<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 bg-slate-400 hover:bg-slate-300">
|
||||||
|
<Check size={18} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-gray-500">현재 월간 목표: {formatWithCommas(budgetInputs.monthly)}원</p>
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
|
</CollapsibleContent>
|
||||||
|
</Collapsible>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
export default BudgetInputCard;
|
|
||||||
|
export default BudgetInputCard;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import BudgetCard from '@/components/BudgetCard';
|
|||||||
import BudgetInputCard from '@/components/BudgetInputCard';
|
import BudgetInputCard from '@/components/BudgetInputCard';
|
||||||
import TransactionCard, { Transaction } from '@/components/TransactionCard';
|
import TransactionCard, { Transaction } from '@/components/TransactionCard';
|
||||||
import AddTransactionButton from '@/components/AddTransactionButton';
|
import AddTransactionButton from '@/components/AddTransactionButton';
|
||||||
import { Wallet, TrendingUp, Bell } from 'lucide-react';
|
import { Bell } from 'lucide-react';
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||||
import { toast } from '@/components/ui/use-toast';
|
import { toast } from '@/components/ui/use-toast';
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ const Index = () => {
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
{/* 목표 진행 상황 - 제목을 카드 위로 이동 */}
|
{/* 목표 진행 상황 */}
|
||||||
<h2 className="text-lg font-semibold mb-3">예산과 지출</h2>
|
<h2 className="text-lg font-semibold mb-3">예산과 지출</h2>
|
||||||
<div className="neuro-card mb-6 overflow-hidden">
|
<div className="neuro-card mb-6 overflow-hidden">
|
||||||
<Tabs defaultValue="daily" value={selectedTab} onValueChange={setSelectedTab} className="w-full">
|
<Tabs defaultValue="daily" value={selectedTab} onValueChange={setSelectedTab} className="w-full">
|
||||||
@@ -205,15 +205,7 @@ const Index = () => {
|
|||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</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>
|
<h2 className="text-lg font-semibold mb-3 mt-8">지출 카테고리</h2>
|
||||||
<div className="grid gap-4 mb-8">
|
<div className="grid gap-4 mb-8">
|
||||||
<BudgetCard title="식비" current={240000} total={400000} color="neuro-income" />
|
<BudgetCard title="식비" current={240000} total={400000} color="neuro-income" />
|
||||||
@@ -221,8 +213,15 @@ const Index = () => {
|
|||||||
<BudgetCard title="교통비" current={190000} total={200000} color="neuro-income" />
|
<BudgetCard title="교통비" current={190000} total={200000} color="neuro-income" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Recent Transactions */}
|
{/* 예산 입력 카드 - 이제는 접힌 상태로 표시됩니다 */}
|
||||||
<h2 className="text-lg font-semibold mb-3">최근 지출</h2>
|
<BudgetInputCard initialBudgets={{
|
||||||
|
daily: budgetData.daily.targetAmount,
|
||||||
|
weekly: budgetData.weekly.targetAmount,
|
||||||
|
monthly: budgetData.monthly.targetAmount
|
||||||
|
}} onSave={handleBudgetGoalUpdate} />
|
||||||
|
|
||||||
|
{/* 최근 지출 */}
|
||||||
|
<h2 className="text-lg font-semibold mb-3 mt-8">최근 지출</h2>
|
||||||
<div className="grid gap-3 mb-6">
|
<div className="grid gap-3 mb-6">
|
||||||
{transactions.map(transaction => <TransactionCard key={transaction.id} transaction={transaction} />)}
|
{transactions.map(transaction => <TransactionCard key={transaction.id} transaction={transaction} />)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user