feat: add performance insights feature with toggle in settings and conditional rendering in dashboard pages
This commit is contained in:
@@ -271,8 +271,8 @@ export default function PublicDashboardPage() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Performance Stats */}
|
{/* Performance Stats - Only show if enabled */}
|
||||||
{performance && (
|
{performance && data.site?.enable_performance_insights && (
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
<PerformanceStats stats={performance} />
|
<PerformanceStats stats={performance} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -228,10 +228,12 @@ export default function SiteDashboardPage() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Performance Stats */}
|
{/* Performance Stats - Only show if enabled */}
|
||||||
<div className="mb-8">
|
{site.enable_performance_insights && (
|
||||||
<PerformanceStats stats={performance} />
|
<div className="mb-8">
|
||||||
</div>
|
<PerformanceStats stats={performance} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="grid gap-6 lg:grid-cols-2 mb-8">
|
<div className="grid gap-6 lg:grid-cols-2 mb-8">
|
||||||
<ContentStats
|
<ContentStats
|
||||||
|
|||||||
@@ -71,6 +71,8 @@ export default function SiteSettingsPage() {
|
|||||||
collect_device_info: true,
|
collect_device_info: true,
|
||||||
collect_geo_data: 'full' as GeoDataLevel,
|
collect_geo_data: 'full' as GeoDataLevel,
|
||||||
collect_screen_resolution: true,
|
collect_screen_resolution: true,
|
||||||
|
// Performance insights setting
|
||||||
|
enable_performance_insights: false,
|
||||||
// Session replay settings
|
// Session replay settings
|
||||||
replay_mode: 'disabled' as ReplayMode,
|
replay_mode: 'disabled' as ReplayMode,
|
||||||
replay_sampling_rate: 100,
|
replay_sampling_rate: 100,
|
||||||
@@ -104,6 +106,8 @@ export default function SiteSettingsPage() {
|
|||||||
collect_device_info: data.collect_device_info ?? true,
|
collect_device_info: data.collect_device_info ?? true,
|
||||||
collect_geo_data: data.collect_geo_data || 'full',
|
collect_geo_data: data.collect_geo_data || 'full',
|
||||||
collect_screen_resolution: data.collect_screen_resolution ?? true,
|
collect_screen_resolution: data.collect_screen_resolution ?? true,
|
||||||
|
// Performance insights setting (default to false)
|
||||||
|
enable_performance_insights: data.enable_performance_insights ?? false,
|
||||||
// Session replay settings (legacy consent_required from API mapped to anonymous_skeleton)
|
// Session replay settings (legacy consent_required from API mapped to anonymous_skeleton)
|
||||||
replay_mode: ((data as { replay_mode?: string }).replay_mode === 'consent_required' ? 'anonymous_skeleton' : data.replay_mode) || 'disabled',
|
replay_mode: ((data as { replay_mode?: string }).replay_mode === 'consent_required' ? 'anonymous_skeleton' : data.replay_mode) || 'disabled',
|
||||||
replay_sampling_rate: snapSamplingRate(data.replay_sampling_rate ?? 100),
|
replay_sampling_rate: snapSamplingRate(data.replay_sampling_rate ?? 100),
|
||||||
@@ -146,6 +150,8 @@ export default function SiteSettingsPage() {
|
|||||||
collect_device_info: formData.collect_device_info,
|
collect_device_info: formData.collect_device_info,
|
||||||
collect_geo_data: formData.collect_geo_data,
|
collect_geo_data: formData.collect_geo_data,
|
||||||
collect_screen_resolution: formData.collect_screen_resolution,
|
collect_screen_resolution: formData.collect_screen_resolution,
|
||||||
|
// Performance insights setting
|
||||||
|
enable_performance_insights: formData.enable_performance_insights,
|
||||||
// Session replay settings
|
// Session replay settings
|
||||||
replay_mode: formData.replay_mode,
|
replay_mode: formData.replay_mode,
|
||||||
replay_sampling_rate: formData.replay_sampling_rate,
|
replay_sampling_rate: formData.replay_sampling_rate,
|
||||||
@@ -685,6 +691,30 @@ export default function SiteSettingsPage() {
|
|||||||
</div>
|
</div>
|
||||||
</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-4 bg-neutral-50 dark:bg-neutral-900/50 rounded-xl 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-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-brand-orange"></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Excluded Paths */}
|
{/* Excluded Paths */}
|
||||||
<div className="space-y-4 pt-6 border-t border-neutral-100 dark:border-neutral-800">
|
<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">Path Filtering</h3>
|
<h3 className="text-sm font-medium text-neutral-700 dark:text-neutral-300">Path Filtering</h3>
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ export interface Site {
|
|||||||
collect_device_info?: boolean
|
collect_device_info?: boolean
|
||||||
collect_geo_data?: GeoDataLevel
|
collect_geo_data?: GeoDataLevel
|
||||||
collect_screen_resolution?: boolean
|
collect_screen_resolution?: boolean
|
||||||
|
// Performance insights setting
|
||||||
|
enable_performance_insights?: boolean
|
||||||
// Session replay settings
|
// Session replay settings
|
||||||
replay_mode?: ReplayMode
|
replay_mode?: ReplayMode
|
||||||
replay_sampling_rate?: number
|
replay_sampling_rate?: number
|
||||||
@@ -46,6 +48,8 @@ export interface UpdateSiteRequest {
|
|||||||
collect_device_info?: boolean
|
collect_device_info?: boolean
|
||||||
collect_geo_data?: GeoDataLevel
|
collect_geo_data?: GeoDataLevel
|
||||||
collect_screen_resolution?: boolean
|
collect_screen_resolution?: boolean
|
||||||
|
// Performance insights setting
|
||||||
|
enable_performance_insights?: boolean
|
||||||
// Session replay settings
|
// Session replay settings
|
||||||
replay_mode?: ReplayMode
|
replay_mode?: ReplayMode
|
||||||
replay_sampling_rate?: number
|
replay_sampling_rate?: number
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
// * Performance Monitoring (Core Web Vitals) State
|
// * Performance Monitoring (Core Web Vitals) State
|
||||||
let currentEventId = null;
|
let currentEventId = null;
|
||||||
let metrics = { lcp: 0, cls: 0, inp: 0 };
|
let metrics = { lcp: 0, cls: 0, inp: 0 };
|
||||||
|
let performanceInsightsEnabled = false;
|
||||||
|
|
||||||
// * Session Replay State
|
// * Session Replay State
|
||||||
let replayEnabled = false;
|
let replayEnabled = false;
|
||||||
@@ -74,7 +75,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function sendMetrics() {
|
function sendMetrics() {
|
||||||
if (!currentEventId || (metrics.lcp === 0 && metrics.cls === 0 && metrics.inp === 0)) return;
|
// * Only send metrics if performance insights are enabled
|
||||||
|
if (!performanceInsightsEnabled || !currentEventId || (metrics.lcp === 0 && metrics.cls === 0 && metrics.inp === 0)) return;
|
||||||
|
|
||||||
// * Use sendBeacon if available for reliability on unload
|
// * Use sendBeacon if available for reliability on unload
|
||||||
const data = JSON.stringify({
|
const data = JSON.stringify({
|
||||||
@@ -96,7 +98,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// * Start observing immediately
|
// * Start observing metrics immediately (buffered observers will capture early metrics)
|
||||||
|
// * Metrics will only be sent if performance insights are enabled (checked in sendMetrics)
|
||||||
observeMetrics();
|
observeMetrics();
|
||||||
|
|
||||||
// * Send metrics when user leaves or hides the page
|
// * Send metrics when user leaves or hides the page
|
||||||
@@ -214,6 +217,9 @@
|
|||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
replaySettings = await res.json();
|
replaySettings = await res.json();
|
||||||
replayMode = replaySettings.replay_mode;
|
replayMode = replaySettings.replay_mode;
|
||||||
|
|
||||||
|
// * Set performance insights enabled flag
|
||||||
|
performanceInsightsEnabled = replaySettings.enable_performance_insights === true;
|
||||||
|
|
||||||
// Check sampling rate
|
// Check sampling rate
|
||||||
if (replayMode !== 'disabled') {
|
if (replayMode !== 'disabled') {
|
||||||
|
|||||||
Reference in New Issue
Block a user