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.
140 lines
8.9 KiB
TypeScript
140 lines
8.9 KiB
TypeScript
import { prisma } from "@/lib/prisma";
|
|
import {
|
|
Users,
|
|
BarChart3,
|
|
Wallet,
|
|
Clock,
|
|
ArrowUpRight,
|
|
} from "lucide-react";
|
|
import { Card } from "@/components/ui/card";
|
|
import { Button } from "@/components/ui/button";
|
|
import { formatCurrency } from "@/lib/utils";
|
|
import Link from "next/link";
|
|
|
|
export default async function AdminDashboard() {
|
|
const [pendingReimbursements, pendingOvertime, totalBudget, recentTransactions] = await Promise.all([
|
|
prisma.reimbursement.count({ where: { status: 'PENDING' } }),
|
|
prisma.overtime.count({ where: { status: 'PENDING' } }),
|
|
prisma.budget.aggregate({ _sum: { amount: true } }),
|
|
prisma.companyTransaction.findMany({
|
|
orderBy: { date: 'desc' },
|
|
take: 5
|
|
})
|
|
]);
|
|
|
|
const totalPending = pendingReimbursements + pendingOvertime;
|
|
|
|
return (
|
|
<div className="space-y-10 animate-in fade-in slide-in-from-bottom-4 duration-700">
|
|
<div className="flex flex-col md:flex-row md:items-center justify-between gap-6">
|
|
<div>
|
|
<h2 className="text-4xl font-extrabold tracking-tight text-neutral-900 dark:text-white">Admin Console</h2>
|
|
<p className="text-neutral-500 mt-2 font-medium text-lg">Real-time overview of company finances and worker requests.</p>
|
|
</div>
|
|
<Link href="/admin/approvals">
|
|
<Button className="bg-emerald-600 hover:bg-emerald-700 text-white rounded-2xl h-14 px-8 font-black shadow-2xl shadow-emerald-200 dark:shadow-none transition-all active:scale-95 flex items-center gap-2">
|
|
<Users className="w-5 h-5" />
|
|
Review Requests ({totalPending})
|
|
</Button>
|
|
</Link>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
|
|
{[
|
|
{ label: 'Total Budget Allocated', value: formatCurrency(totalBudget._sum.amount?.toString() || '0'), icon: Wallet, color: 'emerald', trend: '+5.2% vs last month' },
|
|
{ label: 'Pending Approvals', value: totalPending.toString(), icon: Clock, color: 'amber', trend: `${pendingReimbursements} claims, ${pendingOvertime} OT` },
|
|
{ label: 'Monthly Revenue', value: formatCurrency(245000), icon: BarChart3, color: 'indigo', trend: 'On track for Q1' },
|
|
{ label: 'Active Personnel', value: '142', icon: Users, color: 'violet', trend: '+3 new this week' },
|
|
].map((stat, i) => (
|
|
<Card key={i} className="group border-none shadow-2xl shadow-black/5 bg-white dark:bg-neutral-900 rounded-[2.5rem] p-8 hover:-translate-y-2 transition-all duration-500 overflow-hidden relative">
|
|
<div className="relative z-10">
|
|
<div className={`w-14 h-14 rounded-2xl bg-neutral-50 dark:bg-neutral-800 flex items-center justify-center mb-6 group-hover:scale-110 transition-transform duration-500`}>
|
|
<stat.icon className={`w-7 h-7 text-neutral-900 dark:text-neutral-100`} />
|
|
</div>
|
|
<p className="text-xs font-black text-neutral-400 uppercase tracking-widest mb-1">{stat.label}</p>
|
|
<div className="flex items-baseline gap-2">
|
|
<h3 className="text-3xl font-black text-neutral-900 dark:text-neutral-100">{stat.value}</h3>
|
|
</div>
|
|
<p className="mt-4 text-[10px] font-bold text-neutral-500 flex items-center gap-1">
|
|
{stat.trend}
|
|
</p>
|
|
</div>
|
|
</Card>
|
|
))}
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 xl:grid-cols-3 gap-8">
|
|
<Card className="xl:col-span-2 border-none shadow-2xl shadow-black/5 bg-white dark:bg-neutral-900 rounded-[3rem] p-10 overflow-hidden">
|
|
<div className="flex items-center justify-between mb-10">
|
|
<h3 className="text-2xl font-black tracking-tight">Financial Ledger</h3>
|
|
<Link href="/admin/ledger">
|
|
<Button variant="ghost" className="text-emerald-600 font-bold hover:bg-emerald-50 rounded-xl px-4 py-2">View Full Ledger</Button>
|
|
</Link>
|
|
</div>
|
|
<div className="space-y-6">
|
|
{(recentTransactions as any[]).map((tx) => (
|
|
<div key={tx.id} className="flex items-center justify-between p-6 rounded-[2rem] hover:bg-neutral-50 dark:hover:bg-neutral-800/50 transition-all group border border-transparent hover:border-neutral-100 dark:hover:border-neutral-800">
|
|
<div className="flex items-center gap-6">
|
|
<div className={`w-14 h-14 rounded-2xl ${tx.type === 'DEBIT' ? 'bg-rose-50 text-rose-600' : 'bg-emerald-50 text-emerald-600'} flex items-center justify-center font-black shadow-sm`}>
|
|
{tx.type === 'DEBIT' ? '-' : '+'}
|
|
</div>
|
|
<div>
|
|
<p className="font-bold text-lg text-neutral-900 dark:text-neutral-100">{tx.description}</p>
|
|
<p className="text-xs text-neutral-400 font-medium">{new Date(tx.date).toLocaleDateString()}</p>
|
|
</div>
|
|
</div>
|
|
<div className="text-right">
|
|
<p className={`font-black text-xl ${tx.type === 'DEBIT' ? 'text-rose-600' : 'text-emerald-600'}`}>
|
|
{tx.type === 'DEBIT' ? '-' : '+'}{formatCurrency(tx.amount.toString())}
|
|
</p>
|
|
<span className="text-[10px] font-black uppercase text-neutral-400">Success</span>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</Card>
|
|
|
|
<div className="space-y-8">
|
|
<Card className="border-none shadow-2xl shadow-emerald-500/10 bg-gradient-to-br from-emerald-600 to-teal-700 rounded-[3rem] p-10 text-white relative overflow-hidden group">
|
|
<div className="relative z-10">
|
|
<h3 className="text-xl font-bold mb-6 flex items-center gap-2">
|
|
<BarChart3 className="w-5 h-5" />
|
|
Budget Health
|
|
</h3>
|
|
<p className="text-emerald-100 font-medium leading-relaxed mb-8 text-sm">
|
|
Total budget utilization is at <span className="text-white font-bold text-lg">68%</span>. Marketing and Sales are nearing their monthly limits.
|
|
</p>
|
|
<Link href="/admin/budgeting">
|
|
<Button className="w-full bg-white text-emerald-700 hover:bg-emerald-50 rounded-2xl h-14 font-black shadow-xl transition-all active:scale-95">
|
|
Manage Budgets
|
|
</Button>
|
|
</Link>
|
|
</div>
|
|
</Card>
|
|
|
|
<Card className="border-none shadow-2xl shadow-black/5 bg-white dark:bg-neutral-900 rounded-[3rem] p-10">
|
|
<h3 className="text-xl font-black mb-8">Departmental Spend</h3>
|
|
<div className="space-y-6">
|
|
{[
|
|
{ label: 'Marketing', value: 85, color: 'bg-emerald-500' },
|
|
{ label: 'Engineering', value: 62, color: 'bg-indigo-500' },
|
|
{ label: 'Operations', value: 48, color: 'bg-violet-500' },
|
|
].map((item) => (
|
|
<div key={item.label} className="space-y-2">
|
|
<div className="flex justify-between text-sm font-bold">
|
|
<span className="text-neutral-500">{item.label}</span>
|
|
<span className={item.value > 80 ? 'text-rose-500' : ''}>{item.value}%</span>
|
|
</div>
|
|
<div className="w-full h-2 bg-neutral-100 dark:bg-neutral-800 rounded-full overflow-hidden">
|
|
<div className={`h-full ${item.color} rounded-full transition-all duration-1000`} style={{ width: `${item.value}%` }} />
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|