mirror of
https://github.com/f/awesome-chatgpt-prompts.git
synced 2026-02-12 15:52:47 +00:00
feat(analytics): add external link click tracking in footer and header components
This commit is contained in:
@@ -15,6 +15,7 @@ import {
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { toast } from "sonner";
|
||||
import { analyticsComment } from "@/lib/analytics";
|
||||
|
||||
interface CommentFormProps {
|
||||
promptId: string;
|
||||
@@ -90,6 +91,7 @@ export function CommentForm({
|
||||
const data = await response.json();
|
||||
onCommentAdded(data.comment);
|
||||
setContent("");
|
||||
analyticsComment.post(promptId, !!parentId);
|
||||
toast.success(t("commentPosted"));
|
||||
} catch (error) {
|
||||
toast.error(error instanceof Error ? error.message : tCommon("error"));
|
||||
|
||||
@@ -5,6 +5,7 @@ import Link from "next/link";
|
||||
import { useTranslations } from "next-intl";
|
||||
import DeepWikiIcon from "@/../public/deepwiki.svg";
|
||||
import { useBranding } from "@/components/providers/branding-provider";
|
||||
import { analyticsExternal } from "@/lib/analytics";
|
||||
|
||||
export function Footer() {
|
||||
const branding = useBranding();
|
||||
@@ -21,7 +22,7 @@ export function Footer() {
|
||||
<nav className="flex flex-wrap items-center justify-center gap-x-4 gap-y-2">
|
||||
{!branding.useCloneBranding && (
|
||||
<>
|
||||
<Link href="https://deepwiki.com/f/awesome-chatgpt-prompts" target="_blank" rel="noopener noreferrer" className="hover:text-foreground flex items-center gap-1">
|
||||
<Link href="https://deepwiki.com/f/awesome-chatgpt-prompts" target="_blank" rel="noopener noreferrer" className="hover:text-foreground flex items-center gap-1" onClick={() => analyticsExternal.clickFooterLink("deepwiki")}>
|
||||
<Image src={DeepWikiIcon} alt="" width={14} height={14} />
|
||||
DeepWiki
|
||||
</Link>
|
||||
@@ -34,7 +35,7 @@ export function Footer() {
|
||||
<Link href="/about" className="hover:text-foreground">{t("about")}</Link>
|
||||
</>
|
||||
)}
|
||||
<Link href="https://github.com/f/awesome-chatgpt-prompts" target="_blank" rel="noopener noreferrer" className="hover:text-foreground flex items-center gap-1">
|
||||
<Link href="https://github.com/f/awesome-chatgpt-prompts" target="_blank" rel="noopener noreferrer" className="hover:text-foreground flex items-center gap-1" onClick={() => analyticsExternal.clickFooterLink("github")}>
|
||||
<svg className="h-3.5 w-3.5" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path fillRule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clipRule="evenodd" />
|
||||
</svg>
|
||||
|
||||
@@ -45,7 +45,7 @@ import {
|
||||
import { NotificationBell } from "@/components/layout/notification-bell";
|
||||
import { setLocale } from "@/lib/i18n/client";
|
||||
import { useBranding } from "@/components/providers/branding-provider";
|
||||
import { analyticsAuth, analyticsSettings } from "@/lib/analytics";
|
||||
import { analyticsAuth, analyticsSettings, analyticsExternal } from "@/lib/analytics";
|
||||
import { isChromeBrowser } from "@/lib/utils";
|
||||
|
||||
const languages = [
|
||||
@@ -349,6 +349,7 @@ export function Header({ authProvider = "credentials", allowRegistration = true
|
||||
href={branding.chromeExtensionUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={() => analyticsExternal.clickChromeExtension()}
|
||||
>
|
||||
<Chromium className="h-4 w-4" />
|
||||
<span className="sr-only">Get Chrome Extension</span>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useState } from "react";
|
||||
import { Check, Copy } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { analyticsMcp } from "@/lib/analytics";
|
||||
|
||||
type Client = "cursor" | "claude-code" | "vscode" | "codex" | "windsurf" | "gemini";
|
||||
type Mode = "remote" | "local";
|
||||
@@ -209,6 +210,7 @@ export function McpConfigTabs({ baseUrl, queryParams, className, mode, onModeCha
|
||||
|
||||
const handleCopy = async () => {
|
||||
await navigator.clipboard.writeText(config);
|
||||
analyticsMcp.copyCommand(`${selectedClient}-${selectedMode}`);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
};
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { McpConfigTabs } from "./mcp-config-tabs";
|
||||
import { analyticsMcp } from "@/lib/analytics";
|
||||
|
||||
// MCP Logo component - shows dark version in dark mode
|
||||
export function McpIcon({ className }: { className?: string }) {
|
||||
@@ -129,7 +130,10 @@ export function McpServerPopup({
|
||||
const removeTag = (tag: string) => setTags(tags.filter((t) => t !== tag));
|
||||
|
||||
return (
|
||||
<Popover modal open={isOpen} onOpenChange={setIsOpen}>
|
||||
<Popover modal open={isOpen} onOpenChange={(open) => {
|
||||
if (open) analyticsMcp.openPopup();
|
||||
setIsOpen(open);
|
||||
}}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button variant="outline" size="sm" className="h-8 gap-1.5">
|
||||
<McpIcon className="h-4 w-4" />
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useTranslations } from "next-intl";
|
||||
import { Bookmark, Check, Loader2 } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { analyticsCollection } from "@/lib/analytics";
|
||||
|
||||
interface AddToCollectionButtonProps {
|
||||
promptId: string;
|
||||
@@ -38,6 +39,7 @@ export function AddToCollectionButton({
|
||||
|
||||
if (res.ok) {
|
||||
setInCollection(false);
|
||||
analyticsCollection.remove(promptId);
|
||||
setShowTooltip(true);
|
||||
setTimeout(() => setShowTooltip(false), 2000);
|
||||
}
|
||||
@@ -50,6 +52,7 @@ export function AddToCollectionButton({
|
||||
|
||||
if (res.ok) {
|
||||
setInCollection(true);
|
||||
analyticsCollection.add(promptId);
|
||||
setShowTooltip(true);
|
||||
setTimeout(() => setShowTooltip(false), 2000);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useLocale, useTranslations } from "next-intl";
|
||||
import { Languages, Loader2 } from "lucide-react";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { toast } from "sonner";
|
||||
import { analyticsTranslate } from "@/lib/analytics";
|
||||
|
||||
// Map locale codes to full language names for OpenAI
|
||||
const localeToLanguage: Record<string, string> = {
|
||||
@@ -70,6 +71,7 @@ export function TranslateButton({ content, onTranslate, isLoggedIn }: TranslateB
|
||||
const data = await response.json();
|
||||
onTranslate(data.translatedContent);
|
||||
setIsTranslated(true);
|
||||
analyticsTranslate.translate(targetLanguage);
|
||||
toast.success(t("translated"));
|
||||
} catch (error) {
|
||||
console.error("Translation error:", error);
|
||||
|
||||
@@ -546,6 +546,77 @@ export const analyticsEngagement = {
|
||||
},
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Comment Events
|
||||
// ============================================================================
|
||||
|
||||
export const analyticsComment = {
|
||||
post: (promptId: string, isReply: boolean) => {
|
||||
trackEvent({
|
||||
action: isReply ? "post_reply" : "post_comment",
|
||||
category: "comment",
|
||||
prompt_id: promptId,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Collection Events
|
||||
// ============================================================================
|
||||
|
||||
export const analyticsCollection = {
|
||||
add: (promptId: string) => {
|
||||
trackEvent({
|
||||
action: "add_to_collection",
|
||||
category: "collection",
|
||||
prompt_id: promptId,
|
||||
});
|
||||
},
|
||||
|
||||
remove: (promptId: string) => {
|
||||
trackEvent({
|
||||
action: "remove_from_collection",
|
||||
category: "collection",
|
||||
prompt_id: promptId,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Translation Events
|
||||
// ============================================================================
|
||||
|
||||
export const analyticsTranslate = {
|
||||
translate: (targetLanguage: string) => {
|
||||
trackEvent({
|
||||
action: "translate_prompt",
|
||||
category: "translate",
|
||||
label: targetLanguage,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// External Link Events
|
||||
// ============================================================================
|
||||
|
||||
export const analyticsExternal = {
|
||||
clickChromeExtension: () => {
|
||||
trackEvent({
|
||||
action: "chrome_extension_click",
|
||||
category: "external",
|
||||
});
|
||||
},
|
||||
|
||||
clickFooterLink: (linkName: string) => {
|
||||
trackEvent({
|
||||
action: "footer_link_click",
|
||||
category: "external",
|
||||
label: linkName,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// Admin Events
|
||||
// ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user