diff --git a/src/app/actions.ts b/src/app/actions.ts index 2738f9c..bc42312 100644 --- a/src/app/actions.ts +++ b/src/app/actions.ts @@ -32,6 +32,10 @@ export interface DetectedSystem { name: string; } +export interface DetectedTracker { + name: string; +} + export async function extractMetaData(url: string, keyword?: string) { if (!url) { return { error: "URL is required." }; @@ -229,8 +233,8 @@ export async function extractMetaData(url: string, keyword?: string) { links.push({ href: absoluteUrl, text, type, rel }); }); - const detectedSystems: DetectedSystem[] = []; const htmlContent = $.html(); + const detectedSystems: DetectedSystem[] = []; const uniqueSystems = new Set(); // WordPress @@ -283,6 +287,70 @@ export async function extractMetaData(url: string, keyword?: string) { detectedSystems.push({ name: system }); }); + const detectedTrackers: DetectedTracker[] = []; + const uniqueTrackers = new Set(); + + // Google Analytics / Tag Manager + if ( + htmlContent.includes("googletagmanager.com/gtag/js") || + htmlContent.includes("google-analytics.com/analytics.js") + ) { + uniqueTrackers.add("Google Analytics"); + } + if (htmlContent.includes("googletagmanager.com/gtm.js")) { + uniqueTrackers.add("Google Tag Manager"); + } + + // Facebook Pixel + if ( + htmlContent.includes("connect.facebook.net") || + htmlContent.includes("fbq('init'") + ) { + uniqueTrackers.add("Facebook Pixel"); + } + + // Hotjar + if ( + htmlContent.includes("static.hotjar.com") || + htmlContent.includes("window.hj=window.hj||function()") + ) { + uniqueTrackers.add("Hotjar"); + } + + // HubSpot + if (htmlContent.includes("js.hs-scripts.com")) { + uniqueTrackers.add("HubSpot"); + } + + // Segment + if (htmlContent.includes("cdn.segment.com")) { + uniqueTrackers.add("Segment"); + } + + // Mixpanel + if (htmlContent.includes("cdn.mxpnl.com")) { + uniqueTrackers.add("Mixpanel"); + } + + // Vercel Analytics + if (htmlContent.includes("/_vercel/insights/")) { + uniqueTrackers.add("Vercel Analytics"); + } + + // Plausible + if (htmlContent.includes("plausible.io/js/")) { + uniqueTrackers.add("Plausible"); + } + + // Microsoft Clarity + if (htmlContent.includes("clarity.ms/tag/")) { + uniqueTrackers.add("Microsoft Clarity"); + } + + uniqueTrackers.forEach((tracker) => { + detectedTrackers.push({ name: tracker }); + }); + return { data: { title, @@ -300,6 +368,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, + trackers: detectedTrackers.length > 0 ? detectedTrackers : null, }, }; } catch (error) { diff --git a/src/components/meta-form.tsx b/src/components/meta-form.tsx index 58e77f1..eaa5806 100644 --- a/src/components/meta-form.tsx +++ b/src/components/meta-form.tsx @@ -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.trackers && metaData.trackers.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 )} + {metaData.trackers && metaData.trackers.length > 0 && ( + + {tabColors && } + Tracking + + )} {metaData.faq && metaData.faq.length > 0 && ( {tabColors && } @@ -331,6 +343,12 @@ export function MetaForm() { )} + + {metaData.trackers && metaData.trackers.length > 0 && ( + + + + )} )} diff --git a/src/components/tracking-display.tsx b/src/components/tracking-display.tsx new file mode 100644 index 0000000..8c401d7 --- /dev/null +++ b/src/components/tracking-display.tsx @@ -0,0 +1,122 @@ +"use client"; + +import type { DetectedTracker } from "@/app/actions"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { + BarChart3, + MousePointerClick, + Target, + Users, + Database, + Code, + Eye, +} from "lucide-react"; + +interface TrackingDisplayProps { + trackers: DetectedTracker[]; +} + +const trackerInfo: { + [key: string]: { icon: React.ElementType; description: string }; +} = { + "Google Analytics": { + icon: BarChart3, + description: "Web analytics service that tracks and reports website traffic.", + }, + "Google Tag Manager": { + icon: Code, + description: + "Tag management system to easily deploy and manage marketing tags.", + }, + "Facebook Pixel": { + icon: Target, + description: + "Analytics tool to measure the effectiveness of advertising by understanding actions people take on a website.", + }, + Hotjar: { + icon: MousePointerClick, + description: + "Behavior analytics tool that reveals the online behavior and voice of users.", + }, + HubSpot: { + icon: Users, + description: "Inbound marketing, sales, and service software.", + }, + Segment: { + icon: Database, + description: + "Customer data platform that collects, cleans, and controls customer data.", + }, + Mixpanel: { + icon: BarChart3, + description: + "Product analytics tool for tracking user interactions with web and mobile applications.", + }, + "Vercel Analytics": { + icon: BarChart3, + description: "Privacy-friendly analytics for websites deployed on Vercel.", + }, + Plausible: { + icon: BarChart3, + description: + "A simple, lightweight and privacy-friendly web analytics tool.", + }, + "Microsoft Clarity": { + icon: Eye, + description: + "A free user behavior analytics tool that helps you understand how users are interacting with your website.", + }, + Default: { + icon: Code, + description: "A detected tracking or analytics tool.", + }, +}; + +export function TrackingDisplay({ trackers }: TrackingDisplayProps) { + return ( + + + Tracking Analysis + + Tracking and analytics tools detected on the page. + + + + {trackers && trackers.length > 0 ? ( +
+ {trackers.map((tracker, index) => { + const info = trackerInfo[tracker.name] || trackerInfo["Default"]; + const Icon = info.icon; + return ( +
+
+ +
+
+

{tracker.name}

+

+ {info.description} +

+
+
+ ); + })} +
+ ) : ( +
+

No specific tracking tools detected.

+
+ )} +
+
+ ); +} \ No newline at end of file diff --git a/src/lib/types.ts b/src/lib/types.ts index 2495fb3..e1a7312 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -3,6 +3,7 @@ import type { ImageAltData, LinkData, DetectedSystem, + DetectedTracker, } from "@/app/actions"; export interface OpenGraphData { @@ -39,4 +40,5 @@ export interface MetaData { twitter?: TwitterData; links?: LinkData[] | null; systems?: DetectedSystem[] | null; + trackers?: DetectedTracker[] | null; } \ No newline at end of file