mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
feat: add analytics init, identify, search tracking, consent header, settings toggle
This commit is contained in:
@@ -30,6 +30,7 @@ import { Fragment, useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { apiDelete, apiGet, apiPost, apiPut, clearToken, formatHeaders } from "@/lib/api";
|
||||
import { cn, copyToClipboard } from "@/lib/utils";
|
||||
import { useAnalyticsStore } from "@/stores/analytics-store";
|
||||
import { useSettingsStore } from "@/stores/settings-store";
|
||||
import { GemLogo } from "../common/gem-logo";
|
||||
import { AiFeaturesSection } from "./ai-features-section";
|
||||
@@ -50,6 +51,7 @@ type Section =
|
||||
| "api-keys"
|
||||
| "ai-features"
|
||||
| "tools"
|
||||
| "analytics"
|
||||
| "about";
|
||||
|
||||
interface NavItem {
|
||||
@@ -70,6 +72,7 @@ const NAV_ITEMS: NavItem[] = [
|
||||
{ id: "api-keys", label: "API Keys", icon: Key },
|
||||
{ id: "ai-features", label: "AI Features", icon: Sparkles, requiredPermission: "settings:write" },
|
||||
{ id: "tools", label: "Tools", icon: Wrench },
|
||||
{ id: "analytics", label: "Product Analytics", icon: Eye },
|
||||
{ id: "about", label: "About", icon: Info },
|
||||
];
|
||||
|
||||
@@ -147,6 +150,7 @@ export function SettingsDialog({ open, onClose }: SettingsDialogProps) {
|
||||
{section === "api-keys" && <ApiKeysSection />}
|
||||
{section === "ai-features" && <AiFeaturesSection />}
|
||||
{section === "tools" && <ToolsSection />}
|
||||
{section === "analytics" && <AnalyticsSection />}
|
||||
{section === "about" && <AboutSection />}
|
||||
</div>
|
||||
</div>
|
||||
@@ -2428,6 +2432,69 @@ function ToolsSection() {
|
||||
);
|
||||
}
|
||||
|
||||
/* ────────────────────── Analytics ────────────────────── */
|
||||
|
||||
function AnalyticsSection() {
|
||||
const { consent, config, configLoaded, fetchConfig, toggleAnalytics } = useAnalyticsStore();
|
||||
|
||||
useEffect(() => {
|
||||
fetchConfig();
|
||||
}, [fetchConfig]);
|
||||
|
||||
if (!configLoaded) return null;
|
||||
|
||||
const disabled = !config?.enabled;
|
||||
const enabled = consent.analyticsEnabled === true;
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<h3 className="text-sm font-medium text-foreground">Product Analytics</h3>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
Share anonymous usage data to help improve ashim.
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">Your images never leave your machine.</p>
|
||||
</div>
|
||||
|
||||
{disabled ? (
|
||||
<p className="text-xs text-muted-foreground italic">
|
||||
Product analytics has been disabled by the server administrator.
|
||||
</p>
|
||||
) : (
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm text-foreground">
|
||||
{enabled ? "Analytics enabled" : "Analytics disabled"}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => toggleAnalytics(!enabled)}
|
||||
className={cn(
|
||||
"relative inline-flex h-6 w-11 items-center rounded-full transition-colors",
|
||||
enabled ? "bg-primary" : "bg-muted-foreground/30",
|
||||
)}
|
||||
>
|
||||
<span
|
||||
className={cn(
|
||||
"inline-block h-4 w-4 rounded-full bg-white transition-transform",
|
||||
enabled ? "translate-x-6" : "translate-x-1",
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<a
|
||||
href="/privacy"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-xs text-primary hover:underline"
|
||||
>
|
||||
Learn more
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/* ────────────────────── About ────────────────────── */
|
||||
|
||||
function AboutSection() {
|
||||
|
||||
Reference in New Issue
Block a user