[dyad] Add tracking detection tab - wrote 4 file(s)

This commit is contained in:
[dyad]
2026-01-21 08:24:10 +01:00
parent a8d95d6879
commit 8a615c11f8
4 changed files with 150 additions and 18 deletions

View File

@@ -32,6 +32,11 @@ export interface DetectedSystem {
name: string;
}
export interface TrackingTool {
name: string;
id: string | null;
}
export async function extractMetaData(url: string, keyword?: string) {
if (!url) {
return { error: "URL is required." };
@@ -229,11 +234,10 @@ export async function extractMetaData(url: string, keyword?: string) {
links.push({ href: absoluteUrl, text, type, rel });
});
const detectedSystems: DetectedSystem[] = [];
const htmlContent = $.html();
const uniqueSystems = new Set<string>();
// WordPress
// System Detection
if (
$('meta[name="generator"][content*="WordPress"]').length > 0 ||
htmlContent.includes("/wp-content/") ||
@@ -241,47 +245,54 @@ export async function extractMetaData(url: string, keyword?: string) {
) {
uniqueSystems.add("WordPress");
}
// Shopify
if (
htmlContent.includes("cdn.shopify.com") ||
htmlContent.includes("Shopify.theme")
) {
uniqueSystems.add("Shopify");
}
// Next.js
if ($("#__next").length > 0) {
uniqueSystems.add("Next.js");
uniqueSystems.add("React"); // Next.js uses React
uniqueSystems.add("React");
}
// React (generic)
if ($("#root").length > 0) {
uniqueSystems.add("React");
}
// Webflow
if (
$('meta[name="generator"][content="Webflow"]').length > 0 ||
htmlContent.includes("<!-- This site was created in Webflow.")
) {
uniqueSystems.add("Webflow");
}
// Wix
if ($('meta[name="generator"][content*="Wix.com"]').length > 0) {
uniqueSystems.add("Wix");
}
// Squarespace
if (htmlContent.includes("static1.squarespace.com")) {
uniqueSystems.add("Squarespace");
}
const detectedSystems: DetectedSystem[] = Array.from(uniqueSystems).map(
(name) => ({ name })
);
uniqueSystems.forEach((system) => {
detectedSystems.push({ name: system });
});
// Tracking Detection
const uniqueTrackers = new Map<string, string | null>();
let gtmMatch = htmlContent.match(/['"](GTM-[A-Z0-9]+)['"]/);
if (gtmMatch) uniqueTrackers.set("Google Tag Manager", gtmMatch[1]);
let ga4Match = htmlContent.match(/['"](G-[A-Z0-9]+)['"]/);
if (ga4Match) uniqueTrackers.set("Google Analytics (GA4)", ga4Match[1]);
let uaMatch = htmlContent.match(/['"](UA-\d{4,9}-\d{1,4})['"]/);
if (uaMatch)
uniqueTrackers.set("Google Analytics (Universal)", uaMatch[1]);
let fbPixelMatch = htmlContent.match(/fbq\(['"]init['"], ['"](\d+)['"]\)/);
if (fbPixelMatch) uniqueTrackers.set("Facebook Pixel", fbPixelMatch[1]);
let hotjarMatch = htmlContent.match(/hjid:(\d+)/);
if (hotjarMatch) uniqueTrackers.set("Hotjar", hotjarMatch[1]);
let hubspotMatch = htmlContent.match(/js\.hs-scripts\.com\/(\d+)\.js/);
if (hubspotMatch) uniqueTrackers.set("HubSpot", hubspotMatch[1]);
const detectedTracking: TrackingTool[] = Array.from(
uniqueTrackers,
([name, id]) => ({ name, id })
);
return {
data: {
@@ -300,6 +311,7 @@ export async function extractMetaData(url: string, keyword?: string) {
images: imageAltData.length > 0 ? imageAltData : null,
links: links.length > 0 ? links : null,
systems: detectedSystems.length > 0 ? detectedSystems : null,
tracking: detectedTracking.length > 0 ? detectedTracking : null,
},
};
} catch (error) {

View File

@@ -16,6 +16,7 @@ import { AnalysisTab } from "./analysis-tab";
import { SocialTab } from "./social-tab";
import { LinksDisplay } from "./links-display";
import { SystemDisplay } from "./system-display";
import { TrackingDisplay } from "./tracking-display";
import type { MetaData } from "@/lib/types";
export function MetaForm() {
@@ -130,6 +131,10 @@ export function MetaForm() {
const systemColor: IndicatorColor =
metaData.systems && metaData.systems.length > 0 ? "green" : "gray";
// Tracking Tab
const trackingColor: IndicatorColor =
metaData.tracking && metaData.tracking.length > 0 ? "green" : "gray";
return {
analysis: analysisColor,
headlines: headlinesColor,
@@ -139,6 +144,7 @@ export function MetaForm() {
faq: faqColor,
schema: schemaColor,
system: systemColor,
tracking: trackingColor,
};
}, [metaData, editableTitle, editableDescription]);
@@ -236,6 +242,12 @@ export function MetaForm() {
System
</TabsTrigger>
)}
{metaData.tracking && metaData.tracking.length > 0 && (
<TabsTrigger value="tracking">
{tabColors && <TabIndicator color={tabColors.tracking} />}
Tracking
</TabsTrigger>
)}
{metaData.faq && metaData.faq.length > 0 && (
<TabsTrigger value="faq">
{tabColors && <TabIndicator color={tabColors.faq} />}
@@ -331,6 +343,12 @@ export function MetaForm() {
<SystemDisplay systems={metaData.systems} />
</TabsContent>
)}
{metaData.tracking && metaData.tracking.length > 0 && (
<TabsContent value="tracking">
<TrackingDisplay tools={metaData.tracking} />
</TabsContent>
)}
</Tabs>
)}
</div>

View File

@@ -0,0 +1,100 @@
"use client";
import type { TrackingTool } from "@/app/actions";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { CopyButton } from "./copy-button";
import { BarChart, Tag, Facebook, Activity, Wind } from "lucide-react";
interface TrackingDisplayProps {
tools: TrackingTool[];
}
const toolInfo: {
[key: string]: { icon: React.ElementType; description: string };
} = {
"Google Tag Manager": {
icon: Tag,
description: "A tag management system to deploy and update marketing tags.",
},
"Google Analytics (GA4)": {
icon: BarChart,
description: "The latest version of Google's web analytics service.",
},
"Google Analytics (Universal)": {
icon: BarChart,
description: "The previous generation of Google Analytics.",
},
"Facebook Pixel": {
icon: Facebook,
description: "An analytics tool to measure the effectiveness of advertising.",
},
Hotjar: {
icon: Wind,
description: "A tool for behavior analytics and user feedback.",
},
HubSpot: {
icon: Activity,
description: "An inbound marketing, sales, and service software.",
},
Default: {
icon: Activity,
description: "A detected tracking or analytics tool.",
},
};
export function TrackingDisplay({ tools }: TrackingDisplayProps) {
return (
<Card className="w-full shadow-lg rounded-lg">
<CardHeader>
<CardTitle>Tracking Analysis</CardTitle>
<CardDescription>
External analytics and marketing tools detected on the page.
</CardDescription>
</CardHeader>
<CardContent>
{tools && tools.length > 0 ? (
<div className="space-y-4">
{tools.map((tool, index) => {
const info = toolInfo[tool.name] || toolInfo["Default"];
const Icon = info.icon;
return (
<div
key={index}
className="flex items-start gap-4 p-4 border rounded-lg bg-muted/50"
>
<div className="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg bg-primary text-primary-foreground">
<Icon className="h-6 w-6" />
</div>
<div className="flex-grow">
<h4 className="font-semibold text-lg">{tool.name}</h4>
<p className="text-sm text-muted-foreground">
{info.description}
</p>
{tool.id && (
<div className="mt-2 flex items-center gap-2 p-2 bg-background rounded-md">
<span className="text-sm font-mono text-muted-foreground flex-grow break-all">
{tool.id}
</span>
<CopyButton textToCopy={tool.id} />
</div>
)}
</div>
</div>
);
})}
</div>
) : (
<div className="text-center py-8 text-muted-foreground">
<p>No external tracking tools detected.</p>
</div>
)}
</CardContent>
</Card>
);
}

View File

@@ -3,6 +3,7 @@ import type {
ImageAltData,
LinkData,
DetectedSystem,
TrackingTool,
} from "@/app/actions";
export interface OpenGraphData {
@@ -39,4 +40,5 @@ export interface MetaData {
twitter?: TwitterData;
links?: LinkData[] | null;
systems?: DetectedSystem[] | null;
tracking?: TrackingTool[] | null;
}