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 ( +
+
+

{label}

+

{desc}

+
+ +
+ ) +} + 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