diff --git a/components/settings/unified/tabs/SiteBotSpamTab.tsx b/components/settings/unified/tabs/SiteBotSpamTab.tsx
index 173faf3..27ef6c9 100644
--- a/components/settings/unified/tabs/SiteBotSpamTab.tsx
+++ b/components/settings/unified/tabs/SiteBotSpamTab.tsx
@@ -23,11 +23,12 @@ export default function SiteBotSpamTab({ siteId, onDirtyChange }: { siteId: stri
const { data: sessionsData, mutate: mutateSessions } = useSessions(siteId, botDateRange.start, botDateRange.end, botView === 'review' ? suspiciousOnly : false)
const sessions = sessionsData?.sessions
+ const hasInitialized = useRef(false)
useEffect(() => {
- if (site) {
- setFilterBots(site.filter_bots ?? false)
- initialFilterRef.current = site.filter_bots ?? false
- }
+ if (!site || hasInitialized.current) return
+ setFilterBots(site.filter_bots ?? false)
+ initialFilterRef.current = site.filter_bots ?? false
+ hasInitialized.current = true
}, [site])
// Track dirty state
diff --git a/components/settings/unified/tabs/SiteGeneralTab.tsx b/components/settings/unified/tabs/SiteGeneralTab.tsx
index 9b40fab..b80c5e3 100644
--- a/components/settings/unified/tabs/SiteGeneralTab.tsx
+++ b/components/settings/unified/tabs/SiteGeneralTab.tsx
@@ -41,15 +41,16 @@ export default function SiteGeneralTab({ siteId, onDirtyChange }: { siteId: stri
const canEdit = user?.role === 'owner' || user?.role === 'admin'
const initialRef = useRef('')
+ const hasInitialized = useRef(false)
const [isDirty, setIsDirty] = useState(false)
useEffect(() => {
- if (site) {
- setName(site.name || '')
- setTimezone(site.timezone || 'UTC')
- initialRef.current = JSON.stringify({ name: site.name || '', timezone: site.timezone || 'UTC' })
- setIsDirty(false)
- }
+ if (!site || hasInitialized.current) return
+ setName(site.name || '')
+ setTimezone(site.timezone || 'UTC')
+ initialRef.current = JSON.stringify({ name: site.name || '', timezone: site.timezone || 'UTC' })
+ hasInitialized.current = true
+ setIsDirty(false)
}, [site])
// Track dirty state
diff --git a/components/settings/unified/tabs/SitePrivacyTab.tsx b/components/settings/unified/tabs/SitePrivacyTab.tsx
index a6a9e51..ffc77de 100644
--- a/components/settings/unified/tabs/SitePrivacyTab.tsx
+++ b/components/settings/unified/tabs/SitePrivacyTab.tsx
@@ -16,6 +16,18 @@ const GEO_OPTIONS = [
{ value: 'none', label: 'Disabled' },
]
+function PrivacyToggle({ label, desc, checked, onToggle }: { label: string; desc: string; checked: boolean; onToggle: () => void }) {
+ return (
+
+ )
+}
+
export default function SitePrivacyTab({ siteId, onDirtyChange }: { siteId: string; onDirtyChange?: (dirty: boolean) => void }) {
const { data: site, mutate } = useSite(siteId)
const { data: subscription, error: subscriptionError, mutate: mutateSubscription } = useSubscription()
@@ -33,28 +45,30 @@ export default function SitePrivacyTab({ siteId, onDirtyChange }: { siteId: stri
const [isDirty, setIsDirty] = useState(false)
const initialRef = useRef('')
+ // Sync form state from site data — only on first load, not on SWR revalidation
+ const hasInitialized = useRef(false)
useEffect(() => {
- if (site) {
- setCollectPagePaths(site.collect_page_paths ?? true)
- setCollectReferrers(site.collect_referrers ?? true)
- setCollectDeviceInfo(site.collect_device_info ?? true)
- setCollectScreenRes(site.collect_screen_resolution ?? true)
- setCollectGeoData(site.collect_geo_data ?? 'full')
- setHideUnknownLocations(site.hide_unknown_locations ?? false)
- setDataRetention(site.data_retention_months ?? 6)
- setExcludedPaths((site.excluded_paths || []).join('\n'))
- initialRef.current = JSON.stringify({
- collectPagePaths: site.collect_page_paths ?? true,
- collectReferrers: site.collect_referrers ?? true,
- collectDeviceInfo: site.collect_device_info ?? true,
- collectScreenRes: site.collect_screen_resolution ?? true,
- collectGeoData: site.collect_geo_data ?? 'full',
- hideUnknownLocations: site.hide_unknown_locations ?? false,
- dataRetention: site.data_retention_months ?? 6,
- excludedPaths: (site.excluded_paths || []).join('\n'),
- })
- setIsDirty(false)
- }
+ if (!site || hasInitialized.current) return
+ setCollectPagePaths(site.collect_page_paths ?? true)
+ setCollectReferrers(site.collect_referrers ?? true)
+ setCollectDeviceInfo(site.collect_device_info ?? true)
+ setCollectScreenRes(site.collect_screen_resolution ?? true)
+ setCollectGeoData(site.collect_geo_data ?? 'full')
+ setHideUnknownLocations(site.hide_unknown_locations ?? false)
+ setDataRetention(site.data_retention_months ?? 6)
+ setExcludedPaths((site.excluded_paths || []).join('\n'))
+ initialRef.current = JSON.stringify({
+ collectPagePaths: site.collect_page_paths ?? true,
+ collectReferrers: site.collect_referrers ?? true,
+ collectDeviceInfo: site.collect_device_info ?? true,
+ collectScreenRes: site.collect_screen_resolution ?? true,
+ collectGeoData: site.collect_geo_data ?? 'full',
+ hideUnknownLocations: site.hide_unknown_locations ?? false,
+ dataRetention: site.data_retention_months ?? 6,
+ excludedPaths: (site.excluded_paths || []).join('\n'),
+ })
+ hasInitialized.current = true
+ setIsDirty(false)
}, [site])
// Track dirty state
@@ -101,21 +115,11 @@ export default function SitePrivacyTab({ siteId, onDirtyChange }: { siteId: stri
- {[
- { label: 'Page paths', desc: 'Track which pages visitors view.', checked: collectPagePaths, onChange: setCollectPagePaths },
- { label: 'Referrers', desc: 'Track where visitors come from.', checked: collectReferrers, onChange: setCollectReferrers },
- { label: 'Device info', desc: 'Track browser, OS, and device type.', checked: collectDeviceInfo, onChange: setCollectDeviceInfo },
- { label: 'Screen resolution', desc: 'Track visitor screen dimensions.', checked: collectScreenRes, onChange: setCollectScreenRes },
- { label: 'Hide unknown locations', desc: 'Exclude "Unknown" from location stats.', checked: hideUnknownLocations, onChange: setHideUnknownLocations },
- ].map(item => (
-
-
-
{item.label}
-
{item.desc}
-
-
item.onChange((p: boolean) => !p)} />
-
- ))}
+
setCollectPagePaths(v => !v)} />
+ setCollectReferrers(v => !v)} />
+ setCollectDeviceInfo(v => !v)} />
+ setCollectScreenRes(v => !v)} />
+ setHideUnknownLocations(v => !v)} />
diff --git a/components/settings/unified/tabs/SiteVisibilityTab.tsx b/components/settings/unified/tabs/SiteVisibilityTab.tsx
index 2c28efc..22c5d9f 100644
--- a/components/settings/unified/tabs/SiteVisibilityTab.tsx
+++ b/components/settings/unified/tabs/SiteVisibilityTab.tsx
@@ -18,14 +18,15 @@ export default function SiteVisibilityTab({ siteId, onDirtyChange }: { siteId: s
const [linkCopied, setLinkCopied] = useState(false)
const [isDirty, setIsDirty] = useState(false)
const initialRef = useRef('')
+ const hasInitialized = useRef(false)
useEffect(() => {
- if (site) {
- setIsPublic(site.is_public ?? false)
- setPasswordEnabled(site.has_password ?? false)
- initialRef.current = JSON.stringify({ isPublic: site.is_public ?? false, passwordEnabled: site.has_password ?? false })
- setIsDirty(false)
- }
+ if (!site || hasInitialized.current) return
+ setIsPublic(site.is_public ?? false)
+ setPasswordEnabled(site.has_password ?? false)
+ initialRef.current = JSON.stringify({ isPublic: site.is_public ?? false, passwordEnabled: site.has_password ?? false })
+ hasInitialized.current = true
+ setIsDirty(false)
}, [site])
// Track dirty state