105 lines
3.4 KiB
TypeScript
Executable file
105 lines
3.4 KiB
TypeScript
Executable file
import React, { useState } from 'react'
|
|
|
|
import {
|
|
useConversionMetrics,
|
|
useFunnelData,
|
|
useConversionBySource,
|
|
} from '../hooks/useAdminQuery'
|
|
|
|
export const ConversionFunnelsPage: React.FC = () => {
|
|
const { data: metrics, isLoading, isError } = useConversionMetrics()
|
|
const { data: funnelData } = useFunnelData()
|
|
const { data: bySource } = useConversionBySource()
|
|
const [_dateRange, setDateRange] = useState('30 Days')
|
|
|
|
if (isLoading) {
|
|
return <div>Loading conversion data...</div>
|
|
}
|
|
|
|
if (isError) {
|
|
return <div>Failed to load conversion data</div>
|
|
}
|
|
|
|
const isLowConversion = (metrics?.overallConversionRate ?? 0) < 5
|
|
|
|
return (
|
|
<div className="conversion-funnels-page">
|
|
<h1 data-testid="page-title">Conversion Funnels</h1>
|
|
|
|
{/* Low Conversion Alert */}
|
|
{isLowConversion && (
|
|
<div className="alert">Low Conversion Rate Alert</div>
|
|
)}
|
|
|
|
{/* KPI Cards */}
|
|
<div className="kpi-cards">
|
|
<div className="kpi-card">
|
|
<div className="kpi-label">Overall Conversion Rate</div>
|
|
<div className="kpi-value">{metrics?.overallConversionRate}%</div>
|
|
</div>
|
|
<div className="kpi-card">
|
|
<div className="kpi-label">Signup to Subscriber</div>
|
|
<div className="kpi-value">{metrics?.signupToSubscriber}%</div>
|
|
</div>
|
|
<div className="kpi-card">
|
|
<div className="kpi-label">Visitor to Signup</div>
|
|
<div className="kpi-value">{metrics?.visitorToSignup}%</div>
|
|
</div>
|
|
<div className="kpi-card">
|
|
<div className="kpi-label">Free to Trial</div>
|
|
<div className="kpi-value">{metrics?.freeToTrial}%</div>
|
|
</div>
|
|
<div className="kpi-card">
|
|
<div className="kpi-label">Trial to Paid</div>
|
|
<div className="kpi-value">{metrics?.trialToPaid}%</div>
|
|
</div>
|
|
<div className="kpi-card">
|
|
<div className="kpi-label">Avg Time to Conversion</div>
|
|
<div className="kpi-value">{metrics?.avgTimeToConversion} days</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Date Range Selector */}
|
|
<div className="date-range">
|
|
<button onClick={() => setDateRange('7 Days')}>7 Days</button>
|
|
<button onClick={() => setDateRange('30 Days')}>30 Days</button>
|
|
<button onClick={() => setDateRange('90 Days')}>90 Days</button>
|
|
</div>
|
|
|
|
{/* Conversion Funnel Visualization */}
|
|
<div className="conversion-funnel">
|
|
<h2>Conversion Funnel</h2>
|
|
{funnelData?.map((stage, idx) => (
|
|
<div key={idx} className="funnel-stage">
|
|
<div className="stage-name">{stage.stage}</div>
|
|
<div className="stage-count">{stage.count.toLocaleString()}</div>
|
|
<div className="stage-rate">{stage.rate}%</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{/* Conversion by Source */}
|
|
<div className="conversion-by-source">
|
|
<h2>Conversion by Source</h2>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Source</th>
|
|
<th>Conversions</th>
|
|
<th>Rate</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{bySource?.map((source, idx) => (
|
|
<tr key={idx}>
|
|
<td>{source.source}</td>
|
|
<td>{source.conversions.toLocaleString()}</td>
|
|
<td>{source.rate}%</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|