You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
143 lines
8.9 KiB
TypeScript
143 lines
8.9 KiB
TypeScript
import { prisma } from "@/lib/prisma";
|
|
import {
|
|
Plus,
|
|
Search,
|
|
Filter,
|
|
ArrowUpRight,
|
|
ArrowDownRight,
|
|
Download,
|
|
Calendar,
|
|
Wallet,
|
|
ArrowRightLeft
|
|
} from "lucide-react";
|
|
import { Card } from "@/components/ui/card";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Input } from "@/components/ui/input";
|
|
import { formatCurrency } from "@/lib/utils";
|
|
|
|
export default async function LedgerPage() {
|
|
const transactions = await prisma.companyTransaction.findMany({
|
|
orderBy: { date: 'desc' },
|
|
include: {
|
|
category: true,
|
|
account: true
|
|
}
|
|
});
|
|
|
|
return (
|
|
<div className="space-y-8 animate-in fade-in slide-in-from-bottom-2 duration-500">
|
|
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
|
|
<div>
|
|
<h2 className="text-3xl font-bold tracking-tight">Company Ledger</h2>
|
|
<p className="text-neutral-500 mt-1">Full transaction history and cash flow tracking.</p>
|
|
</div>
|
|
<div className="flex items-center gap-3">
|
|
<Button variant="outline" className="rounded-xl border-neutral-200 gap-2 h-12 px-6 font-bold shadow-sm hover:bg-neutral-50 transition-all">
|
|
<Download className="w-4 h-4" />
|
|
Export CSV
|
|
</Button>
|
|
<Button className="bg-emerald-600 hover:bg-emerald-700 text-white rounded-xl gap-2 h-12 px-8 font-bold shadow-lg shadow-emerald-100 dark:shadow-none transition-all active:scale-95">
|
|
<Plus className="w-4 h-4" />
|
|
New Entry
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
|
{[
|
|
{ label: 'Total Inflow', amount: 452000, color: 'emerald', icon: ArrowUpRight },
|
|
{ label: 'Total Outflow', amount: 128400, color: 'rose', icon: ArrowDownRight },
|
|
{ label: 'Net Balance', amount: 323600, color: 'indigo', icon: Wallet },
|
|
].map((stat, i) => (
|
|
<Card key={i} className="border-none shadow-xl shadow-black/5 bg-white dark:bg-neutral-900 rounded-3xl p-8 relative overflow-hidden group">
|
|
<div className="relative z-10">
|
|
<div className={`w-12 h-12 rounded-2xl bg-${stat.color}-50 dark:bg-${stat.color}-900/20 flex items-center justify-center mb-4`}>
|
|
<stat.icon className={`w-6 h-6 text-${stat.color}-600 dark:text-${stat.color}-400`} />
|
|
</div>
|
|
<p className="text-xs font-bold text-neutral-400 uppercase tracking-widest mb-1">{stat.label}</p>
|
|
<h3 className="text-3xl font-black text-neutral-900 dark:text-neutral-100">{formatCurrency(stat.amount.toString())}</h3>
|
|
</div>
|
|
</Card>
|
|
))}
|
|
</div>
|
|
|
|
<Card className="border-none shadow-2xl shadow-black/5 bg-white dark:bg-neutral-900 rounded-[2.5rem] overflow-hidden">
|
|
<div className="p-8 border-b border-neutral-100 dark:border-neutral-800 flex flex-col md:flex-row md:items-center justify-between gap-4">
|
|
<div className="relative flex-1 max-w-md">
|
|
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-neutral-400" />
|
|
<Input placeholder="Search transactions..." className="pl-10 h-12 rounded-xl bg-neutral-50 dark:bg-neutral-800 border-none shadow-none" />
|
|
</div>
|
|
<div className="flex items-center gap-3">
|
|
<Button variant="outline" className="rounded-xl border-neutral-200 h-12 flex items-center gap-2 font-bold transition-all hover:bg-neutral-50">
|
|
<Filter className="w-4 h-4" />
|
|
All Accounts
|
|
</Button>
|
|
<Button variant="outline" className="rounded-xl border-neutral-200 h-12 flex items-center gap-2 font-bold transition-all hover:bg-neutral-50">
|
|
<Calendar className="w-4 h-4" />
|
|
This Month
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="overflow-x-auto">
|
|
<table className="w-full text-left">
|
|
<thead>
|
|
<tr className="bg-neutral-50/50 dark:bg-neutral-800/50 border-b border-neutral-100 dark:border-neutral-800">
|
|
<th className="p-6 px-8 text-xs font-black uppercase tracking-[0.15em] text-neutral-400">Transaction</th>
|
|
<th className="p-6 text-xs font-black uppercase tracking-[0.15em] text-neutral-400">Account</th>
|
|
<th className="p-6 text-xs font-black uppercase tracking-[0.15em] text-neutral-400">Category</th>
|
|
<th className="p-6 text-xs font-black uppercase tracking-[0.15em] text-neutral-400">Amount</th>
|
|
<th className="p-6 px-8 text-xs font-black uppercase tracking-[0.15em] text-neutral-400 text-right">Date</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody className="divide-y divide-neutral-100 dark:divide-neutral-800">
|
|
{transactions.map((tx) => (
|
|
<tr key={tx.id} className="hover:bg-neutral-50/50 transition-all group">
|
|
<td className="p-6 px-8">
|
|
<div className="flex items-center gap-4">
|
|
<div className={`w-10 h-10 rounded-xl flex items-center justify-center ${tx.type === 'DEBIT' ? 'bg-rose-50 text-rose-600' : 'bg-emerald-50 text-emerald-600'
|
|
}`}>
|
|
{tx.type === 'DEBIT' ? <ArrowDownRight className="w-5 h-5" /> : <ArrowUpRight className="w-5 h-5" />}
|
|
</div>
|
|
<div>
|
|
<p className="font-bold text-neutral-900 dark:text-neutral-100">{tx.description}</p>
|
|
<p className="text-[10px] text-neutral-400 font-bold uppercase tracking-tight">TX-{tx.id.substring(0, 8).toUpperCase()}</p>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td className="p-6">
|
|
<span className="text-sm font-semibold text-neutral-600 dark:text-neutral-400">{tx.account.name}</span>
|
|
</td>
|
|
<td className="p-6">
|
|
<span className="px-2.5 py-1 rounded-lg bg-neutral-100 dark:bg-neutral-800 text-[10px] font-black uppercase text-neutral-500">
|
|
{tx.category?.name || 'Uncategorized'}
|
|
</span>
|
|
</td>
|
|
<td className="p-6">
|
|
<span className={`text-lg font-black ${tx.type === 'DEBIT' ? 'text-rose-600' : 'text-emerald-600'}`}>
|
|
{tx.type === 'DEBIT' ? '-' : '+'}{formatCurrency(tx.amount.toString())}
|
|
</span>
|
|
</td>
|
|
<td className="p-6 px-8 text-right">
|
|
<p className="text-sm font-bold text-neutral-900 dark:text-neutral-100">{new Date(tx.date).toLocaleDateString()}</p>
|
|
<p className="text-[10px] text-neutral-400 uppercase tracking-tighter">14:20 PM</p>
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
{transactions.length === 0 && (
|
|
<div className="p-20 text-center">
|
|
<div className="w-20 h-20 bg-neutral-100 dark:bg-neutral-800 rounded-full flex items-center justify-center mx-auto mb-6">
|
|
<ArrowRightLeft className="w-10 h-10 text-neutral-300" />
|
|
</div>
|
|
<h3 className="text-xl font-black mb-2">No Transactions Found</h3>
|
|
<p className="text-neutral-500 font-medium">Start recording financial activities to see them here.</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
);
|
|
}
|