stock / src /components /transactions /TransactionList.tsx
Zelyanoth's picture
Upload 101 files
24d40b9 verified
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import {
Search, Filter, ChevronDown, Coffee, ShoppingCart, Zap,
Home as HomeIcon, Car, Utensils, Briefcase, CreditCard
} from 'lucide-react';
import { cn } from '@/lib/utils';
import { Transaction } from '../dashboard/RecentTransactions';
const CATEGORY_ICONS: Record<string, any> = {
food: Utensils,
coffee: Coffee,
shopping: ShoppingCart,
utilities: Zap,
housing: HomeIcon,
transport: Car,
salary: Briefcase,
other: CreditCard
};
interface TransactionListProps {
transactions: Transaction[];
currency?: string;
}
const TransactionList = ({
transactions,
currency = "$"
}: TransactionListProps) => {
const navigate = useNavigate();
const [searchTerm, setSearchTerm] = useState('');
const [showFilters, setShowFilters] = useState(false);
const formatDate = (date: Date) => {
return new Intl.DateTimeFormat('en-US', {
month: 'short',
day: 'numeric'
}).format(date);
};
const formatCurrency = (amount: number) => {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
currencyDisplay: 'symbol',
}).format(Math.abs(amount)).replace('$', '');
};
const getCategoryIcon = (category: string) => {
const IconComponent = CATEGORY_ICONS[category.toLowerCase()] || CreditCard;
return <IconComponent size={16} />;
};
const filteredTransactions = transactions.filter(transaction =>
transaction.title.toLowerCase().includes(searchTerm.toLowerCase())
);
return (
<div className="space-y-4 animate-fade-in">
<div className="relative">
<Search size={18} className="absolute left-3 top-3 text-muted-foreground" />
<input
type="text"
placeholder="Search transactions..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="w-full pl-10 pr-4 py-2.5 rounded-lg border border-border focus:border-primary/50 focus:ring-2 focus:ring-primary/20 transition-all bg-card"
/>
</div>
<div className="flex justify-between items-center">
<button
onClick={() => setShowFilters(!showFilters)}
className="flex items-center space-x-2 text-sm text-muted-foreground hover:text-foreground transition-colors"
>
<Filter size={16} />
<span>Filter</span>
<ChevronDown size={16} className={cn("transition-transform", showFilters && "rotate-180")} />
</button>
<div className="text-sm text-muted-foreground">
{filteredTransactions.length} transactions
</div>
</div>
{showFilters && (
<div className="p-4 border border-border rounded-lg bg-card/50 animate-scale-in">
<div className="flex flex-wrap gap-2">
<button className="px-3 py-1.5 bg-primary/10 text-primary text-sm rounded-full">
All
</button>
<button className="px-3 py-1.5 bg-secondary text-foreground text-sm rounded-full">
Expenses
</button>
<button className="px-3 py-1.5 bg-secondary text-foreground text-sm rounded-full">
Income
</button>
<button className="px-3 py-1.5 bg-secondary text-foreground text-sm rounded-full">
Last 7 days
</button>
<button className="px-3 py-1.5 bg-secondary text-foreground text-sm rounded-full">
This month
</button>
</div>
</div>
)}
{filteredTransactions.length > 0 ? (
<div className="space-y-3">
{filteredTransactions.map((transaction) => (
<div
key={transaction.id}
onClick={() => navigate(`/transactions/${transaction.id}`)}
className="flex items-center p-3 bg-card rounded-xl border border-border/60 subtle-shadow transition-all duration-300 hover:card-shadow hover:-translate-y-0.5 cursor-pointer"
>
<div className={cn(
"w-10 h-10 rounded-full flex items-center justify-center",
transaction.type === "expense" ? "bg-red-100 text-red-600" : "bg-green-100 text-green-600"
)}>
{getCategoryIcon(transaction.category)}
</div>
<div className="ml-3 flex-1">
<p className="font-medium">{transaction.title}</p>
<p className="text-xs text-muted-foreground">{formatDate(transaction.date)}</p>
</div>
<div className={cn(
"font-medium",
transaction.type === "expense" ? "text-red-600" : "text-green-600"
)}>
{transaction.type === "expense" ? "- " : "+ "}
{currency}{formatCurrency(transaction.amount)}
</div>
</div>
))}
</div>
) : (
<div className="text-center p-8 bg-muted/50 rounded-xl border border-border">
<p className="text-muted-foreground">No transactions found</p>
</div>
)}
</div>
);
};
export default TransactionList;