'use client' import { useEffect, useState } from 'react' import { useParams, useRouter } from 'next/navigation' import { getSite, updateSite, resetSiteData, deleteSite, type Site, type GeoDataLevel, type ReplayMode } from '@/lib/api/sites' import { getRealtime } from '@/lib/api/stats' import { toast } from 'sonner' import LoadingOverlay from '@/components/LoadingOverlay' import VerificationModal from '@/components/sites/VerificationModal' import PasswordInput from '@/components/PasswordInput' import { APP_URL, API_URL } from '@/lib/api/client' import { motion, AnimatePresence } from 'framer-motion' import { GearIcon, GlobeIcon, FileTextIcon, CheckIcon, CopyIcon, ExclamationTriangleIcon, LightningBoltIcon, VideoIcon, } from '@radix-ui/react-icons' const TIMEZONES = [ 'UTC', 'America/New_York', 'America/Los_Angeles', 'America/Chicago', 'America/Toronto', 'Europe/London', 'Europe/Paris', 'Europe/Berlin', 'Europe/Amsterdam', 'Asia/Tokyo', 'Asia/Singapore', 'Asia/Dubai', 'Australia/Sydney', 'Pacific/Auckland', ] export default function SiteSettingsPage() { const params = useParams() const router = useRouter() const siteId = params.id as string const [site, setSite] = useState(null) const [loading, setLoading] = useState(true) const [saving, setSaving] = useState(false) const [activeTab, setActiveTab] = useState<'general' | 'visibility' | 'data' | 'replay'>('general') const [formData, setFormData] = useState({ name: '', timezone: 'UTC', is_public: false, password: '', excluded_paths: '', // Data collection settings collect_page_paths: true, collect_referrers: true, collect_device_info: true, collect_geo_data: 'full' as GeoDataLevel, collect_screen_resolution: true, // Session replay settings replay_mode: 'disabled' as ReplayMode, replay_sampling_rate: 100, replay_retention_days: 30, replay_mask_all_text: false, replay_mask_all_inputs: true }) const [scriptCopied, setScriptCopied] = useState(false) const [linkCopied, setLinkCopied] = useState(false) const [showVerificationModal, setShowVerificationModal] = useState(false) const [isPasswordEnabled, setIsPasswordEnabled] = useState(false) useEffect(() => { loadSite() }, [siteId]) const loadSite = async () => { try { setLoading(true) const data = await getSite(siteId) setSite(data) setFormData({ name: data.name, timezone: data.timezone || 'UTC', is_public: data.is_public || false, password: '', // Don't show existing password excluded_paths: (data.excluded_paths || []).join('\n'), // Data collection settings (default to true/full for backwards compatibility) collect_page_paths: data.collect_page_paths ?? true, collect_referrers: data.collect_referrers ?? true, collect_device_info: data.collect_device_info ?? true, collect_geo_data: data.collect_geo_data || 'full', collect_screen_resolution: data.collect_screen_resolution ?? true, // Session replay settings replay_mode: data.replay_mode || 'disabled', replay_sampling_rate: data.replay_sampling_rate ?? 100, replay_retention_days: data.replay_retention_days ?? 30, replay_mask_all_text: data.replay_mask_all_text ?? false, replay_mask_all_inputs: data.replay_mask_all_inputs ?? true }) if (data.has_password) { setIsPasswordEnabled(true) } else { setIsPasswordEnabled(false) } } catch (error: any) { toast.error('Failed to load site: ' + (error.message || 'Unknown error')) } finally { setLoading(false) } } const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() setSaving(true) try { const excludedPathsArray = formData.excluded_paths .split('\n') .map(p => p.trim()) .filter(p => p.length > 0) await updateSite(siteId, { name: formData.name, timezone: formData.timezone, is_public: formData.is_public, password: isPasswordEnabled ? (formData.password || undefined) : undefined, clear_password: !isPasswordEnabled, excluded_paths: excludedPathsArray, // Data collection settings collect_page_paths: formData.collect_page_paths, collect_referrers: formData.collect_referrers, collect_device_info: formData.collect_device_info, collect_geo_data: formData.collect_geo_data, collect_screen_resolution: formData.collect_screen_resolution, // Session replay settings replay_mode: formData.replay_mode, replay_sampling_rate: formData.replay_sampling_rate, replay_retention_days: formData.replay_retention_days, replay_mask_all_text: formData.replay_mask_all_text, replay_mask_all_inputs: formData.replay_mask_all_inputs }) toast.success('Site updated successfully') loadSite() } catch (error: any) { toast.error('Failed to update site: ' + (error.message || 'Unknown error')) } finally { setSaving(false) } } const handleResetData = async () => { if (!confirm('Are you sure you want to delete ALL data for this site? This action cannot be undone.')) { return } try { await resetSiteData(siteId) toast.success('All site data has been reset') } catch (error: any) { toast.error('Failed to reset data: ' + (error.message || 'Unknown error')) } } const handleDeleteSite = async () => { const confirmation = prompt('To confirm deletion, please type the site domain:') if (confirmation !== site?.domain) { if (confirmation) toast.error('Domain does not match') return } try { await deleteSite(siteId) toast.success('Site deleted successfully') router.push('/') } catch (error: any) { toast.error('Failed to delete site: ' + (error.message || 'Unknown error')) } } const copyScript = () => { const script = `` navigator.clipboard.writeText(script) setScriptCopied(true) toast.success('Script copied to clipboard') setTimeout(() => setScriptCopied(false), 2000) } const copyLink = () => { const link = `${APP_URL}/share/${siteId}` navigator.clipboard.writeText(link) setLinkCopied(true) toast.success('Link copied to clipboard') setTimeout(() => setLinkCopied(false), 2000) } if (loading) { return } if (!site) { return (

Site not found

) } return (

Site Settings

Manage settings for {site.domain}

{/* Sidebar Navigation */} {/* Content Area */}
{activeTab === 'general' && (

General Configuration

Update your site details and tracking script.

setFormData({ ...formData, name: e.target.value })} className="w-full px-4 py-2 border border-neutral-200 dark:border-neutral-800 rounded-xl bg-neutral-50/50 dark:bg-neutral-900/50 focus:bg-white dark:focus:bg-neutral-900 focus:border-brand-orange focus:ring-4 focus:ring-brand-orange/10 outline-none transition-all duration-200 dark:text-white" />

Domain cannot be changed after creation

Tracking Script

Add this script to your website to start tracking visitors.

{``}

Check if your site is sending data correctly.

Danger Zone

Irreversible actions for your site.

Reset Data

Delete all stats and events. This cannot be undone.

Delete Site

Permanently delete this site and all data.

)} {activeTab === 'visibility' && (

Visibility Settings

Manage who can view your dashboard.

Public Dashboard

Allow anyone with the link to view this dashboard

{formData.is_public && (

Share this link with others to view the dashboard.

Password Protection

Restrict access to this dashboard.

{isPasswordEnabled && ( setFormData({ ...formData, password: value })} placeholder={site.has_password ? "Change password (leave empty to keep current)" : "Set a password"} />

Visitors will need to enter this password to view the dashboard.

)}
)}
)} {activeTab === 'data' && (

Data & Privacy

Control what visitor data is collected. Less data = more privacy.

{/* Data Collection Controls */}

Data Collection

{/* Page Paths Toggle */}

Page Paths

Track which pages visitors view

{/* Referrers Toggle */}

Referrers

Track where visitors come from

{/* Device Info Toggle */}

Device Info

Track browser, OS, and device type

{/* Geographic Data Dropdown */}

Geographic Data

Control location tracking granularity

{/* Screen Resolution Toggle */}

Screen Resolution

Track visitor screen sizes

{/* Excluded Paths */}

Path Filtering