Files
avbeat-com/src/app/admin/accounting/page.tsx
Ryan Salazar (via Claude) 52fe7bceb6 slice 4: accent palette refinement — blue → teal
Replace the four accent-family hex codes site-wide with the teal palette
(keeps the dark theme; only swaps the accent family, not the dark base):

  #3b82f6 (accent primary CTA)   → #5B7C8D  (teal)        [839×]
  #2563eb (accent-dark / hover)  → #4A6473  (darker teal) [44×]
  #60a5fa (info link, lighter)   → #8FB0C3  (mid teal)    [68×]
  #1e3a5f (accent-muted bg)      → #2F4F5F  (navy)        [44×]

tailwind.config.js tokens updated to match (accent/accent-dark/accent-muted).
Sweep also covers tailwind.css. 108 files touched. The dark base (#0d0d0d,
#111, #161616, #1a1a1a etc.) is intentionally untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 23:33:04 +00:00

158 lines
8.0 KiB
TypeScript

'use client';
import React, { useState, useEffect } from 'react';
import Link from 'next/link';
import { useAuth } from '@/contexts/AuthContext';
import { useRouter } from 'next/navigation';
const STAT_CARDS = [
{ key: 'revenue_ytd', label: 'Revenue YTD', color: 'text-green-400', bg: 'bg-green-400/10', href: '/admin/accounting/invoices' },
{ key: 'expenses_ytd', label: 'Expenses YTD', color: 'text-red-400', bg: 'bg-red-400/10', href: '/admin/accounting/expenses' },
{ key: 'net_profit', label: 'Net Profit YTD', color: 'text-blue-400', bg: 'bg-blue-400/10', href: '/admin/accounting/reports' },
{ key: 'outstanding', label: 'Outstanding Invoices', color: 'text-yellow-400', bg: 'bg-yellow-400/10', href: '/admin/accounting/invoices?status=pending' },
{ key: 'unreconciled', label: 'Unreconciled Transactions', color: 'text-orange-400', bg: 'bg-orange-400/10', href: '/admin/accounting/reconciliation' },
{ key: 'commissions_outstanding', label: 'Commissions Outstanding', color: 'text-purple-400', bg: 'bg-purple-400/10', href: '/admin/accounting/commissions' },
];
function fmt(cents: number) {
return '$' + (cents / 100).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}
export default function AccountingDashboard() {
const { user, loading } = useAuth();
const router = useRouter();
const [stats, setStats] = useState<Record<string, number>>({});
const [siteRevenue, setSiteRevenue] = useState<any[]>([]);
const [topClients, setTopClients] = useState<any[]>([]);
const [loadingData, setLoadingData] = useState(true);
useEffect(() => {
if (!loading && !user) router.push('/login');
}, [user, loading, router]);
useEffect(() => {
if (!user) return;
fetch('/api/accounting/dashboard')
.then(r => r.json())
.then(d => {
setStats(d.stats ?? {});
setSiteRevenue(d.siteRevenue ?? []);
setTopClients(d.topClients ?? []);
})
.catch(() => {})
.finally(() => setLoadingData(false));
}, [user]);
if (loading || loadingData) return (
<div className="min-h-screen bg-[#0a0a0a] flex items-center justify-center">
<div className="w-8 h-8 border-2 border-[#5B7C8D] border-t-transparent rounded-full animate-spin" />
</div>
);
if (!user) return null;
return (
<div className="min-h-screen bg-[#0a0a0a] text-white p-6">
<div className="max-w-7xl mx-auto space-y-6">
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold text-white">Accounting Dashboard</h1>
<p className="text-[#888] text-sm mt-1">Relevant Media Properties, LLC</p>
</div>
<div className="flex gap-2 flex-wrap">
<Link href="/admin/accounting/clients" className="px-3 py-1.5 bg-[#1a2535] border border-[#2a3a50] rounded text-xs text-[#5B7C8D] hover:bg-[#0d1a2d] transition-colors">+ New Client</Link>
<Link href="/admin/accounting/orders" className="px-3 py-1.5 bg-[#1a2535] border border-[#2a3a50] rounded text-xs text-[#5B7C8D] hover:bg-[#0d1a2d] transition-colors">+ New Order</Link>
<Link href="/admin/accounting/invoices" className="px-3 py-1.5 bg-[#1a2535] border border-[#2a3a50] rounded text-xs text-[#5B7C8D] hover:bg-[#0d1a2d] transition-colors">+ New Invoice</Link>
<Link href="/admin/accounting/expenses" className="px-3 py-1.5 bg-[#1a2535] border border-[#2a3a50] rounded text-xs text-[#5B7C8D] hover:bg-[#0d1a2d] transition-colors">+ Log Expense</Link>
<Link href="/admin/accounting/reconciliation" className="px-3 py-1.5 bg-[#1a2535] border border-[#2a3a50] rounded text-xs text-[#5B7C8D] hover:bg-[#0d1a2d] transition-colors">Upload Bank CSV</Link>
</div>
</div>
{/* Stat Cards */}
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-3">
{STAT_CARDS.map(c => (
<Link key={c.key} href={c.href} className="bg-[#111] border border-[#252525] rounded-lg p-4 hover:border-[#333] transition-colors">
<p className={`text-lg font-bold ${c.color}`}>{fmt(stats[c.key] ?? 0)}</p>
<p className="text-xs text-[#888] mt-1">{c.label}</p>
</Link>
))}
</div>
{/* Site Revenue Table */}
<div className="bg-[#111] border border-[#252525] rounded-lg overflow-hidden">
<div className="px-4 py-3 border-b border-[#252525]">
<h2 className="text-sm font-semibold text-white">Revenue by Site</h2>
</div>
<table className="w-full text-sm">
<thead>
<tr className="border-b border-[#1a1a1a]">
{['Site','Revenue MTD','Revenue YTD','Outstanding','Active Orders'].map(h => (
<th key={h} className="px-4 py-2 text-left text-xs text-[#555] font-medium">{h}</th>
))}
</tr>
</thead>
<tbody>
{siteRevenue.length === 0 ? (
<tr><td colSpan={5} className="px-4 py-6 text-center text-[#555] text-xs">No data yet</td></tr>
) : siteRevenue.map((row: any) => (
<tr key={row.site} className="border-b border-[#1a1a1a] hover:bg-[#161616]">
<td className="px-4 py-2 text-white font-medium capitalize">{row.site}</td>
<td className="px-4 py-2 text-green-400">{fmt(row.revenue_mtd ?? 0)}</td>
<td className="px-4 py-2 text-green-400">{fmt(row.revenue_ytd ?? 0)}</td>
<td className="px-4 py-2 text-yellow-400">{fmt(row.outstanding ?? 0)}</td>
<td className="px-4 py-2 text-[#ccc]">{row.active_orders ?? 0}</td>
</tr>
))}
</tbody>
</table>
</div>
{/* Top Clients */}
<div className="bg-[#111] border border-[#252525] rounded-lg overflow-hidden">
<div className="px-4 py-3 border-b border-[#252525]">
<h2 className="text-sm font-semibold text-white">Top 5 Clients YTD</h2>
</div>
<table className="w-full text-sm">
<thead>
<tr className="border-b border-[#1a1a1a]">
{['Client','Revenue YTD','Active Orders'].map(h => (
<th key={h} className="px-4 py-2 text-left text-xs text-[#555] font-medium">{h}</th>
))}
</tr>
</thead>
<tbody>
{topClients.length === 0 ? (
<tr><td colSpan={3} className="px-4 py-6 text-center text-[#555] text-xs">No data yet</td></tr>
) : topClients.map((c: any) => (
<tr key={c.id} className="border-b border-[#1a1a1a] hover:bg-[#161616]">
<td className="px-4 py-2 text-white">{c.company_name}</td>
<td className="px-4 py-2 text-green-400">{fmt(c.revenue_ytd ?? 0)}</td>
<td className="px-4 py-2 text-[#ccc]">{c.active_orders ?? 0}</td>
</tr>
))}
</tbody>
</table>
</div>
{/* Sub-nav links */}
<div className="grid grid-cols-2 md:grid-cols-5 gap-2">
{[
{ label: 'Clients', href: '/admin/accounting/clients' },
{ label: 'Orders', href: '/admin/accounting/orders' },
{ label: 'Invoices', href: '/admin/accounting/invoices' },
{ label: 'Staff & Commissions', href: '/admin/accounting/staff' },
{ label: 'Expenses', href: '/admin/accounting/expenses' },
{ label: 'Reconciliation', href: '/admin/accounting/reconciliation' },
{ label: 'Commissions', href: '/admin/accounting/commissions' },
{ label: 'Reports', href: '/admin/accounting/reports' },
{ label: 'Documents', href: '/admin/accounting/documents' },
].map(l => (
<Link key={l.href} href={l.href} className="bg-[#111] border border-[#252525] rounded-lg p-3 text-center text-sm text-[#888] hover:text-white hover:border-[#5B7C8D] transition-colors">
{l.label}
</Link>
))}
</div>
</div>
</div>
);
}