import React, { useState, useEffect } from 'react' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@core/components/ui/card' import { Badge } from '@core/components/ui/badge' import { Button } from '@core/components/ui/button' import { Input } from '@core/components/ui/input' import { Label } from '@core/components/ui/label' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@core/components/ui/select' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@core/components/ui/table' import { useApi } from '@core/hooks/useApi' import { formatDistanceToNow } from 'date-fns' import { Search, Filter, Download } from 'lucide-react' interface SignalRecord { id: string action_type: string action_value: number stage: string source: string created_at: string account_name?: string person_email?: string anonymous_id?: string session_id?: string rating: number raw_score: number referrer_domain?: string utm_source?: string utm_medium?: string utm_campaign?: string } interface AuditFilters { date_from?: string date_to?: string source?: string stage?: string action_type?: string min_rating?: number account_id?: string search?: string } export default function AuditFunnelPage() { const [signals, setSignals] = useState([]) const [loading, setLoading] = useState(false) const [filters, setFilters] = useState({}) const [totalCount, setTotalCount] = useState(0) const [page, setPage] = useState(1) const { apiFetch } = useApi() useEffect(() => { loadSignals() }, [page, filters]) const loadSignals = async () => { setLoading(true) try { const params = new URLSearchParams({ page: page.toString(), limit: '50', ...Object.fromEntries( Object.entries(filters).filter(([_, v]) => v != null && v !== '') ) }) const response = await apiFetch(`/admin/signals/audit?${params}`) setSignals(response.signals || []) setTotalCount(response.total || 0) } catch (error) { console.error('Failed to load signals:', error) } finally { setLoading(false) } } const exportData = async () => { try { const params = new URLSearchParams({ export: 'true', ...Object.fromEntries( Object.entries(filters).filter(([_, v]) => v != null && v !== '') ) }) const response = await apiFetch(`/admin/signals/audit?${params}`) // Create download link const blob = new Blob([JSON.stringify(response, null, 2)], { type: 'application/json' }) const url = window.URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = `signals-audit-${new Date().toISOString().split('T')[0]}.json` document.body.appendChild(a) a.click() window.URL.revokeObjectURL(url) document.body.removeChild(a) } catch (error) { console.error('Failed to export data:', error) } } const updateFilter = (key: keyof AuditFilters, value: any) => { setFilters(prev => ({ ...prev, [key]: value })) setPage(1) // Reset to first page when filtering } const getRatingColor = (rating: number) => { if (rating >= 4) return 'bg-green-100 text-green-800' if (rating >= 3) return 'bg-yellow-100 text-yellow-800' return 'bg-red-100 text-red-800' } const getSourceColor = (source: string) => { const colors = { mar: 'bg-blue-100 text-blue-800', int: 'bg-purple-100 text-purple-800', use: 'bg-green-100 text-green-800', manual: 'bg-gray-100 text-gray-800' } return colors[source as keyof typeof colors] || 'bg-gray-100 text-gray-800' } return (

Funnel Audit

Detailed audit trail of all funnel signals and conversions

{/* Filters */} Filters
updateFilter('search', e.target.value)} />
updateFilter('date_from', e.target.value)} />
updateFilter('date_to', e.target.value)} />
{/* Results Summary */} Results Showing {signals.length} of {totalCount} signals Timestamp Action User Source Stage Rating Score Referrer UTM {loading ? ( Loading signals... ) : signals.length === 0 ? ( No signals found matching the current filters ) : ( signals.map((signal) => (
{formatDistanceToNow(new Date(signal.created_at), { addSuffix: true })}
{new Date(signal.created_at).toLocaleString()}
{signal.action_type}
Value: {signal.action_value}
{signal.account_name ? (
{signal.account_name}
{signal.person_email}
) : signal.anonymous_id ? (
Anonymous: {signal.anonymous_id.slice(0, 8)}...
) : (
Unknown
)}
{signal.source} {signal.stage} {signal.rating} {signal.raw_score}
{signal.referrer_domain || 'Direct'}
{signal.utm_source && (
Source: {signal.utm_source}
)} {signal.utm_medium && (
Medium: {signal.utm_medium}
)} {signal.utm_campaign && (
Campaign: {signal.utm_campaign}
)}
)) )}
{/* Pagination */} {totalCount > 50 && (
Page {page} of {Math.ceil(totalCount / 50)}
)}
) }