From 0e70729e3889be8f2d4e13a77df3dec0f8d7ee92 Mon Sep 17 00:00:00 2001 From: ConnorYoh <40631091+ConnorYoh@users.noreply.github.com> Date: Mon, 27 Oct 2025 16:54:59 +0000 Subject: [PATCH] Customised Analytics for admins and users (#4687) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds granular privacy controls for analytics - splits single enableAnalytics toggle into separate PostHog and Scarf controls with improved admin UX. Backend Changes Configuration (ApplicationProperties.java) - Added enablePosthog and enableScarf boolean fields - New methods: isPosthogEnabled(), isScarfEnabled() (null = enabled when analytics is on) Services - PostHogService: Now checks isPosthogEnabled() instead of isAnalyticsEnabled() - ConfigController: Exposes new flags via API - SettingsController: Changed endpoint from @RequestBody to @RequestParam Frontend Changes Architecture - Converted useAppConfig hook → AppConfigContext provider for global access - Added refetch() method for config updates without reload New Features 1. AdminAnalyticsChoiceModal: First-launch modal when enableAnalytics === null - Enable/disable without editing YAML - Includes documentation link 2. Scarf Tracking System: Modular utility with React hook wrapper - Respects config + per-service cookie consent - Works from any code location (React or vanilla JS) 3. Enhanced Cookie Consent: Per-service toggles (PostHog and Scarf separate) Integration - App.tsx: Added AppConfigProvider + scarf initializer - HomePage.tsx: Shows admin modal when needed - index.tsx: PostHog opt-out by default, service-level consent Key Benefits ✅ Backward compatible (null defaults to enabled) ✅ Granular control per analytics service ✅ First-launch admin modal (no YAML editing) ✅ Privacy-focused with opt-out defaults ✅ API-based config updates --------- Co-authored-by: Connor Yoh # Conflicts: # frontend/src/App.tsx # frontend/src/contexts/AppConfigContext.tsx --- frontend/src/core/contexts/AppConfigContext.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/core/contexts/AppConfigContext.tsx b/frontend/src/core/contexts/AppConfigContext.tsx index d59fa63ed..d8a2a13c2 100644 --- a/frontend/src/core/contexts/AppConfigContext.tsx +++ b/frontend/src/core/contexts/AppConfigContext.tsx @@ -42,13 +42,13 @@ interface AppConfigContextValue { refetch: () => Promise; } -// Create context const AppConfigContext = createContext({ config: null, loading: true, error: null, refetch: async () => {}, }); + /** * Provider component that fetches and provides app configuration * Should be placed at the top level of the app, before any components that need config @@ -99,7 +99,7 @@ export const AppConfigProvider: React.FC<{ children: React.ReactNode }> = ({ chi } const data: AppConfig = await response.json(); - console.debug('[AppConfigContext] Config fetched successfully:', data); + console.debug('[AppConfig] Config fetched successfully:', data); setConfig(data); } catch (err) { const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';