import React, { useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import { AppLayout } from '../../components/layout/AppLayout';
import { multiMarketApi, type MarketComparisonRow, type MarketDrillDownResponse } from '../../api/multiMarketApi';
import {
BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer,
Cell, PieChart, Pie, AreaChart, Area,
} from 'recharts';
import {
Map,
TrendingUp,
TrendingDown,
AlertTriangle,
CheckCircle,
Users,
DollarSign,
Target,
ArrowUpRight,
ArrowDownRight,
Loader2,
ChevronRight,
ChevronDown,
BarChart3,
PieChart as PieChartIcon,
} from 'lucide-react';
const formatCurrency = (value: number) => {
if (value >= 1_000_000) return `$${(value / 1_000_000).toFixed(1)}M`;
if (value >= 1_000) return `$${(value / 1_000).toFixed(0)}K`;
return `$${value.toFixed(0)}`;
};
const formatPercent = (value: number) => `${value.toFixed(1)}%`;
// ============================================================================
// Market Overview Section
// ============================================================================
function MarketOverview() {
const { data, isLoading } = useQuery({
queryKey: ['multi-market', 'overview'],
queryFn: multiMarketApi.getOverview,
});
if (isLoading) {
return (
<div className="flex h-64 items-center justify-center">
<Loader2 className="h-6 w-6 animate-spin text-brand-600" />
</div>
);
}
if (!data?.markets?.length) {
return (
<div className="rounded-xl border border-surface-200 bg-white p-8 text-center">
<Map className="mx-auto h-12 w-12 text-surface-300" />
<p className="mt-4 text-lg font-medium text-surface-700">No market data available</p>
<p className="mt-2 text-sm text-surface-500">Connect your CRM to start tracking markets</p>
</div>
);
}
return (
<div className="space-y-6">
{/* Summary Cards */}
<div className="grid grid-cols-2 gap-4 sm:grid-cols-4">
<div className="rounded-xl border border-surface-200 bg-white p-4">
<div className="flex items-center gap-2 text-sm text-surface-500">
<Map className="h-4 w-4" />
Markets
</div>
<p className="mt-2 text-2xl font-bold text-surface-900">{data.total_markets}</p>
</div>
<div className="rounded-xl border border-surface-200 bg-white p-4">
<div className="flex items-center gap-2 text-sm text-surface-500">
<DollarSign className="h-4 w-4" />
Total Revenue
</div>
<p className="mt-2 text-2xl font-bold text-surface-900">
{formatCurrency(data.markets.reduce((sum, m) => sum + m.total_revenue, 0))}
</p>
</div>
<div className="rounded-xl border border-surface-200 bg-white p-4">
<div className="flex items-center gap-2 text-sm text-surface-500">
<Target className="h-4 w-4" />
Avg Close Rate
</div>
<p className="mt-2 text-2xl font-bold text-surface-900">
{formatPercent(data.markets.reduce((sum, m) => sum + m.close_rate, 0) / data.total_markets)}
</p>
</div>
<div className="rounded-xl border border-surface-200 bg-white p-4">
<div className="flex items-center gap-2 text-sm text-surface-500">
<Users className="h-4 w-4" />
Active Contacts
</div>
<p className="mt-2 text-2xl font-bold text-surface-900">
{data.markets.reduce((sum, m) => sum + m.active_contacts, 0).toLocaleString()}
</p>
</div>
</div>
{/* Revenue by Market Chart */}
<div className="rounded-xl border border-surface-200 bg-white p-6">
<h3 className="mb-4 flex items-center gap-2 text-lg font-semibold text-surface-900">
<BarChart3 className="h-5 w-5 text-brand-600" />
Revenue by Market
</h3>
<ResponsiveContainer width="100%" height={300}>
<BarChart data={data.markets.slice(0, 10)}>
<CartesianGrid strokeDasharray="3 3" stroke="#e5e7eb" />
<XAxis dataKey="market" tick={{ fontSize: 12 }} />
<YAxis tick={{ fontSize: 12 }} tickFormatter={formatCurrency} />
<Tooltip
formatter={(value: number) => formatCurrency(value)}
contentStyle={{ borderRadius: '8px', border: '1px solid #e5e7eb' }}
/>
<Legend />
<Bar dataKey="total_revenue" name="Revenue" fill="#00A846" radius={[4, 4, 0, 0]} />
<Bar dataKey="avg_deal_size" name="Avg Deal" fill="#008036" radius={[4, 4, 0, 0]} />
</BarChart>
</ResponsiveContainer>
</div>
</div>
);
}
// ============================================================================
// Market Comparison Table
// ============================================================================
function MarketComparison() {
const { data, isLoading } = useQuery({
queryKey: ['multi-market', 'comparison'],
queryFn: multiMarketApi.getComparison,
});
if (isLoading) {
return (
<div className="flex h-64 items-center justify-center">
<Loader2 className="h-6 w-6 animate-spin text-brand-600" />
</div>
);
}
if (!data?.markets?.length) {
return (
<div className="rounded-xl border border-surface-200 bg-white p-8 text-center">
<Map className="mx-auto h-12 w-12 text-surface-300" />
<p className="mt-4 text-lg font-medium text-surface-700">No market data available</p>
</div>
);
}
const avgCloseRate = data.averages?.close_rate ?? 0;
const avgRevenue = data.averages?.revenue ?? 0;
return (
<div className="space-y-6">
{/* Top/Bottom Performers */}
<div className="grid gap-4 sm:grid-cols-2">
{data.top_performers?.length > 0 && (
<div className="rounded-xl border border-green-200 bg-green-50 p-4">
<h4 className="flex items-center gap-2 text-sm font-semibold text-green-800">
<TrendingUp className="h-4 w-4" />
Top Performers
</h4>
<div className="mt-3 space-y-2">
{data.top_performers.map((m: MarketComparisonRow) => (
<div key={m.market} className="flex items-center justify-between">
<span className="text-sm font-medium text-green-900">{m.market}</span>
<span className="text-sm text-green-700">{formatPercent(m.close_rate)} CR</span>
</div>
))}
</div>
</div>
)}
{data.bottom_performers?.length > 0 && (
<div className="rounded-xl border border-orange-200 bg-orange-50 p-4">
<h4 className="flex items-center gap-2 text-sm font-semibold text-orange-800">
<TrendingDown className="h-4 w-4" />
Bottom Performers
</h4>
<div className="mt-3 space-y-2">
{data.bottom_performers.map((m: MarketComparisonRow) => (
<div key={m.market} className="flex items-center justify-between">
<span className="text-sm font-medium text-orange-900">{m.market}</span>
<span className="text-sm text-orange-700">{formatPercent(m.close_rate)} CR</span>
</div>
))}
</div>
</div>
)}
</div>
{/* Comparison Table */}
<div className="overflow-x-auto rounded-xl border border-surface-200 bg-white">
<table className="w-full text-sm">
<thead className="border-b border-surface-200 bg-surface-50">
<tr>
<th className="px-4 py-3 text-left font-semibold text-surface-700">Market</th>
<th className="px-4 py-3 text-right font-semibold text-surface-700">Revenue</th>
<th className="px-4 py-3 text-right font-semibold text-surface-700">Close Rate</th>
<th className="px-4 py-3 text-right font-semibold text-surface-700">Avg Deal</th>
<th className="px-4 py-3 text-right font-semibold text-surface-700">Market Share</th>
<th className="px-4 py-3 text-right font-semibold text-surface-700">Deals/Owner</th>
</tr>
</thead>
<tbody>
{data.markets.map((m: MarketComparisonRow, idx: number) => (
<tr key={m.market} className={`border-b border-surface-100 ${idx % 2 === 0 ? 'bg-white' : 'bg-surface-50/50'}`}>
<td className="px-4 py-3 font-medium text-surface-900">{m.market}</td>
<td className="px-4 py-3 text-right">
<span className={m.revenue >= avgRevenue ? 'text-green-600' : 'text-red-600'}>
{formatCurrency(m.revenue)}
</span>
</td>
<td className="px-4 py-3 text-right">
<span className={`inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium ${
m.close_rate >= avgCloseRate
? 'bg-green-100 text-green-700'
: 'bg-red-100 text-red-700'
}`}>
{formatPercent(m.close_rate)}
{m.close_rate >= avgCloseRate ? <ArrowUpRight className="h-3 w-3" /> : <ArrowDownRight className="h-3 w-3" />}
</span>
</td>
<td className="px-4 py-3 text-right">{formatCurrency(m.avg_deal_size)}</td>
<td className="px-4 py-3 text-right">{formatPercent(m.market_share)}</td>
<td className="px-4 py-3 text-right">{m.deals_per_owner.toFixed(1)}</td>
</tr>
))}
</tbody>
</table>
</div>
{/* Market Share Pie Chart */}
<div className="rounded-xl border border-surface-200 bg-white p-6">
<h3 className="mb-4 flex items-center gap-2 text-lg font-semibold text-surface-900">
<PieChartIcon className="h-5 w-5 text-brand-600" />
Revenue Distribution
</h3>
<ResponsiveContainer width="100%" height={300}>
<PieChart>
<Pie
data={data.markets}
dataKey="revenue"
nameKey="market"
cx="50%"
cy="50%"
outerRadius={100}
innerRadius={60}
paddingAngle={2}
>
{data.markets.map((_entry: any, index: number) => (
<Cell key={`cell-${index}`} fill={['#00A846', '#008036', '#006628', '#33C75F', '#66D98A', '#99E6B1'][index % 6]} />
))}
</Pie>
<Tooltip formatter={(value: number) => formatCurrency(value)} />
<Legend />
</PieChart>
</ResponsiveContainer>
</div>
</div>
);
}
// ============================================================================
// Market Drill-Down
// ============================================================================
function MarketDrillDown({ market, onBack }: { market: string; onBack: () => void }) {
const { data, isLoading } = useQuery({
queryKey: ['multi-market', 'drilldown', market],
queryFn: () => multiMarketApi.getDrillDown(market),
});
if (isLoading) {
return (
<div className="flex h-64 items-center justify-center">
<Loader2 className="h-6 w-6 animate-spin text-brand-600" />
</div>
);
}
if (!data) return null;
const { summary, pipeline_by_stage, top_deals, revenue_trend } = data;
return (
<div className="space-y-6">
{/* Back button */}
<button
onClick={onBack}
className="flex items-center gap-2 text-sm font-medium text-surface-600 hover:text-surface-900 min-h-[44px]"
>
<ChevronRight className="h-4 w-4 rotate-180" />
Back to Markets
</button>
{/* Market Header */}
<div className="rounded-xl border border-surface-200 bg-white p-6">
<div className="flex items-center justify-between">
<div>
<h2 className="text-xl font-bold text-surface-900">{market}</h2>
<p className="mt-1 text-sm text-surface-500">Market Drill-Down</p>
</div>
<div className={`flex items-center gap-2 rounded-full px-3 py-1 text-sm font-medium ${
summary.close_rate >= 30 ? 'bg-green-100 text-green-700' : 'bg-orange-100 text-orange-700'
}`}>
{summary.close_rate >= 30 ? <CheckCircle className="h-4 w-4" /> : <AlertTriangle className="h-4 w-4" />}
{formatPercent(summary.close_rate)} Close Rate
</div>
</div>
{/* Summary Grid */}
<div className="mt-6 grid grid-cols-2 gap-4 sm:grid-cols-4">
<div>
<p className="text-sm text-surface-500">Total Revenue</p>
<p className="mt-1 text-xl font-bold text-surface-900">{formatCurrency(summary.total_revenue)}</p>
</div>
<div>
<p className="text-sm text-surface-500">Won Deals</p>
<p className="mt-1 text-xl font-bold text-surface-900">{summary.won_deals}</p>
</div>
<div>
<p className="text-sm text-surface-500">Avg Deal Size</p>
<p className="mt-1 text-xl font-bold text-surface-900">{formatCurrency(summary.avg_deal_size)}</p>
</div>
<div>
<p className="text-sm text-surface-500">Active Contacts</p>
<p className="mt-1 text-xl font-bold text-surface-900">{summary.active_contacts}</p>
</div>
</div>
</div>
{/* Pipeline by Stage */}
<div className="rounded-xl border border-surface-200 bg-white p-6">
<h3 className="mb-4 flex items-center gap-2 text-lg font-semibold text-surface-900">
<BarChart3 className="h-5 w-5 text-brand-600" />
Pipeline by Stage
</h3>
<ResponsiveContainer width="100%" height={250}>
<BarChart data={pipeline_by_stage}>
<CartesianGrid strokeDasharray="3 3" stroke="#e5e7eb" />
<XAxis dataKey="stage" tick={{ fontSize: 11 }} />
<YAxis tick={{ fontSize: 12 }} />
<Tooltip />
<Legend />
<Bar dataKey="count" name="Count" fill="#00A846" radius={[4, 4, 0, 0]} />
<Bar dataKey="value" name="Value" fill="#008036" radius={[4, 4, 0, 0]} />
</BarChart>
</ResponsiveContainer>
</div>
{/* Revenue Trend */}
<div className="rounded-xl border border-surface-200 bg-white p-6">
<h3 className="mb-4 flex items-center gap-2 text-lg font-semibold text-surface-900">
<TrendingUp className="h-5 w-5 text-brand-600" />
Revenue Trend (6 Months)
</h3>
<ResponsiveContainer width="100%" height={250}>
<AreaChart data={revenue_trend}>
<CartesianGrid strokeDasharray="3 3" stroke="#e5e7eb" />
<XAxis dataKey="month" tick={{ fontSize: 12 }} />
<YAxis tick={{ fontSize: 12 }} tickFormatter={formatCurrency} />
<Tooltip formatter={(value: number) => formatCurrency(value)} />
<Area
type="monotone"
dataKey="revenue"
stroke="#00A846"
fill="#00A846"
fillOpacity={0.1}
name="Revenue"
/>
</AreaChart>
</ResponsiveContainer>
</div>
{/* Top Deals */}
{top_deals.length > 0 && (
<div className="overflow-x-auto rounded-xl border border-surface-200 bg-white">
<div className="border-b border-surface-200 bg-surface-50 px-4 py-3">
<h3 className="text-lg font-semibold text-surface-900">Top Deals</h3>
</div>
<table className="w-full text-sm">
<thead className="border-b border-surface-200">
<tr>
<th className="px-4 py-3 text-left font-semibold text-surface-700">Amount</th>
<th className="px-4 py-3 text-left font-semibold text-surface-700">Stage</th>
<th className="px-4 py-3 text-left font-semibold text-surface-700">Probability</th>
<th className="px-4 py-3 text-left font-semibold text-surface-700">Expected Close</th>
</tr>
</thead>
<tbody>
{top_deals.map((deal: any, idx: number) => (
<tr key={deal.id} className={`border-b border-surface-100 ${idx % 2 === 0 ? 'bg-white' : 'bg-surface-50/50'}`}>
<td className="px-4 py-3 font-medium text-surface-900">{formatCurrency(deal.amount)}</td>
<td className="px-4 py-3">
<span className="inline-flex rounded-full bg-surface-100 px-2 py-0.5 text-xs font-medium text-surface-700">
{deal.stage}
</span>
</td>
<td className="px-4 py-3">{deal.probability ? formatPercent(deal.probability * 100) : '-'}</td>
<td className="px-4 py-3">{deal.expected_close_date ? new Date(deal.expected_close_date).toLocaleDateString() : '-'}</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</div>
);
}
// ============================================================================
// Underperforming Markets
// ============================================================================
function UnderperformingMarkets() {
const { data, isLoading } = useQuery({
queryKey: ['multi-market', 'underperforming'],
queryFn: multiMarketApi.getUnderperforming,
});
if (isLoading) {
return (
<div className="flex h-64 items-center justify-center">
<Loader2 className="h-6 w-6 animate-spin text-brand-600" />
</div>
);
}
if (!data?.underperforming?.length) {
return (
<div className="rounded-xl border border-green-200 bg-green-50 p-8 text-center">
<CheckCircle className="mx-auto h-12 w-12 text-green-600" />
<p className="mt-4 text-lg font-medium text-green-800">All markets performing well</p>
<p className="mt-2 text-sm text-green-600">No underperforming markets detected</p>
</div>
);
}
const severityColor = {
high: 'bg-red-100 border-red-200 text-red-800',
medium: 'bg-orange-100 border-orange-200 text-orange-800',
low: 'bg-yellow-100 border-yellow-200 text-yellow-800',
};
return (
<div className="space-y-6">
{/* Benchmarks */}
<div className="grid gap-4 sm:grid-cols-2">
<div className="rounded-xl border border-surface-200 bg-white p-4">
<p className="text-sm text-surface-500">Avg Close Rate</p>
<p className="mt-1 text-2xl font-bold text-surface-900">{formatPercent(data.benchmarks?.avg_close_rate ?? 0)}</p>
</div>
<div className="rounded-xl border border-surface-200 bg-white p-4">
<p className="text-sm text-surface-500">Avg Revenue</p>
<p className="mt-1 text-2xl font-bold text-surface-900">{formatCurrency(data.benchmarks?.avg_revenue ?? 0)}</p>
</div>
</div>
{/* Underperforming Markets */}
<div className="space-y-4">
{data.underperforming.map((item: any) => (
<div key={item.market} className={`rounded-xl border p-6 ${severityColor[item.severity]}`}>
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<AlertTriangle className="h-5 w-5" />
<h4 className="text-lg font-semibold">{item.market}</h4>
<span className={`inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${
item.severity === 'high' ? 'bg-red-200 text-red-800' :
item.severity === 'medium' ? 'bg-orange-200 text-orange-800' :
'bg-yellow-200 text-yellow-800'
}`}>
{item.severity.toUpperCase()}
</span>
</div>
<div className="text-right">
<p className="text-sm opacity-75">Revenue: {formatCurrency(item.metrics.total_revenue)}</p>
<p className="text-sm opacity-75">CR: {formatPercent(item.metrics.close_rate)}</p>
</div>
</div>
{/* Issues */}
<div className="mt-4">
<p className="text-sm font-medium opacity-75">Issues:</p>
<div className="mt-2 flex flex-wrap gap-2">
{item.issues.map((issue: string) => (
<span key={issue} className="inline-flex items-center gap-1 rounded-full bg-white/50 px-2 py-1 text-xs font-medium">
{issue.replace(/_/g, ' ')}
</span>
))}
</div>
</div>
{/* Recommendations */}
<div className="mt-4">
<p className="text-sm font-medium opacity-75">Recommendations:</p>
<ul className="mt-2 space-y-1">
{item.recommendations.map((rec: string, idx: number) => (
<li key={idx} className="flex items-start gap-2 text-sm">
<ArrowUpRight className="mt-0.5 h-4 w-4 shrink-0" />
<span>{rec}</span>
</li>
))}
</ul>
</div>
</div>
))}
</div>
</div>
);
}
// ============================================================================
// Main Page
// ============================================================================
export default function MultiMarketPage() {
const [activeTab, setActiveTab] = useState<'overview' | 'comparison' | 'underperforming'>('overview');
const [selectedMarket, setSelectedMarket] = useState<string | null>(null);
const tabs = [
{ id: 'overview' as const, label: 'Overview', icon: Map },
{ id: 'comparison' as const, label: 'Comparison', icon: BarChart3 },
{ id: 'underperforming' as const, label: 'Underperforming', icon: AlertTriangle },
];
return (
<AppLayout>
<div className="space-y-6">
{/* Header */}
<div>
<h1 className="text-xl font-bold text-surface-900 sm:text-2xl">Multi-Market Analytics</h1>
<p className="mt-1 text-sm text-surface-500">
Rank markets by performance and identify coaching opportunities
</p>
</div>
{/* Tabs */}
<div className="flex gap-1 rounded-lg bg-surface-100 p-1">
{tabs.map(tab => {
const Icon = tab.icon;
const isActive = activeTab === tab.id;
return (
<button
key={tab.id}
onClick={() => { setActiveTab(tab.id); setSelectedMarket(null); }}
className={`flex flex-1 items-center justify-center gap-2 rounded-md py-2.5 text-sm font-medium transition-colors min-h-[44px] ${
isActive
? 'bg-white text-surface-900 shadow-sm'
: 'text-surface-600 hover:text-surface-900'
}`}
>
<Icon className="h-4 w-4" />
<span className="hidden sm:inline">{tab.label}</span>
</button>
);
})}
</div>
{/* Content */}
{selectedMarket ? (
<MarketDrillDown
market={selectedMarket}
onBack={() => setSelectedMarket(null)}
/>
) : activeTab === 'overview' ? (
<MarketOverview />
) : activeTab === 'comparison' ? (
<MarketComparison />
) : (
<UnderperformingMarkets />
)}
</div>
</AppLayout>
);
}