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.
167 lines
11 KiB
TypeScript
167 lines
11 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { Card, CardHeader, CardTitle, CardContent, CardDescription } from "@/components/ui/card";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Label } from "@/components/ui/label";
|
|
import {
|
|
PieChart,
|
|
Plus,
|
|
Settings2,
|
|
AlertTriangle,
|
|
CheckCircle2,
|
|
ArrowRight,
|
|
Target,
|
|
BarChart,
|
|
Edit2
|
|
} from "lucide-react";
|
|
|
|
const initialBudgets = [
|
|
{ department: 'Engineering (IT)', limit: 250000, spent: 185000, status: 'Healthy', color: 'bg-indigo-600' },
|
|
{ department: 'Marketing', limit: 120000, spent: 115000, status: 'Warning', color: 'bg-rose-500' },
|
|
{ department: 'Human Resources', limit: 80000, spent: 42000, status: 'Healthy', color: 'bg-emerald-500' },
|
|
{ department: 'Customer Success', limit: 60000, spent: 58000, status: 'Critical', color: 'bg-amber-500' },
|
|
];
|
|
|
|
export default function BudgetingPage() {
|
|
const [budgets, setBudgets] = useState(initialBudgets);
|
|
|
|
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">Department Budgeting</h2>
|
|
<p className="text-neutral-500 mt-1 font-medium">Set and monitor expenditure limits across the organization.</p>
|
|
</div>
|
|
<Button className="bg-neutral-900 text-white hover:bg-black rounded-2xl h-14 px-8 font-black shadow-2xl transition-all active:scale-95 group">
|
|
<Plus className="w-5 h-5 mr-1 group-hover:rotate-180 transition-transform duration-500" />
|
|
Create Budget
|
|
</Button>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
|
<Card className="border-none shadow-2xl shadow-indigo-500/10 bg-white dark:bg-neutral-900 rounded-[3rem] p-10 overflow-hidden relative">
|
|
<div className="relative z-10 flex flex-col h-full">
|
|
<div className="flex items-center gap-4 mb-10">
|
|
<div className="w-16 h-16 bg-indigo-50 dark:bg-indigo-900/20 rounded-3xl flex items-center justify-center">
|
|
<Target className="w-8 h-8 text-indigo-600" />
|
|
</div>
|
|
<div>
|
|
<h3 className="text-2xl font-black text-neutral-900 dark:text-neutral-100 italic">Total Budget Allocation</h3>
|
|
<p className="font-bold text-neutral-400 uppercase tracking-widest text-xs mt-1">FY 2024 • Q1 ACTIVE</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex-1 flex flex-col justify-center gap-12">
|
|
<div>
|
|
<div className="flex justify-between items-end mb-4">
|
|
<p className="text-sm font-bold text-neutral-400 uppercase tracking-[0.2em]">Overall Utilization</p>
|
|
<p className="text-6xl font-black text-neutral-900 dark:text-neutral-100 tracking-tighter">78<span className="text-indigo-600 text-4xl">%</span></p>
|
|
</div>
|
|
<div className="h-6 w-full bg-neutral-50 dark:bg-neutral-800 rounded-full overflow-hidden p-1.5 border border-neutral-100 dark:border-neutral-800 shadow-inner">
|
|
<div className="h-full bg-gradient-to-r from-indigo-600 to-indigo-400 rounded-full shadow-[0_0_20px_rgba(79,70,229,0.3)]" style={{ width: '78%' }} />
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 gap-8">
|
|
<div className="space-y-1">
|
|
<p className="text-[10px] font-black text-neutral-400 uppercase tracking-widest">Total Limit</p>
|
|
<p className="text-2xl font-black text-neutral-900 dark:text-neutral-100">$510,000.00</p>
|
|
</div>
|
|
<div className="space-y-1">
|
|
<p className="text-[10px] font-black text-neutral-400 uppercase tracking-widest">Spent to Date</p>
|
|
<p className="text-2xl font-black text-indigo-600">$400,000.00</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Decorative blobs */}
|
|
<div className="absolute top-[-10%] right-[-10%] w-[40%] h-[40%] bg-indigo-500/5 blur-[80px] rounded-full" />
|
|
</Card>
|
|
|
|
<Card className="border-none shadow-2xl shadow-black/5 bg-neutral-900 rounded-[3rem] p-10 text-white flex flex-col justify-center">
|
|
<div className="flex items-center gap-4 mb-8">
|
|
<div className="p-3 bg-white/10 rounded-2xl">
|
|
<AlertTriangle className="w-6 h-6 text-amber-500" />
|
|
</div>
|
|
<div>
|
|
<h3 className="text-xl font-bold">Budget Insights</h3>
|
|
<p className="text-neutral-400 text-sm">Automated analysis of current spend</p>
|
|
</div>
|
|
</div>
|
|
<div className="space-y-6">
|
|
<div className="p-6 bg-white/5 rounded-3xl border border-white/5 hover:bg-white/10 transition-colors group cursor-pointer">
|
|
<div className="flex justify-between items-start mb-2">
|
|
<span className="text-xs font-black text-rose-500 uppercase tracking-widest bg-rose-500/10 px-2 py-1 rounded-lg">Critical Alert</span>
|
|
<ArrowRight className="w-4 h-4 text-neutral-600 group-hover:text-white transition-colors" />
|
|
</div>
|
|
<p className="font-bold text-lg mb-1">Marketing Budget at 96%</p>
|
|
<p className="text-sm text-neutral-400 font-medium leading-relaxed">Marketing spend is significantly higher than projected. Approvals for non-essential spend restricted.</p>
|
|
</div>
|
|
|
|
<div className="p-6 bg-white/5 rounded-3xl border border-white/5 hover:bg-white/10 transition-colors group cursor-pointer">
|
|
<div className="flex justify-between items-start mb-2">
|
|
<span className="text-xs font-black text-emerald-500 uppercase tracking-widest bg-emerald-500/10 px-2 py-1 rounded-lg">Optimization</span>
|
|
<ArrowRight className="w-4 h-4 text-neutral-600 group-hover:text-white transition-colors" />
|
|
</div>
|
|
<p className="font-bold text-lg mb-1">HR Savings Opportunity</p>
|
|
<p className="text-sm text-neutral-400 font-medium leading-relaxed">Current HR spend is 45% below budget. Consider reallocating $15k to Engineering Q2.</p>
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 gap-6">
|
|
<h3 className="text-xl font-black text-neutral-900 dark:text-neutral-100 flex items-center gap-3">
|
|
<BarChart className="w-6 h-6 text-indigo-600" />
|
|
Departmental Breakdown
|
|
</h3>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-6">
|
|
{budgets.map((b, i) => {
|
|
const percent = (b.spent / b.limit) * 100;
|
|
return (
|
|
<Card key={i} className="border-none shadow-xl shadow-black/5 bg-white dark:bg-neutral-900 rounded-[2.5rem] p-8 flex flex-col justify-between group hover:scale-[1.03] transition-all duration-300">
|
|
<div>
|
|
<div className="flex justify-between items-start mb-6">
|
|
<div className={`p-3 rounded-2xl ${b.color} text-white shadow-lg shadow-black/5`}>
|
|
<Settings2 className="w-5 h-5" />
|
|
</div>
|
|
<Button variant="ghost" size="icon" className="rounded-full hover:bg-neutral-100">
|
|
<Edit2 className="w-4 h-4 text-neutral-400" />
|
|
</Button>
|
|
</div>
|
|
<h4 className="font-black text-xl mb-1 text-neutral-900 dark:text-neutral-100">{b.department}</h4>
|
|
<p className="text-[10px] font-black text-neutral-400 uppercase tracking-[0.2em] mb-4">Current Period</p>
|
|
|
|
<div className="space-y-4 mb-8">
|
|
<div className="flex justify-between text-sm font-bold">
|
|
<span className="text-neutral-500">Utilization</span>
|
|
<span className={percent > 90 ? 'text-rose-600' : 'text-neutral-900 dark:text-neutral-100'}>{percent.toFixed(0)}%</span>
|
|
</div>
|
|
<div className="h-2 w-full bg-neutral-50 dark:bg-neutral-800 rounded-full overflow-hidden">
|
|
<div className={`h-full ${b.color} rounded-full transition-all duration-1000 ease-out shadow-[0_0_10px_rgba(0,0,0,0.1)]`} style={{ width: `${percent}%` }} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="pt-6 border-t border-neutral-100 dark:border-neutral-800 space-y-2">
|
|
<div className="flex justify-between items-center">
|
|
<span className="text-xs font-bold text-neutral-400 uppercase tracking-tighter">Spent</span>
|
|
<span className="font-black text-neutral-900 dark:text-neutral-100 text-lg">${(b.spent / 1000).toFixed(0)}k</span>
|
|
</div>
|
|
<div className="flex justify-between items-center opacity-40">
|
|
<span className="text-xs font-bold text-neutral-400 uppercase tracking-tighter">Limit</span>
|
|
<span className="font-bold text-sm">${(b.limit / 1000).toFixed(0)}k</span>
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|