feat(frontend): setup datafast custom events (#11231)

## Changes 🏗️

- Add [custom events](https://datafa.st/docs/custom-goals) in
**Datafa.st** to track the user journey around core actions
  - track `add_to_library`
  - track `download_agent`
  - track `run_agent`
  - track `schedule_agent` 
- Refactor the analytics service to encapsulate both **GA** and
**Datafa.st**

## Checklist 📋

### For code changes:
- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [x] I have tested my changes according to the test plan:
  - [x] Analytics load correctly locally
  - [x] Events fire in production
 
### For configuration changes:

Once deployed to production we need to verify we are receiving analytics
and custom events in [Datafa.st](https://datafa.st/)
This commit is contained in:
Ubbe
2025-10-22 16:56:30 +04:00
committed by GitHub
parent bd03697ff2
commit e203e65dc4
12 changed files with 220 additions and 374 deletions

View File

@@ -1,7 +1,7 @@
import Shepherd from "shepherd.js";
import "shepherd.js/dist/css/shepherd.css";
import { sendGAEvent } from "@/services/analytics/google-analytics";
import { Key, storage } from "@/services/storage/local-storage";
import { analytics } from "@/services/analytics";
export const startTutorial = (
emptyNodeList: (forceEmpty: boolean) => boolean,
@@ -555,7 +555,7 @@ export const startTutorial = (
"use client";
console.debug("sendTutorialStep");
sendGAEvent("event", "tutorial_step_shown", { value: step.id });
analytics.sendGAEvent("event", "tutorial_step_shown", { value: step.id });
});
}

View File

@@ -17,6 +17,7 @@ import { GraphExecutionJobInfo } from "@/app/api/__generated__/models/graphExecu
import { LibraryAgentPreset } from "@/app/api/__generated__/models/libraryAgentPreset";
import { useGetV1GetUserTimezone } from "@/app/api/__generated__/endpoints/auth/auth";
import { useOnboarding } from "@/providers/onboarding/onboarding-provider";
import { analytics } from "@/services/analytics";
export type RunVariant =
| "manual"
@@ -78,6 +79,10 @@ export function useAgentRunModal(
agent.graph_id,
).queryKey,
});
analytics.sendDatafastEvent("run_agent", {
name: agent.name,
id: agent.graph_id,
});
setIsOpen(false);
}
},
@@ -105,6 +110,11 @@ export function useAgentRunModal(
agent.graph_id,
),
});
analytics.sendDatafastEvent("schedule_agent", {
name: agent.name,
id: agent.graph_id,
cronExpression: cronExpression,
});
setIsOpen(false);
}
},

View File

