stock / src /pages /TransactionDetail.tsx
Zelyanoth's picture
Upload 101 files
24d40b9 verified
import { useState, useEffect } from "react";
import { useParams, useNavigate } from "react-router-dom";
import { Trash, Edit, ArrowDownRight, ArrowUpRight } from "lucide-react";
import AppLayout from "@/components/layout/AppLayout";
import Header from "@/components/shared/Header";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { Transaction } from "@/components/dashboard/RecentTransactions";
import { toast } from "sonner";
import database from "@/services/database";
const TransactionDetail = () => {
const { id } = useParams<{ id: string }>();
const navigate = useNavigate();
const [transaction, setTransaction] = useState<Transaction | null>(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const loadTransaction = async () => {
if (!id) return;
try {
await database.initialize();
// Fetch the transaction from SQLite
const results = database.exec('SELECT * FROM transactions WHERE id = ?', [id]);
if (results.length === 0) {
setTransaction(null);
} else {
const row = results[0];
// Convert to our Transaction type
setTransaction({
id: row.id,
title: row.title,
amount: parseFloat(row.amount),
type: row.type as 'income' | 'expense',
category: row.category,
date: new Date(row.date),
note: row.note || undefined
});
}
} catch (error) {
console.error('Error loading transaction:', error);
toast.error('Failed to load transaction');
} finally {
setIsLoading(false);
}
};
loadTransaction();
}, [id]);
if (isLoading) {
return (
<AppLayout>
<div className="max-w-md mx-auto p-4 flex justify-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div>
</div>
</AppLayout>
);
}
if (!transaction) {
return (
<AppLayout>
<div className="max-w-md mx-auto p-4 text-center">
<h2 className="text-xl font-semibold">Transaction not found</h2>
<Button
onClick={() => navigate('/transactions')}
className="mt-4"
>
Go Back
</Button>
</div>
</AppLayout>
);
}
const formatDate = (date: Date) => {
return new Intl.DateTimeFormat('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric'
}).format(date);
};
const formatCurrency = (amount: number) => {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
currencyDisplay: 'symbol',
}).format(Math.abs(amount));
};
const handleDelete = async () => {
if (!id) return;
try {
await database.initialize();
// Delete the transaction from SQLite
database.exec('DELETE FROM transactions WHERE id = ?', [id]);
toast.success('Transaction deleted successfully');
navigate('/transactions');
} catch (error) {
console.error('Error deleting transaction:', error);
toast.error('Failed to delete transaction');
}
};
return (
<AppLayout>
<div className="max-w-md mx-auto">
<Header
title="Transaction Details"
showBackButton
rightElement={
<Button
variant="ghost"
size="icon"
onClick={handleDelete}
className="text-red-500 hover:text-red-600 hover:bg-red-50"
>
<Trash size={18} />
</Button>
}
/>
<div className="p-4 space-y-6 animate-fade-in">
<div className="flex justify-center py-6">
<div
className={cn(
"w-16 h-16 rounded-full flex items-center justify-center",
transaction.type === "expense"
? "bg-red-100 text-red-600"
: "bg-green-100 text-green-600"
)}
>
{transaction.type === "expense"
? <ArrowUpRight size={28} />
: <ArrowDownRight size={28} />
}
</div>
</div>
<div className="text-center">
<h2 className="text-2xl font-semibold">{transaction.title}</h2>
<p className={cn(
"text-3xl font-bold mt-2",
transaction.type === "expense" ? "text-red-600" : "text-green-600"
)}>
{transaction.type === "expense" ? "- " : "+ "}
{formatCurrency(transaction.amount)}
</p>
<p className="text-muted-foreground mt-1">{formatDate(transaction.date)}</p>
</div>
<div className="bg-card border border-border rounded-xl p-4 space-y-4">
<div>
<p className="text-sm text-muted-foreground">Type</p>
<p className="font-medium capitalize">{transaction.type}</p>
</div>
<div>
<p className="text-sm text-muted-foreground">Category</p>
<p className="font-medium capitalize">{transaction.category}</p>
</div>
<div>
<p className="text-sm text-muted-foreground">Note</p>
<p className="font-medium">{transaction.note || "No notes"}</p>
</div>
</div>
<Button
onClick={() => navigate(`/edit-transaction/${transaction.id}`)}
className="w-full flex items-center justify-center py-6"
>
<Edit size={18} className="mr-2" />
Edit Transaction
</Button>
</div>
</div>
</AppLayout>
);
};
export default TransactionDetail;