import { useState } from 'react';
import { useAuth } from '../../contexts/AuthContext';
import { useQuery } from '@tanstack/react-query';
import { AppLayout } from '../../components/layout/AppLayout';
import { quickbooksApi, type QuickbooksAnalyticsResponse } from '../../api/quickbooks';
import { LoadingSpinner } from '../../components/ui/LoadingSpinner';
import {
DollarSign,
FileText,
TrendingUp,
TrendingDown,
AlertTriangle,
CheckCircle2,
Users,
RefreshCw,
BarChart3,
ArrowUpRight,
ArrowDownRight,
} from 'lucide-react';
// ─── Helpers ──────────────────────────────────────────────────────────────
function formatCurrency(value: number | null | undefined): string {
if (value == null || value === 0) return '$0';
if (Math.abs(value) >= 1_000_000) return `$${(value / 1_000_000).toFixed(1)}M`;
if (Math.abs(value) >= 1_000) return `$${(value / 1_000).toFixed(1)}k`;
return `$${value.toFixed(2)}`;
}
function formatPercent(value: number): string {
return `${value.toFixed(1)}%`;
}
// ─── Stat Card ────────────────────────────────────────────────────────────
function StatCard({
icon: Icon,
label,
value,
sublabel,
trend,
color,
}: {
icon: React.ElementType;
label: string;
value: string;
sublabel?: string;
trend?: { value: string; positive: boolean };
color: string;
}) {
return (
<div className="rounded-xl border border-surface-200 bg-white p-4 sm:p-5">
<div className="flex items-start justify-between">
<div className={`flex h-10 w-10 shrink-0 items-center justify-center rounded-lg ${color}`}>
<Icon className="h-5 w-5" />
</div>
{trend && (
<div
className={`flex items-center gap-0.5 text-xs font-medium ${
trend.positive ? 'text-emerald-600' : 'text-red-600'
}`}
>
{trend.positive ? (
<ArrowUpRight className="h-3.5 w-3.5" />
) : (
<ArrowDownRight className="h-3.5 w-3.5" />
)}
{trend.value}
</div>
)}
</div>
<div className="mt-3">
<p className="text-2xl font-bold text-surface-900">{value}</p>
<p className="mt-0.5 text-sm text-surface-500">{label}</p>
{sublabel && <p className="mt-0.5 text-xs text-surface-400">{sublabel}</p>}
</div>
</div>
);
}
// ─── AR Aging Bar ─────────────────────────────────────────────────────────
function ArAgingBar({
label,
amount,
total,
colorClass,
}: {
label: string;
amount: number;
total: number;
colorClass: string;
}) {
const pct = total > 0 ? (amount / total) * 100 : 0;
return (
<div className="flex items-center gap-3">
<span className="w-24 shrink-0 text-right text-sm text-surface-600">{label}</span>
<div className="flex-1">
<div className="flex h-6 overflow-hidden rounded-md bg-surface-100">
<div
className={`${colorClass} h-full transition-all duration-500`}
style={{ width: `${pct}%` }}
/>
</div>
</div>
<span className="w-20 shrink-0 text-sm font-medium text-surface-900">{formatCurrency(amount)}</span>
</div>
);
}
// ─── Main Page ────────────────────────────────────────────────────────────
export function QuickBooksOverviewPage() {
const { user } = useAuth();
const companyId = user?.tenantId as string;
const [periodDays, setPeriodDays] = useState(90);
const { data, isLoading, error, refetch } = useQuery<QuickbooksAnalyticsResponse>({
queryKey: ['quickbooks-analytics', companyId, periodDays],
queryFn: () => quickbooksApi.getAnalytics(companyId),
staleTime: 2 * 60 * 1000,
});
const analytics = data?.data;
if (isLoading) {
return (
<AppLayout title="QuickBooks Overview">
<div className="flex items-center justify-center py-20">
<LoadingSpinner size="lg" />
</div>
</AppLayout>
);
}
if (error) {
return (
<AppLayout title="QuickBooks Overview">
<div className="flex flex-col items-center justify-center py-20 text-center">
<AlertTriangle className="h-12 w-12 text-amber-400" />
<h3 className="mt-4 text-lg font-semibold text-surface-900">Failed to Load Data</h3>
<p className="mt-1 text-sm text-surface-500 max-w-md">
{error instanceof Error ? error.message : 'Could not load QuickBooks analytics. Make sure QuickBooks is connected in Integrations.'}
</p>
<button
onClick={() => refetch()}
className="mt-4 flex items-center gap-2 rounded-lg bg-brand-600 px-4 py-2.5 text-sm font-medium text-white hover:bg-brand-700 min-h-[44px]"
>
<RefreshCw className="h-4 w-4" />
Try Again
</button>
</div>
</AppLayout>
);
}
if (!analytics) {
return (
<AppLayout title="QuickBooks Overview">
<div className="flex flex-col items-center justify-center py-20 text-center">
<BarChart3 className="h-12 w-12 text-surface-300" />
<h3 className="mt-4 text-lg font-semibold text-surface-900">No Data Available</h3>
<p className="mt-1 text-sm text-surface-500 max-w-md">
Connect QuickBooks in the Integrations page to see your financial dashboard.
</p>
</div>
</AppLayout>
);
}
const revenue = analytics.revenue_summary;
const expenses = analytics.expense_breakdown;
const arAging = analytics.ar_aging;
const topCustomers = analytics.top_customers || [];
const collectionRate =
revenue.total_revenue > 0
? (revenue.paid_revenue / revenue.total_revenue) * 100
: 0;
const profitMargin =
revenue.total_revenue > 0
? ((revenue.total_revenue - expenses.total_expenses) / revenue.total_revenue) * 100
: 0;
return (
<AppLayout title="QuickBooks Overview">
<div className="space-y-6">
{/* Header */}
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<div>
<h1 className="text-xl font-bold text-surface-900">QuickBooks Dashboard</h1>
<p className="text-sm text-surface-500">
Financial overview for the last {periodDays} days
</p>
</div>
<div className="flex items-center gap-2">
<select
value={periodDays}
onChange={(e) => setPeriodDays(Number(e.target.value))}
className="rounded-lg border border-surface-200 bg-white px-3 py-2 text-sm font-medium text-surface-700 min-h-[44px]"
>
<option value={30}>Last 30 days</option>
<option value={60}>Last 60 days</option>
<option value={90}>Last 90 days</option>
<option value={180}>Last 6 months</option>
<option value={365}>Last year</option>
</select>
<button
onClick={() => refetch()}
className="flex items-center gap-1.5 rounded-lg border border-surface-200 bg-white px-3 py-2 text-sm font-medium text-surface-700 hover:bg-surface-50 min-h-[44px]"
>
<RefreshCw className="h-4 w-4" />
Refresh
</button>
</div>
</div>
{/* Summary Stats */}
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
<StatCard
icon={DollarSign}
label="Total Revenue"
value={formatCurrency(revenue.total_revenue)}
sublabel={`${revenue.total_invoices} invoices`}
color="bg-emerald-100 text-emerald-600"
/>
<StatCard
icon={CheckCircle2}
label="Collected"
value={formatCurrency(revenue.paid_revenue)}
sublabel={`${formatPercent(collectionRate)} collection rate`}
color="bg-blue-100 text-blue-600"
/>
<StatCard
icon={TrendingDown}
label="Total Expenses"
value={formatCurrency(expenses.total_expenses)}
sublabel={`${expenses.expenses_by_category.length} categories`}
color="bg-red-100 text-red-600"
/>
<StatCard
icon={TrendingUp}
label="Profit Margin"
value={formatPercent(Math.max(0, profitMargin))}
sublabel={profitMargin < 0 ? 'Below cost' : 'Healthy margin'}
color="bg-indigo-100 text-indigo-600"
/>
</div>
{/* AR Aging + Expense Breakdown */}
<div className="grid grid-cols-1 gap-6 lg:grid-cols-2">
{/* AR Aging */}
<div className="rounded-xl border border-surface-200 bg-white p-5">
<div className="mb-4 flex items-center gap-2">
<AlertTriangle className="h-5 w-5 text-amber-500" />
<h2 className="text-sm font-semibold text-surface-900">Accounts Receivable Aging</h2>
</div>
<p className="mb-4 text-sm text-surface-500">
Unpaid invoices: {formatCurrency(arAging.total_unpaid)}
</p>
<div className="space-y-3">
<ArAgingBar
label="Current"
amount={arAging.current}
total={arAging.total_unpaid}
colorClass="bg-emerald-500"
/>
<ArAgingBar
label="Overdue 30"
amount={arAging.overdue_30}
total={arAging.total_unpaid}
colorClass="bg-amber-500"
/>
<ArAgingBar
label="Overdue 60"
amount={arAging.overdue_60}
total={arAging.total_unpaid}
colorClass="bg-orange-500"
/>
<ArAgingBar
label="Overdue 90+"
amount={arAging.overdue_90_plus}
total={arAging.total_unpaid}
colorClass="bg-red-500"
/>
</div>
</div>
{/* Expense Breakdown */}
<div className="rounded-xl border border-surface-200 bg-white p-5">
<div className="mb-4 flex items-center gap-2">
<FileText className="h-5 w-5 text-red-500" />
<h2 className="text-sm font-semibold text-surface-900">Expenses by Category</h2>
</div>
<p className="mb-4 text-sm text-surface-500">
Total: {formatCurrency(expenses.total_expenses)}
</p>
<div className="space-y-3">
{expenses.expenses_by_category
.sort((a, b) => b.total - a.total)
.slice(0, 6)
.map((cat) => {
const pct = expenses.total_expenses > 0 ? (cat.total / expenses.total_expenses) * 100 : 0;
return (
<div key={cat.category}>
<div className="flex items-center justify-between text-sm">
<span className="font-medium text-surface-700">{cat.category}</span>
<span className="text-surface-500">
{formatCurrency(cat.total)}{' '}
<span className="text-surface-400">({pct.toFixed(0)}%)</span>
</span>
</div>
<div className="mt-1 h-2 overflow-hidden rounded-full bg-surface-100">
<div
className="h-full rounded-full bg-brand-500 transition-all duration-500"
style={{ width: `${pct}%` }}
/>
</div>
<p className="mt-0.5 text-xs text-surface-400">{cat.count} transactions</p>
</div>
);
})}
{expenses.expenses_by_category.length === 0 && (
<p className="text-sm text-surface-400">No expenses recorded</p>
)}
</div>
</div>
</div>
{/* Top Customers */}
<div className="rounded-xl border border-surface-200 bg-white p-5">
<div className="mb-4 flex items-center gap-2">
<Users className="h-5 w-5 text-blue-500" />
<h2 className="text-sm font-semibold text-surface-900">Top Customers by Revenue</h2>
</div>
{topCustomers.length > 0 ? (
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-surface-200">
<th className="pb-3 text-left text-xs font-medium text-surface-500">#</th>
<th className="pb-3 text-left text-xs font-medium text-surface-500">Customer</th>
<th className="pb-3 text-right text-xs font-medium text-surface-500">Invoices</th>
<th className="pb-3 text-right text-xs font-medium text-surface-500">Total</th>
<th className="pb-3 text-right text-xs font-medium text-surface-500">Balance</th>
</tr>
</thead>
<tbody>
{topCustomers.slice(0, 10).map((customer, i) => (
<tr key={customer.customer_name} className="border-b border-surface-100 last:border-b-0">
<td className="py-3 text-surface-400">{i + 1}</td>
<td className="py-3 font-medium text-surface-900">{customer.customer_name}</td>
<td className="py-3 text-right text-surface-600">{customer.invoice_count}</td>
<td className="py-3 text-right font-medium text-surface-900">
{formatCurrency(customer.total_amount)}
</td>
<td className={`py-3 text-right font-medium ${customer.balance > 0 ? 'text-amber-600' : 'text-emerald-600'}`}>
{formatCurrency(customer.balance)}
</td>
</tr>
))}
</tbody>
</table>
</div>
) : (
<p className="text-sm text-surface-400">No customer data available</p>
)}
</div>
</div>
</AppLayout>
);
}