@@ -37,6 +37,7 @@ import { useToastOnFail } from "@/components/molecules/Toast/use-toast";
import { AgentRunStatus, agentRunStatusMap } from "./agent-run-status-chip";
import useCredits from "@/hooks/useCredits";
import { AgentRunOutputView } from "./agent-run-output-view";
import { analytics } from "@/services/analytics";
export function AgentRunDetailsView({
agent,
@@ -131,7 +132,13 @@ export function AgentRunDetailsView({
run.inputs!,
run.credential_inputs!,
)
.then(({ id }) => onRun(id))
.then(({ id }) => {
analytics.sendDatafastEvent("run_agent", {
name: graph.name,
id: graph.id,
});
onRun(id);
})
.catch(toastOnFail("execute agent preset"));
}
@@ -142,7 +149,13 @@ export function AgentRunDetailsView({
run.inputs!,
run.credential_inputs!,
)
.then(({ id }) => onRun(id))
.then(({ id }) => {
analytics.sendDatafastEvent("run_agent", {
name: graph.name,
id: graph.id,
});
onRun(id);
})
.catch(toastOnFail("execute agent"));
}, [api, graph, run, onRun, toastOnFail]);

View File

@@ -43,6 +43,7 @@ import {
import { AgentStatus, AgentStatusChip } from "./agent-status-chip";
import { useOnboarding } from "@/providers/onboarding/onboarding-provider";
import { analytics } from "@/services/analytics";
export function AgentRunDraftView({
graph,
@@ -197,6 +198,12 @@ export function AgentRunDraftView({
}
// Mark run agent onboarding step as completed
completeOnboardingStep("MARKETPLACE_RUN_AGENT");
analytics.sendDatafastEvent("run_agent", {
name: graph.name,
id: graph.id,
});
if (runCount > 0) {
completeOnboardingStep("RE_RUN_AGENT");
}
@@ -373,6 +380,12 @@ export function AgentRunDraftView({
})
.catch(toastOnFail("set up agent run schedule"));
analytics.sendDatafastEvent("schedule_agent", {
name: graph.name,
id: graph.id,
cronExpression: cronExpression,
});
if (schedule && onCreateSchedule) onCreateSchedule(schedule);
},
[api, graph, inputValues, inputCredentials, onCreateSchedule, toastOnFail],

View File

@@ -6,10 +6,10 @@ import Link from "next/link";
import { User } from "@supabase/supabase-js";
import { cn } from "@/lib/utils";
import { useAgentInfo } from "./useAgentInfo";
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
interface AgentInfoProps {
user: User | null;
agentId: string;
name: string;
creator: string;
shortDescription: string;
@@ -20,11 +20,12 @@ interface AgentInfoProps {
lastUpdated: string;
version: string;
storeListingVersionId: string;
libraryAgent: LibraryAgent | undefined;
isAgentAddedToLibrary: boolean;
}
export const AgentInfo = ({
user,
agentId,
name,
creator,
shortDescription,
@@ -35,7 +36,7 @@ export const AgentInfo = ({
lastUpdated,
version,
storeListingVersionId,
libraryAgent,
isAgentAddedToLibrary,
}: AgentInfoProps) => {
const {
handleDownload,
@@ -82,11 +83,15 @@ export const AgentInfo = ({
"transition-colors duration-200 hover:bg-violet-500 disabled:bg-zinc-400",
)}
data-testid={"agent-add-library-button"}
onClick={handleLibraryAction}
disabled={isAddingAgentToLibrary}
onClick={() =>
handleLibraryAction({
isAddingAgentFirstTime: !isAgentAddedToLibrary,
})
}
>
<span className="justify-start font-sans text-sm font-medium leading-snug text-primary-foreground">
{libraryAgent ? "See runs" : "Add to library"}
{isAgentAddedToLibrary ? "See runs" : "Add to library"}
</span>
</button>
)}
@@ -96,7 +101,7 @@ export const AgentInfo = ({
"transition-colors duration-200 hover:bg-zinc-200/70 disabled:bg-zinc-200/40",
)}
data-testid={"agent-download-button"}
onClick={handleDownload}
onClick={() => handleDownload(agentId, name)}
disabled={isDownloadingAgent}
>
<div className="justify-start text-center font-sans text-sm font-medium leading-snug text-zinc-800">

View File

@@ -1,10 +1,11 @@
import { usePostV2AddMarketplaceAgent } from "@/app/api/__generated__/endpoints/library/library";
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
import { useToast } from "@/components/molecules/Toast/use-toast";
import { useRouter } from "next/navigation";
import * as Sentry from "@sentry/nextjs";
import { useGetV2DownloadAgentFile } from "@/app/api/__generated__/endpoints/store/store";
import { useOnboarding } from "@/providers/onboarding/onboarding-provider";
import { analytics } from "@/services/analytics";
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
interface UseAgentInfoProps {
storeListingVersionId: string;
@@ -16,29 +17,9 @@ export const useAgentInfo = ({ storeListingVersionId }: UseAgentInfoProps) => {
const { completeStep } = useOnboarding();
const {
mutate: addMarketplaceAgentToLibrary,
mutateAsync: addMarketplaceAgentToLibrary,
isPending: isAddingAgentToLibrary,
} = usePostV2AddMarketplaceAgent({
mutation: {
onSuccess: ({ data }) => {
completeStep("MARKETPLACE_ADD_AGENT");
router.push(`/library/agents/${(data as LibraryAgent).id}`);
toast({
title: "Agent Added",
description: "Redirecting to your library...",
duration: 2000,
});
},
onError: (error) => {
Sentry.captureException(error);
toast({
title: "Error",
description: "Failed to add agent to library. Please try again.",
variant: "destructive",
});
},
},
});
} = usePostV2AddMarketplaceAgent();
const { refetch: downloadAgent, isFetching: isDownloadingAgent } =
useGetV2DownloadAgentFile(storeListingVersionId, {
@@ -50,13 +31,46 @@ export const useAgentInfo = ({ storeListingVersionId }: UseAgentInfoProps) => {
},
});
const handleLibraryAction = async () => {
addMarketplaceAgentToLibrary({
data: { store_listing_version_id: storeListingVersionId },
});
const handleLibraryAction = async ({
isAddingAgentFirstTime,
}: {
isAddingAgentFirstTime: boolean;
}) => {
try {
const { data: response } = await addMarketplaceAgentToLibrary({
data: { store_listing_version_id: storeListingVersionId },
});
const data = response as LibraryAgent;
if (isAddingAgentFirstTime) {
completeStep("MARKETPLACE_ADD_AGENT");
analytics.sendDatafastEvent("add_to_library", {
name: data.name,
id: data.id,
});
}
router.push(`/library/agents/${data.id}`);
toast({
title: "Agent Added",
description: "Redirecting to your library...",
duration: 2000,
});
} catch (error) {
Sentry.captureException(error);
toast({
title: "Error",
description: "Failed to add agent to library. Please try again.",
variant: "destructive",
});
}
};
const handleDownload = async () => {
const handleDownload = async (agentId: string, agentName: string) => {
try {
const { data: file } = await downloadAgent();
@@ -74,6 +88,11 @@ export const useAgentInfo = ({ storeListingVersionId }: UseAgentInfoProps) => {
window.URL.revokeObjectURL(url);
analytics.sendDatafastEvent("download_agent", {
name: agentName,
id: agentId,
});
toast({
title: "Download Complete",
description: "Your agent has been successfully downloaded.",

View File

@@ -82,6 +82,7 @@ export const MainAgentPage = ({ params }: MainAgentPageProps) => {
<div className="w-full md:w-auto md:shrink-0">
<AgentInfo
user={user}
agentId={agent.active_version_id ?? ""}
name={agent.agent_name}
creator={agent.creator}
shortDescription={agent.sub_heading}
@@ -92,7 +93,7 @@ export const MainAgentPage = ({ params }: MainAgentPageProps) => {
lastUpdated={agent.last_updated.toISOString()}
version={agent.versions[agent.versions.length - 1]}
storeListingVersionId={agent.store_listing_version_id}
libraryAgent={libraryAgent}
isAgentAddedToLibrary={Boolean(libraryAgent)}
/>
</div>
<AgentImages

View File

@@ -6,14 +6,12 @@ import "./globals.css";
import { Providers } from "@/app/providers";
import TallyPopupSimple from "@/components/molecules/TallyPoup/TallyPopup";
import { GoogleAnalytics } from "@/services/analytics/google-analytics";
import { Toaster } from "@/components/molecules/Toast/toaster";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { SpeedInsights } from "@vercel/speed-insights/next";
import { Analytics } from "@vercel/analytics/next";
import Script from "next/script";
import { environment } from "@/services/environment";
import { headers } from "next/headers";
import { SetupAnalytics } from "@/services/analytics";
export const metadata: Metadata = {
title: "AutoGPT Platform",
@@ -27,7 +25,6 @@ export default async function RootLayout({
}>) {
const headersList = await headers();
const host = headersList.get("host") || "";
const withAnalytics = environment.areAnalyticsEnabled(host);
return (
<html
@@ -36,17 +33,12 @@ export default async function RootLayout({
suppressHydrationWarning
>
<head>
<GoogleAnalytics
gaId={process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID || "G-FH2XK2W4GN"} // This is the measurement Id for the Google Analytics dev project
<SetupAnalytics
host={host}
ga={{
gaId: process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID || "G-FH2XK2W4GN",
}}
/>
{withAnalytics ? (
<Script
strategy="afterInteractive"
data-website-id="dfid_g5wtBIiHUwSkWKcGz80lu"
data-domain="agpt.co"
src="https://datafa.st/js/script.js"
/>
) : null}
</head>
<body>
<Providers

View File

@@ -1,248 +0,0 @@
"use client";
import { StarRatingIcons } from "@/components/__legacy__/ui/icons";
import { Separator } from "@/components/__legacy__/ui/separator";
import BackendAPI, { LibraryAgent } from "@/lib/autogpt-server-api";
import { useRouter } from "next/navigation";
import Link from "next/link";
import { useToast } from "@/components/molecules/Toast/use-toast";
import { useOnboarding } from "@/providers/onboarding/onboarding-provider";
import { User } from "@supabase/supabase-js";
import { cn } from "@/lib/utils";
import { FC, useCallback, useMemo, useState } from "react";
interface AgentInfoProps {
user: User | null;
name: string;
creator: string;
shortDescription: string;
longDescription: string;
rating: number;
runs: number;
categories: string[];
lastUpdated: string;
version: string;
storeListingVersionId: string;
libraryAgent: LibraryAgent | null;
}
export const AgentInfo: FC<AgentInfoProps> = ({
user,
name,
creator,
shortDescription,
longDescription,
rating,
runs,
categories,
lastUpdated,
version,
storeListingVersionId,
libraryAgent,
}) => {
const router = useRouter();
const api = useMemo(() => new BackendAPI(), []);
const { toast } = useToast();
const { completeStep } = useOnboarding();
const [adding, setAdding] = useState(false);
const [downloading, setDownloading] = useState(false);
const libraryAction = useCallback(async () => {
setAdding(true);
if (libraryAgent) {
toast({
description: "Redirecting to your library...",
duration: 2000,
});
// Redirect to the library agent page
router.push(`/library/agents/${libraryAgent.id}`);
return;
}
try {
const newLibraryAgent = await api.addMarketplaceAgentToLibrary(
storeListingVersionId,
);
completeStep("MARKETPLACE_ADD_AGENT");
router.push(`/library/agents/${newLibraryAgent.id}`);
toast({
title: "Agent Added",
description: "Redirecting to your library...",
duration: 2000,
});
} catch (error) {
console.error("Failed to add agent to library:", error);
toast({
title: "Error",
description: "Failed to add agent to library. Please try again.",
variant: "destructive",
});
}
}, [toast, api, storeListingVersionId, completeStep, router]);
const handleDownload = useCallback(async () => {
const downloadAgent = async (): Promise<void> => {
setDownloading(true);
try {
const file = await api.downloadStoreAgent(storeListingVersionId);
// Similar to Marketplace v1
const jsonData = JSON.stringify(file, null, 2);
// Create a Blob from the file content
const blob = new Blob([jsonData], { type: "application/json" });
// Create a temporary URL for the Blob
const url = window.URL.createObjectURL(blob);
// Create a temporary anchor element
const a = document.createElement("a");
a.href = url;
a.download = `agent_${storeListingVersionId}.json`; // Set the filename
// Append the anchor to the body, click it, and remove it
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
// Revoke the temporary URL
window.URL.revokeObjectURL(url);
toast({
title: "Download Complete",
description: "Your agent has been successfully downloaded.",
});
} catch (error) {
console.error(`Error downloading agent:`, error);
toast({
title: "Error",
description: "Failed to download agent. Please try again.",
variant: "destructive",
});
}
};
await downloadAgent();
setDownloading(false);
}, [setDownloading, api, storeListingVersionId, toast]);
return (
<div className="w-full max-w-[396px] px-4 sm:px-6 lg:w-[396px] lg:px-0">
{/* Title */}
<div
data-testid="agent-title"
className="mb-3 w-full font-poppins text-2xl font-medium leading-normal text-neutral-900 dark:text-neutral-100 sm:text-3xl lg:mb-4 lg:text-[35px] lg:leading-10"
>
{name}
</div>
{/* Creator */}
<div className="mb-3 flex w-full items-center gap-1.5 lg:mb-4">
<div className="text-base font-normal text-neutral-800 dark:text-neutral-200 sm:text-lg lg:text-xl">
by
</div>
<Link
data-testid={"agent-creator"}
href={`/marketplace/creator/${encodeURIComponent(creator)}`}
className="text-base font-medium text-neutral-800 hover:underline dark:text-neutral-200 sm:text-lg lg:text-xl"
>
{creator}
</Link>
</div>
{/* Short Description */}
<div className="mb-4 line-clamp-2 w-full text-base font-normal leading-normal text-neutral-600 dark:text-neutral-300 sm:text-lg lg:mb-6 lg:text-xl lg:leading-7">
{shortDescription}
</div>
{/* Buttons */}
<div className="mb-4 flex w-full gap-3 lg:mb-[60px]">
{user && (
<button
className={cn(
"inline-flex min-w-24 items-center justify-center rounded-full bg-violet-600 px-4 py-3",
"transition-colors duration-200 hover:bg-violet-500 disabled:bg-zinc-400",
)}
data-testid={"agent-add-library-button"}
onClick={libraryAction}
disabled={adding}
>
<span className="justify-start font-sans text-sm font-medium leading-snug text-primary-foreground">
{libraryAgent ? "See runs" : "Add to library"}
</span>
</button>
)}
<button
className={cn(
"inline-flex min-w-24 items-center justify-center rounded-full bg-zinc-200 px-4 py-3",
"transition-colors duration-200 hover:bg-zinc-200/70 disabled:bg-zinc-200/40",
)}
data-testid={"agent-download-button"}
onClick={handleDownload}
disabled={downloading}
>
<div className="justify-start text-center font-sans text-sm font-medium leading-snug text-zinc-800">
Download agent
</div>
</button>
</div>
{/* Rating and Runs */}
<div className="mb-4 flex w-full items-center justify-between lg:mb-[44px]">
<div className="flex items-center gap-1.5 sm:gap-2">
<span className="whitespace-nowrap text-base font-semibold text-neutral-800 dark:text-neutral-200 sm:text-lg">
{rating.toFixed(1)}
</span>
<div className="flex gap-0.5">{StarRatingIcons(rating)}</div>
</div>
<div className="whitespace-nowrap text-base font-semibold text-neutral-800 dark:text-neutral-200 sm:text-lg">
{runs.toLocaleString()} runs
</div>
</div>
{/* Separator */}
<Separator className="mb-4 lg:mb-[44px]" />
{/* Description Section */}
<div className="mb-4 w-full lg:mb-[36px]">
<div className="decoration-skip-ink-none mb-1.5 text-base font-medium leading-6 text-neutral-800 dark:text-neutral-200 sm:mb-2">
Description
</div>
<div
data-testid={"agent-description"}
className="whitespace-pre-line text-base font-normal leading-6 text-neutral-600 dark:text-neutral-400"
>
{longDescription}
</div>
</div>
{/* Categories */}
<div className="mb-4 flex w-full flex-col gap-1.5 sm:gap-2 lg:mb-[36px]">
<div className="decoration-skip-ink-none mb-1.5 text-base font-medium leading-6 text-neutral-800 dark:text-neutral-200 sm:mb-2">
Categories
</div>
<div className="flex flex-wrap gap-1.5 sm:gap-2">
{categories.map((category, index) => (
<div
key={index}
className="decoration-skip-ink-none whitespace-nowrap rounded-full border border-neutral-600 bg-white px-2 py-0.5 text-base font-normal leading-6 text-neutral-800 underline-offset-[from-font] dark:border-neutral-700 dark:bg-neutral-800 dark:text-neutral-200 sm:px-[16px] sm:py-[10px]"
>
{category}
</div>
))}
</div>
</div>
{/* Version History */}
<div className="flex w-full flex-col gap-0.5 sm:gap-1">
<div className="decoration-skip-ink-none mb-1.5 text-base font-medium leading-6 text-neutral-800 dark:text-neutral-200 sm:mb-2">
Version history
</div>
<div className="decoration-skip-ink-none text-base font-normal leading-6 text-neutral-600 underline-offset-[from-font] dark:text-neutral-400">
Last updated {lastUpdated}
</div>
<div className="text-xs text-neutral-600 dark:text-neutral-400 sm:text-sm">
Version {version}
</div>
</div>
</div>
);
};

View File

@@ -1,68 +0,0 @@
/**
* Modified copy of ga.tsx from @next/third-parties/google, with modified gtag.js source URL.
* Original source file: https://github.com/vercel/next.js/blob/b304b45e3a6e3e79338568d76e28805e77c03ec9/packages/third-parties/src/google/ga.tsx
*/
"use client";
import type { GAParams } from "@/types/google";
import Script from "next/script";
import { useEffect } from "react";
let currDataLayerName: string | undefined = undefined;
export function GoogleAnalytics(props: GAParams) {
const { gaId, debugMode, dataLayerName = "dataLayer", nonce } = props;
if (currDataLayerName === undefined) {
currDataLayerName = dataLayerName;
}
useEffect(() => {
// Feature usage signal (same as original implementation)
performance.mark("mark_feature_usage", {
detail: {
feature: "custom-ga",
},
});
}, []);
return (
<>
<Script
id="_custom-ga-init"
// Using "afterInteractive" to avoid blocking the initial page rendering
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
window['${dataLayerName}'] = window['${dataLayerName}'] || [];
function gtag(){window['${dataLayerName}'].push(arguments);}
gtag('js', new Date());
gtag('config', '${gaId}' ${debugMode ? ",{ 'debug_mode': true }" : ""});
`,
}}
nonce={nonce}
/>
<Script
id="_custom-ga"
strategy="afterInteractive"
src="/gtag.js"
nonce={nonce}
/>
</>
);
}
export function sendGAEvent(...args: any[]) {
if (currDataLayerName === undefined) {
console.warn(`Custom GA: GA has not been initialized`);
return;
}
const dataLayer = (window as any)[currDataLayerName];
if (dataLayer) {
dataLayer.push(...args);
} else {
console.warn(`Custom GA: dataLayer ${currDataLayerName} does not exist`);
}
}

View File

@@ -0,0 +1,114 @@
/**
* Modified copy of ga.tsx from @next/third-parties/google, with modified gtag.js source URL.
* Original source file: https://github.com/vercel/next.js/blob/b304b45e3a6e3e79338568d76e28805e77c03ec9/packages/third-parties/src/google/ga.tsx
*/
"use client";
import type { GAParams } from "@/types/google";
import Script from "next/script";
import { useEffect } from "react";
import { environment } from "../environment";
declare global {
interface Window {
datafast: (name: string, metadata: Record<string, unknown>) => void;
[key: string]: unknown[] | ((...args: unknown[]) => void) | unknown;
}
}
let currDataLayerName: string | undefined = undefined;
type SetupProps = {
ga: GAParams;
host: string;
};
export function SetupAnalytics(props: SetupProps) {
const { ga, host } = props;
const { gaId, debugMode, dataLayerName = "dataLayer", nonce } = ga;
const isProductionDomain = host.includes("platform.agpt.co");
// Datafa.st journey analytics only on production
const dataFastEnabled = isProductionDomain;
// We collect analytics too for open source developers running the platform locally
const googleAnalyticsEnabled = environment.isLocal() || isProductionDomain;
if (currDataLayerName === undefined) {
currDataLayerName = dataLayerName;
}
useEffect(() => {
if (!googleAnalyticsEnabled) return;
// Google Analytics: feature usage signal (same as original implementation)
performance.mark("mark_feature_usage", {
detail: {
feature: "custom-ga",
},
});
}, [googleAnalyticsEnabled]);
return (
<>
{/* Google Analytics */}
{googleAnalyticsEnabled ? (
<>
<Script
id="_custom-ga-init"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
window['${dataLayerName}'] = window['${dataLayerName}'] || [];
function gtag(){window['${dataLayerName}'].push(arguments);}
gtag('js', new Date());
gtag('config', '${gaId}' ${debugMode ? ",{ 'debug_mode': true }" : ""});
`,
}}
nonce={nonce}
/>
{/* Google Tag Manager */}
<Script
id="_custom-ga"
strategy="afterInteractive"
src="/gtag.js"
nonce={nonce}
/>
</>
) : null}
{/* Datafa.st */}
{dataFastEnabled ? (
<Script
strategy="afterInteractive"
data-website-id="dfid_g5wtBIiHUwSkWKcGz80lu"
data-domain="agpt.co"
src="https://datafa.st/js/script.js"
/>
) : null}
</>
);
}
export const analytics = {
sendGAEvent,
sendDatafastEvent,
};
function sendGAEvent(...args: unknown[]) {
if (typeof window === "undefined") return;
if (currDataLayerName === undefined) return;
const dataLayer = window[currDataLayerName];
if (!dataLayer) return;
if (Array.isArray(dataLayer)) {
dataLayer.push(...args);
} else {
console.warn(`Custom GA: dataLayer ${currDataLayerName} does not exist`);
}
}
function sendDatafastEvent(name: string, metadata: Record<string, unknown>) {
if (typeof window === "undefined" || !window.datafast) return;
window.datafast(name, metadata);
}

View File

@@ -92,10 +92,6 @@ function areFeatureFlagsEnabled() {
return process.env.NEXT_PUBLIC_LAUNCHDARKLY_ENABLED === "enabled";
}
function areAnalyticsEnabled(host: string) {
return host.includes("platform.agpt.co");
}
export const environment = {
// Generic
getEnvironmentStr,
@@ -115,6 +111,5 @@ export const environment = {
isCloud,
isLocal,
isCAPTCHAEnabled,
areAnalyticsEnabled,
areFeatureFlagsEnabled,
};