diff --git a/app/sites/[id]/settings/page.tsx b/app/sites/[id]/settings/page.tsx index 6795db4..9d26399 100644 --- a/app/sites/[id]/settings/page.tsx +++ b/app/sites/[id]/settings/page.tsx @@ -809,9 +809,15 @@ export default function SiteSettingsPage() { Add this script to your website to start tracking visitors. Choose your framework for setup instructions.

{ + try { + await updateSite(siteId, { name: site.name, script_features: features }) + mutateSite() + } catch { /* silent — not critical */ } + }} />
diff --git a/components/sites/ScriptSetupBlock.tsx b/components/sites/ScriptSetupBlock.tsx index 339fbfb..6648d27 100644 --- a/components/sites/ScriptSetupBlock.tsx +++ b/components/sites/ScriptSetupBlock.tsx @@ -37,6 +37,7 @@ type FeatureKey = (typeof FEATURES)[number]['key'] | 'frustration' export interface ScriptSetupBlockSite { domain: string name?: string + script_features?: Record } interface ScriptSetupBlockProps { @@ -44,27 +45,39 @@ interface ScriptSetupBlockProps { site: ScriptSetupBlockSite /** Called when user copies the script (e.g. for analytics). */ onScriptCopy?: () => void + /** Called when features change so the parent can save to backend. */ + onFeaturesChange?: (features: Record) => void /** Show framework picker. Default true. */ showFrameworkPicker?: boolean /** Optional class for the root wrapper. */ className?: string } +const DEFAULT_FEATURES: Record = { + scroll: true, + '404': true, + outbound: true, + downloads: true, + frustration: false, +} + export default function ScriptSetupBlock({ site, onScriptCopy, + onFeaturesChange, showFrameworkPicker = true, className = '', }: ScriptSetupBlockProps) { + const sf = site.script_features || {} const [features, setFeatures] = useState>({ - scroll: true, - '404': true, - outbound: true, - downloads: true, - frustration: false, + scroll: sf.scroll != null ? Boolean(sf.scroll) : DEFAULT_FEATURES.scroll, + '404': sf['404'] != null ? Boolean(sf['404']) : DEFAULT_FEATURES['404'], + outbound: sf.outbound != null ? Boolean(sf.outbound) : DEFAULT_FEATURES.outbound, + downloads: sf.downloads != null ? Boolean(sf.downloads) : DEFAULT_FEATURES.downloads, + frustration: sf.frustration != null ? Boolean(sf.frustration) : DEFAULT_FEATURES.frustration, }) - const [storage, setStorage] = useState('local') - const [ttl, setTtl] = useState('24') + const [storage, setStorage] = useState(typeof sf.storage === 'string' ? sf.storage : 'local') + const [ttl, setTtl] = useState(typeof sf.ttl === 'string' ? sf.ttl : '24') const [framework, setFramework] = useState('') const [copied, setCopied] = useState(false) @@ -97,7 +110,11 @@ export default function ScriptSetupBlock({ }, [scriptSnippet, onScriptCopy]) const toggleFeature = (key: FeatureKey) => { - setFeatures((prev) => ({ ...prev, [key]: !prev[key] })) + setFeatures((prev) => { + const next = { ...prev, [key]: !prev[key] } + onFeaturesChange?.({ ...next, storage, ttl }) + return next + }) } const selectedIntegration = framework ? getIntegration(framework) : null @@ -201,7 +218,7 @@ export default function ScriptSetupBlock({ { setTtl(v); onFeaturesChange?.({ ...features, storage, ttl: v }) }} options={TTL_OPTIONS} />
diff --git a/lib/api/sites.ts b/lib/api/sites.ts index 7093b57..5cdf284 100644 --- a/lib/api/sites.ts +++ b/lib/api/sites.ts @@ -23,6 +23,8 @@ export interface Site { hide_unknown_locations?: boolean // Data retention (months); 0 = keep forever data_retention_months?: number + // Script feature toggles + script_features?: Record is_verified?: boolean created_at: string updated_at: string @@ -49,6 +51,8 @@ export interface UpdateSiteRequest { collect_screen_resolution?: boolean // Bot and noise filtering filter_bots?: boolean + // Script feature toggles + script_features?: Record // Hide unknown locations from stats hide_unknown_locations?: boolean // Data retention (months); 0 = keep forever