refactor: remove performance insights (Web Vitals) feature entirely

Remove Performance tab, PerformanceStats component, settings toggle,
Web Vitals observers from tracking script, and all related API types
and SWR hooks. Duration tracking is preserved.
This commit is contained in:
Usman Baig
2026-03-14 22:47:33 +01:00
parent 7247281ce2
commit b305b5345b
12 changed files with 13 additions and 628 deletions

View File

@@ -3,7 +3,7 @@
import { useCallback, useEffect, useState } from 'react'
import Image from 'next/image'
import { useParams, useSearchParams, useRouter } from 'next/navigation'
import { getPublicDashboard, getPublicStats, getPublicDailyStats, getPublicRealtime, getPublicPerformanceByPage, type DashboardData, type Stats, type DailyStat, type PerformanceByPageStat } from '@/lib/api/stats'
import { getPublicDashboard, getPublicStats, getPublicDailyStats, getPublicRealtime, type DashboardData, type Stats, type DailyStat } from '@/lib/api/stats'
import { toast } from '@ciphera-net/ui'
import { getAuthErrorMessage } from '@ciphera-net/ui'
import { ApiError } from '@/lib/api/client'
@@ -13,7 +13,6 @@ import TopPages from '@/components/dashboard/ContentStats'
import TopReferrers from '@/components/dashboard/TopReferrers'
import Locations from '@/components/dashboard/Locations'
import TechSpecs from '@/components/dashboard/TechSpecs'
import PerformanceStats from '@/components/dashboard/PerformanceStats'
import { Select, DatePicker as DatePickerModal, Captcha, DownloadIcon, ZapIcon } from '@ciphera-net/ui'
import { DashboardSkeleton, useMinimumLoading, useSkeletonFade } from '@/components/skeletons'
import ExportModal from '@/components/dashboard/ExportModal'
@@ -257,7 +256,7 @@ export default function PublicDashboardPage() {
if (!data) return null
const { site, stats, daily_stats, top_pages, entry_pages, exit_pages, top_referrers, countries, cities, regions, browsers, os, devices, screen_resolutions, performance, performance_by_page, realtime_visitors } = data
const { site, stats, daily_stats, top_pages, entry_pages, exit_pages, top_referrers, countries, cities, regions, browsers, os, devices, screen_resolutions, realtime_visitors } = data
// Provide defaults for potentially undefined data
const safeDailyStats = daily_stats || []
@@ -395,29 +394,6 @@ export default function PublicDashboardPage() {
/>
</div>
{/* Performance Stats - Only show if enabled */}
{performance && data.site?.enable_performance_insights && (
<div className="mb-8">
<PerformanceStats
stats={performance}
performanceByPage={performance_by_page}
siteId={siteId}
startDate={dateRange.start}
endDate={dateRange.end}
getPerformanceByPage={(siteId, startDate, endDate, opts) => {
return getPublicPerformanceByPage(siteId, startDate, endDate, opts, {
password,
captcha: {
captcha_id: captchaId,
captcha_solution: captchaSolution,
captcha_token: captchaToken
}
})
}}
/>
</div>
)}
{/* Details Grid */}
<div className="grid gap-6 lg:grid-cols-2 mb-8">
<TopPages

View File

@@ -234,9 +234,9 @@ export default function SiteDashboardPage() {
return { start: prevStart.toISOString().split('T')[0], end: prevEnd.toISOString().split('T')[0] }
}, [dateRange])
// Single dashboard request replaces 7 focused hooks (overview, pages, locations,
// devices, referrers, performance, goals). The backend runs all queries in parallel
// and caches the result in Redis, reducing requests from 12 to 6 per refresh cycle.
// Single dashboard request replaces focused hooks (overview, pages, locations,
// devices, referrers, goals). The backend runs all queries in parallel
// and caches the result in Redis for efficient data loading.
const { data: dashboard, isLoading: dashboardLoading, error: dashboardError } = useDashboard(siteId, dateRange.start, dateRange.end, interval, filtersParam || undefined)
const { data: realtimeData } = useRealtime(siteId)
const { data: prevStats } = useStats(siteId, prevRange.start, prevRange.end)

View File

@@ -1,156 +0,0 @@
'use client'
import { useEffect, useState } from 'react'
import { useParams } from 'next/navigation'
import { getDateRange, formatDate } from '@ciphera-net/ui'
import { Select, DatePicker } from '@ciphera-net/ui'
import { getPerformanceByPage } from '@/lib/api/stats'
import { useDashboard } from '@/lib/swr/dashboard'
import { useMinimumLoading, useSkeletonFade } from '@/components/skeletons'
import dynamic from 'next/dynamic'
const PerformanceStats = dynamic(() => import('@/components/dashboard/PerformanceStats'))
function getThisWeekRange(): { start: string; end: string } {
const today = new Date()
const dayOfWeek = today.getDay()
const monday = new Date(today)
monday.setDate(today.getDate() - (dayOfWeek === 0 ? 6 : dayOfWeek - 1))
return { start: formatDate(monday), end: formatDate(today) }
}
function getThisMonthRange(): { start: string; end: string } {
const today = new Date()
const firstOfMonth = new Date(today.getFullYear(), today.getMonth(), 1)
return { start: formatDate(firstOfMonth), end: formatDate(today) }
}
function PerformanceSkeleton() {
return (
<div className="w-full max-w-6xl mx-auto px-4 sm:px-6 pb-8 animate-pulse">
<div className="mb-8 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
<div>
<div className="h-7 w-40 bg-neutral-200 dark:bg-neutral-800 rounded mb-2" />
<div className="h-4 w-64 bg-neutral-200 dark:bg-neutral-800 rounded" />
</div>
<div className="h-9 w-36 bg-neutral-200 dark:bg-neutral-800 rounded" />
</div>
<div className="h-6 w-24 bg-neutral-200 dark:bg-neutral-800 rounded mb-6" />
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
{[1, 2, 3].map(i => (
<div key={i} className="h-24 bg-neutral-200 dark:bg-neutral-800 rounded-lg" />
))}
</div>
<div className="h-64 bg-neutral-200 dark:bg-neutral-800 rounded-2xl" />
</div>
)
}
export default function PerformancePage() {
const params = useParams()
const siteId = params.id as string
const [period, setPeriod] = useState('30')
const [dateRange, setDateRange] = useState(() => getDateRange(30))
const [isDatePickerOpen, setIsDatePickerOpen] = useState(false)
const { data: dashboard, isLoading: loading } = useDashboard(siteId, dateRange.start, dateRange.end)
const site = dashboard?.site ?? null
const showSkeleton = useMinimumLoading(loading && !dashboard)
const fadeClass = useSkeletonFade(showSkeleton)
useEffect(() => {
const domain = site?.domain
document.title = domain ? `Performance \u00b7 ${domain} | Pulse` : 'Performance | Pulse'
}, [site?.domain])
if (showSkeleton) return <PerformanceSkeleton />
if (site && !site.enable_performance_insights) {
return (
<div className="w-full max-w-6xl mx-auto px-4 sm:px-6 pb-8">
<div className="text-center py-16">
<h2 className="text-lg font-semibold text-neutral-900 dark:text-white mb-2">
Performance insights are disabled
</h2>
<p className="text-sm text-neutral-500 dark:text-neutral-400">
Enable performance insights in your site settings to start collecting Core Web Vitals data.
</p>
</div>
</div>
)
}
return (
<div className={`w-full max-w-6xl mx-auto px-4 sm:px-6 pb-8 ${fadeClass}`}>
{/* Header */}
<div className="mb-8 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
<div>
<h1 className="text-2xl font-bold text-neutral-900 dark:text-white mb-1">
Performance
</h1>
<p className="text-sm text-neutral-500 dark:text-neutral-400">
Core Web Vitals from real user sessions
</p>
</div>
<Select
variant="input"
className="min-w-[140px]"
value={period}
onChange={(value) => {
if (value === 'today') {
const today = formatDate(new Date())
setDateRange({ start: today, end: today })
setPeriod('today')
} else if (value === '7') {
setDateRange(getDateRange(7))
setPeriod('7')
} else if (value === 'week') {
setDateRange(getThisWeekRange())
setPeriod('week')
} else if (value === '30') {
setDateRange(getDateRange(30))
setPeriod('30')
} else if (value === 'month') {
setDateRange(getThisMonthRange())
setPeriod('month')
} else if (value === 'custom') {
setIsDatePickerOpen(true)
}
}}
options={[
{ value: 'today', label: 'Today' },
{ value: '7', label: 'Last 7 days' },
{ value: '30', label: 'Last 30 days' },
{ value: 'divider-1', label: '', divider: true },
{ value: 'week', label: 'This week' },
{ value: 'month', label: 'This month' },
{ value: 'divider-2', label: '', divider: true },
{ value: 'custom', label: 'Custom' },
]}
/>
</div>
<PerformanceStats
stats={dashboard?.performance ?? null}
performanceByPage={dashboard?.performance_by_page ?? null}
siteId={siteId}
startDate={dateRange.start}
endDate={dateRange.end}
getPerformanceByPage={getPerformanceByPage}
/>
<DatePicker
isOpen={isDatePickerOpen}
onClose={() => setIsDatePickerOpen(false)}
onApply={(range) => {
setDateRange(range)
setPeriod('custom')
setIsDatePickerOpen(false)
}}
initialRange={dateRange}
/>
</div>
)
}

View File

@@ -74,8 +74,6 @@ export default function SiteSettingsPage() {
collect_device_info: true,
collect_geo_data: 'full' as GeoDataLevel,
collect_screen_resolution: true,
// Performance insights setting
enable_performance_insights: false,
// Bot and noise filtering
filter_bots: true,
// Hide unknown locations
@@ -135,7 +133,6 @@ export default function SiteSettingsPage() {
collect_device_info: site.collect_device_info ?? true,
collect_geo_data: site.collect_geo_data || 'full',
collect_screen_resolution: site.collect_screen_resolution ?? true,
enable_performance_insights: site.enable_performance_insights ?? false,
filter_bots: site.filter_bots ?? true,
hide_unknown_locations: site.hide_unknown_locations ?? false,
data_retention_months: site.data_retention_months ?? 6
@@ -150,7 +147,6 @@ export default function SiteSettingsPage() {
collect_device_info: site.collect_device_info ?? true,
collect_geo_data: site.collect_geo_data || 'full',
collect_screen_resolution: site.collect_screen_resolution ?? true,
enable_performance_insights: site.enable_performance_insights ?? false,
filter_bots: site.filter_bots ?? true,
hide_unknown_locations: site.hide_unknown_locations ?? false,
data_retention_months: site.data_retention_months ?? 6
@@ -423,8 +419,6 @@ export default function SiteSettingsPage() {
collect_device_info: formData.collect_device_info,
collect_geo_data: formData.collect_geo_data,
collect_screen_resolution: formData.collect_screen_resolution,
// Performance insights setting
enable_performance_insights: formData.enable_performance_insights,
// Bot and noise filtering
filter_bots: formData.filter_bots,
// Hide unknown locations
@@ -443,7 +437,6 @@ export default function SiteSettingsPage() {
collect_device_info: formData.collect_device_info,
collect_geo_data: formData.collect_geo_data,
collect_screen_resolution: formData.collect_screen_resolution,
enable_performance_insights: formData.enable_performance_insights,
filter_bots: formData.filter_bots,
hide_unknown_locations: formData.hide_unknown_locations,
data_retention_months: formData.data_retention_months
@@ -511,7 +504,6 @@ export default function SiteSettingsPage() {
collect_device_info: formData.collect_device_info,
collect_geo_data: formData.collect_geo_data,
collect_screen_resolution: formData.collect_screen_resolution,
enable_performance_insights: formData.enable_performance_insights,
filter_bots: formData.filter_bots,
hide_unknown_locations: formData.hide_unknown_locations,
data_retention_months: formData.data_retention_months
@@ -1113,30 +1105,6 @@ export default function SiteSettingsPage() {
</div>
</div>
{/* Performance Insights Toggle */}
<div className="space-y-4 pt-6 border-t border-neutral-100 dark:border-neutral-800">
<h3 className="text-sm font-medium text-neutral-700 dark:text-neutral-300">Performance Insights</h3>
<div className="p-6 bg-neutral-50 dark:bg-neutral-900/50 rounded-2xl border border-neutral-100 dark:border-neutral-800">
<div className="flex items-center justify-between">
<div>
<h4 className="font-medium text-neutral-900 dark:text-white">Performance Insights (Add-on)</h4>
<p className="text-sm text-neutral-500 dark:text-neutral-400 mt-0.5">
Track Core Web Vitals (LCP, CLS, INP) to monitor site performance
</p>
</div>
<label className="relative inline-flex items-center cursor-pointer">
<input
type="checkbox"
checked={formData.enable_performance_insights}
onChange={(e) => setFormData({ ...formData, enable_performance_insights: e.target.checked })}
className="sr-only peer"
/>
<div className="w-11 h-6 bg-neutral-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-brand-orange/20 dark:peer-focus:ring-brand-orange/20 rounded-full peer dark:bg-neutral-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-neutral-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-neutral-600 peer-checked:bg-brand-orange"></div>
</label>
</div>
</div>
</div>
{/* Data Retention */}
<div className="space-y-4 pt-6 border-t border-neutral-100 dark:border-neutral-800">
<h3 className="text-sm font-medium text-neutral-700 dark:text-neutral-300">Data Retention</h3>