From ac7ac0e39e8c5b285bd802b5632bd40eea99125b Mon Sep 17 00:00:00 2001 From: Vivek R <123vivekr@gmail.com> Date: Wed, 30 Jul 2025 19:44:17 +0530 Subject: [PATCH] feat: add analytics consent UI components - Create AnalyticsConsent modal dialog for initial consent - Add AnalyticsConsentBanner for non-intrusive consent request - Implement privacy-focused consent flow with clear data collection info - Show what data is collected and privacy protections - Support both controlled and uncontrolled component usage - Add smooth animations with Framer Motion - Include accept/decline handlers with analytics service integration --- src/components/AnalyticsConsent.tsx | 235 ++++++++++++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 src/components/AnalyticsConsent.tsx diff --git a/src/components/AnalyticsConsent.tsx b/src/components/AnalyticsConsent.tsx new file mode 100644 index 0000000..dfd4c4c --- /dev/null +++ b/src/components/AnalyticsConsent.tsx @@ -0,0 +1,235 @@ +import React, { useState, useEffect } from 'react'; +import { motion, AnimatePresence } from 'framer-motion'; +import { BarChart3, Shield, X, Check, Info } from 'lucide-react'; +import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog'; +import { Button } from '@/components/ui/button'; +import { Card } from '@/components/ui/card'; +import { analytics } from '@/lib/analytics'; +import { cn } from '@/lib/utils'; + +interface AnalyticsConsentProps { + open?: boolean; + onOpenChange?: (open: boolean) => void; + onComplete?: () => void; +} + +export const AnalyticsConsent: React.FC = ({ + open: controlledOpen, + onOpenChange, + onComplete, +}) => { + const [internalOpen, setInternalOpen] = useState(false); + const [hasShownConsent, setHasShownConsent] = useState(false); + + const isControlled = controlledOpen !== undefined; + const open = isControlled ? controlledOpen : internalOpen; + + useEffect(() => { + // Check if we should show the consent dialog + const checkConsent = async () => { + await analytics.initialize(); + const settings = analytics.getSettings(); + + if (!settings?.hasConsented && !hasShownConsent) { + if (!isControlled) { + setInternalOpen(true); + } + setHasShownConsent(true); + } + }; + + checkConsent(); + }, [isControlled, hasShownConsent]); + + const handleOpenChange = (newOpen: boolean) => { + if (isControlled && onOpenChange) { + onOpenChange(newOpen); + } else { + setInternalOpen(newOpen); + } + }; + + const handleAccept = async () => { + await analytics.enable(); + handleOpenChange(false); + onComplete?.(); + }; + + const handleDecline = async () => { + await analytics.disable(); + handleOpenChange(false); + onComplete?.(); + }; + + return ( + + +
+ +
+
+ +
+ Help Improve Claudia +
+ + We'd like to collect anonymous usage data to improve your experience. + +
+
+ +
+
+ +
+ +
+

What we collect:

+
    +
  • • Feature usage (which tools and commands you use)
  • +
  • • Performance metrics (app speed and reliability)
  • +
  • • Error reports (to fix bugs and improve stability)
  • +
  • • General usage patterns (session frequency and duration)
  • +
+
+
+
+ + +
+ +
+

Your privacy is protected:

+
    +
  • • No personal information is collected
  • +
  • • No file contents, paths, or project names
  • +
  • • No API keys or sensitive data
  • +
  • • Completely anonymous with random IDs
  • +
  • • You can opt-out anytime in Settings
  • +
+
+
+
+
+ +
+
+ +

+ This data helps us understand which features are most valuable, identify performance + issues, and prioritize improvements. Your choice won't affect any functionality. +

+
+
+
+ +
+ + +
+
+
+ ); +}; + +interface AnalyticsConsentBannerProps { + className?: string; +} + +export const AnalyticsConsentBanner: React.FC = ({ + className, +}) => { + const [visible, setVisible] = useState(false); + const [hasChecked, setHasChecked] = useState(false); + + useEffect(() => { + const checkConsent = async () => { + if (hasChecked) return; + + await analytics.initialize(); + const settings = analytics.getSettings(); + + if (!settings?.hasConsented) { + setVisible(true); + } + setHasChecked(true); + }; + + // Delay banner appearance for better UX + const timer = setTimeout(checkConsent, 2000); + return () => clearTimeout(timer); + }, [hasChecked]); + + const handleAccept = async () => { + await analytics.enable(); + setVisible(false); + }; + + const handleDecline = async () => { + await analytics.disable(); + setVisible(false); + }; + + return ( + + {visible && ( + + +
+ +
+

Help improve Claudia

+

+ We collect anonymous usage data to improve your experience. No personal data is collected. +

+
+ + +
+
+ +
+
+
+ )} +
+ ); +};