/ dashboard-v2 / frontend / src / pages / Outreach.jsx
Outreach.jsx
  1  import { useQuery } from '@tanstack/react-query';
  2  import { fetchOutreach } from '../api';
  3  import MetricCard from '../components/MetricCard';
  4  import DataTable from '../components/DataTable';
  5  import { BarChart, LineChart, PieChart, FunnelChart } from '../components/charts';
  6  
  7  const PROVIDER_COLS = [
  8    { accessorKey: 'provider', header: 'Provider' },
  9    { accessorKey: 'model', header: 'Model' },
 10    {
 11      accessorKey: 'total_tokens',
 12      header: 'Tokens',
 13      cell: i => i.getValue()?.toLocaleString() ?? '—',
 14    },
 15    { accessorKey: 'total_cost', header: 'Cost', cell: i => `$${(i.getValue() ?? 0).toFixed(4)}` },
 16    { accessorKey: 'request_count', header: 'Requests' },
 17  ];
 18  
 19  export default function Outreach() {
 20    const { data, isLoading, error } = useQuery({ queryKey: ['outreach'], queryFn: fetchOutreach });
 21  
 22    if (isLoading) return <div className="text-slate-400 p-8">Loading…</div>;
 23    if (error) return <div className="text-red-400 p-8">Error: {error.message}</div>;
 24  
 25    const d = data ?? {};
 26    const rr = d.response_rates ?? {};
 27    const ss = d.sales_data ?? {};
 28    const lu = d.llm_usage_by_stage ?? [];
 29    const dc = d.llm_daily_costs_30d ?? [];
 30    const prov = d.llm_usage_by_provider ?? [];
 31    const funnel = d.outreach_funnel ?? [];
 32  
 33    // Build channel response chart data
 34    const channelData = Object.entries(rr.by_channel ?? {}).map(([k, v]) => ({
 35      channel: k,
 36      sent: v.sent ?? 0,
 37      replied: v.replied ?? 0,
 38      rate: (((v.replied ?? 0) / Math.max(v.sent ?? 1, 1)) * 100).toFixed(1),
 39    }));
 40  
 41    return (
 42      <div className="space-y-6">
 43        {/* Top metrics */}
 44        <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
 45          <MetricCard label="Total Sent" value={(rr.total_sent ?? 0).toLocaleString()} />
 46          <MetricCard
 47            label="Response Rate"
 48            value={`${(rr.response_rate ?? 0).toFixed(1)}%`}
 49            variant={
 50              (rr.response_rate ?? 0) >= 2
 51                ? 'success'
 52                : (rr.response_rate ?? 0) >= 1
 53                  ? 'warning'
 54                  : 'error'
 55            }
 56          />
 57          <MetricCard label="Total Sales" value={ss.total_sales ?? 0} variant="success" />
 58          <MetricCard
 59            label="Revenue"
 60            value={`$${(ss.total_revenue ?? 0).toLocaleString('en-AU', { minimumFractionDigits: 0 })}`}
 61            variant="success"
 62          />
 63        </div>
 64  
 65        {/* Delivery funnel + channel response rates */}
 66        <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
 67          {funnel.length > 0 && (
 68            <div className="bg-slate-800 rounded-xl p-4 border border-slate-700">
 69              <h3 className="text-sm font-medium text-slate-400 mb-3">Delivery Funnel</h3>
 70              <FunnelChart data={funnel} nameKey="status" valueKey="count" />
 71            </div>
 72          )}
 73          {channelData.length > 0 && (
 74            <div className="bg-slate-800 rounded-xl p-4 border border-slate-700">
 75              <h3 className="text-sm font-medium text-slate-400 mb-3">Replies by Channel</h3>
 76              <BarChart
 77                data={channelData}
 78                xKey="channel"
 79                bars={[
 80                  { key: 'sent', name: 'Sent', color: '#60a5fa' },
 81                  { key: 'replied', name: 'Replied', color: '#34d399' },
 82                ]}
 83              />
 84            </div>
 85          )}
 86        </div>
 87  
 88        {/* Sales metrics */}
 89        {ss.total_sales > 0 && (
 90          <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
 91            <MetricCard label="Avg Deal Value" value={`$${(ss.avg_deal_value ?? 297).toFixed(0)}`} />
 92            <MetricCard label="Conversion Rate" value={`${(ss.conversion_rate ?? 0).toFixed(2)}%`} />
 93            <MetricCard
 94              label="Cost per Sale"
 95              value={`$${(ss.cost_per_sale ?? 0).toFixed(2)}`}
 96              variant="warning"
 97            />
 98            <MetricCard
 99              label="Gross Margin"
100              value={`${(ss.gross_margin ?? 99.3).toFixed(1)}%`}
101              variant="success"
102            />
103          </div>
104        )}
105  
106        {/* LLM cost by stage */}
107        {lu.length > 0 && (
108          <div className="bg-slate-800 rounded-xl p-4 border border-slate-700">
109            <h3 className="text-sm font-medium text-slate-400 mb-3">LLM Cost by Pipeline Stage</h3>
110            <BarChart
111              data={lu}
112              xKey="stage"
113              bars={[{ key: 'total_cost', name: 'Cost ($)', color: '#fbbf24' }]}
114            />
115          </div>
116        )}
117  
118        {/* Daily LLM costs 30d */}
119        {dc.length > 0 && (
120          <div className="bg-slate-800 rounded-xl p-4 border border-slate-700">
121            <h3 className="text-sm font-medium text-slate-400 mb-3">
122              Daily LLM Costs (Last 30 Days)
123            </h3>
124            <LineChart
125              data={dc}
126              xKey="date"
127              lines={[{ key: 'daily_cost', name: 'Cost ($)', color: '#fbbf24' }]}
128            />
129          </div>
130        )}
131  
132        {/* Provider breakdown */}
133        {prov.length > 0 && (
134          <div className="bg-slate-800 rounded-xl p-4 border border-slate-700">
135            <h3 className="text-sm font-medium text-slate-400 mb-3">Cost by Provider & Model</h3>
136            <DataTable columns={PROVIDER_COLS} data={prov} />
137          </div>
138        )}
139      </div>
140    );
141  }