@mosierdata/emdash-plugin-analytics 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"admin.mjs","names":[],"sources":["../src/admin/Dashboard.tsx","../src/admin/Settings.tsx","../src/admin/TrackingSettings.tsx","../src/admin.tsx"],"sourcesContent":["import React, { useEffect, useState } from 'react';\nimport { usePluginAPI } from '@emdash-cms/admin';\nimport type { LicenseData } from '../types';\n\nconst DASHBOARD_BASE_URL = 'https://my.roiknowledge.com/embed';\n\nexport function AdminDashboard() {\n const api = usePluginAPI();\n const [dashboardUrl, setDashboardUrl] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n void (async () => {\n try {\n const license = await api.get<LicenseData>('license/status');\n\n if (!license.isValid) {\n setError('No active license. Configure your license key in Settings, then click Activate.');\n return;\n }\n if (license.isFallback) {\n setError('Dashboard unavailable — could not reach the MosierData backend. Tracking scripts are still active.');\n return;\n }\n if (!license.sessionToken) {\n setError('Session token missing. Re-activate your license in Settings.');\n return;\n }\n\n const url = new URL(DASHBOARD_BASE_URL);\n url.searchParams.set('token', license.sessionToken);\n url.searchParams.set('tier', license.tier ?? 'free');\n setDashboardUrl(url.toString());\n } catch {\n setError('Failed to load license status.');\n }\n })();\n }, [api]);\n\n if (error) {\n return (\n <div style={{ padding: '2rem', color: '#555' }}>\n <h3>ROI Insights Dashboard</h3>\n <p>{error}</p>\n <a href=\"../settings\">Go to Settings →</a>\n </div>\n );\n }\n\n if (!dashboardUrl) {\n return <div style={{ padding: '2rem', color: '#888' }}>Loading ROI Insights Dashboard…</div>;\n }\n\n return (\n <div style={{ width: '100%', height: 'calc(100vh - 64px)', overflow: 'hidden' }}>\n <iframe\n src={dashboardUrl}\n style={{ width: '100%', height: '100%', border: 'none', display: 'block' }}\n title=\"ROI Insights Dashboard\"\n sandbox=\"allow-scripts allow-same-origin allow-forms allow-popups\"\n />\n </div>\n );\n}\n","import React, { useState, useEffect, useCallback } from 'react';\nimport { usePluginAPI } from '@emdash-cms/admin';\nimport type { LicenseData } from '../types';\n\nexport function SettingsPage() {\n const api = usePluginAPI();\n const [license, setLicense] = useState<LicenseData | null>(null);\n const [googleConnected, setGoogleConnected] = useState(false);\n const [activating, setActivating] = useState(false);\n const [message, setMessage] = useState('');\n\n // Load current license status and Google connection state on mount\n useEffect(() => {\n void Promise.all([\n api.get<LicenseData>('license/status').then(setLicense),\n api.get<{ connected: boolean }>('google-oauth/status').then(r => setGoogleConnected(r.connected))\n ]);\n }, [api]);\n\n // Handle OAuth redirect callback — persist the connected flag via route\n useEffect(() => {\n const params = new URLSearchParams(window.location.search);\n if (params.get('oauth_callback') !== '1' || params.get('google_connected') !== 'true') return;\n\n void api.post<{ connected: boolean }>('google-oauth/connected', {}).then(() => {\n setGoogleConnected(true);\n setMessage('Google Services connected successfully.');\n });\n }, [api]);\n\n const handleActivateLicense = useCallback(async () => {\n setActivating(true);\n setMessage('');\n try {\n const fresh = await api.get<LicenseData>('license/validate');\n setLicense(fresh);\n setMessage(fresh.isValid ? 'License activated.' : `License invalid: ${fresh.reason ?? 'unknown'}`);\n } catch {\n setMessage('Error validating license.');\n } finally {\n setActivating(false);\n }\n }, [api]);\n\n const handleGoogleOAuth = useCallback(async () => {\n try {\n const result = await api.post<{ authUrl?: string; error?: string }>('google-oauth/initiate', {});\n if (result.error) { setMessage(result.error); return; }\n if (result.authUrl) window.location.href = result.authUrl;\n } catch {\n setMessage('Error initiating Google connection.');\n }\n }, [api]);\n\n const licenseStatusLabel = (): string => {\n if (!license) return 'Not checked';\n if (license.isFallback) return 'Active (offline)';\n if (!license.isValid) return `Invalid — ${license.reason ?? 'unknown'}`;\n return `Active — ${license.tier ?? 'free'} tier`;\n };\n\n return (\n <div style={{ maxWidth: 640, padding: '1.5rem' }}>\n <h2>License &amp; Google</h2>\n\n <p style={{ fontSize: '0.9rem', color: '#555', marginBottom: '1.5rem' }}>\n Enter your license key in the <strong>Settings</strong> tab above, then click Activate below.\n </p>\n\n <section style={{ marginBottom: '1.5rem' }}>\n <h3>License</h3>\n {license && (\n <p style={{ fontSize: '0.9rem', marginBottom: '0.75rem' }}>\n Status: <strong>{licenseStatusLabel()}</strong>\n </p>\n )}\n <button onClick={() => void handleActivateLicense()} disabled={activating}>\n {activating ? 'Activating…' : 'Activate License'}\n </button>\n </section>\n\n <section style={{ marginBottom: '1.5rem' }}>\n <h3>Google Services (Free Tier)</h3>\n {googleConnected ? (\n <p>Connected to Google Analytics &amp; Search Console ✅</p>\n ) : (\n <>\n <p style={{ fontSize: '0.9rem', color: '#555' }}>\n Grant read access to GA4 and Search Console. Tokens are stored securely on the MosierData backend.\n </p>\n <button onClick={() => void handleGoogleOAuth()}>Connect Google Services</button>\n </>\n )}\n </section>\n\n {message && (\n <p style={{ fontSize: '0.9rem', color: '#444', marginTop: '1rem' }}>{message}</p>\n )}\n\n {license?.isValid && license.tier === 'free' && (\n <div style={{ marginTop: '2rem', padding: '1rem', background: '#f0f7ff', border: '1px solid #c0d8f5', borderRadius: 6 }}>\n <h4 style={{ margin: '0 0 0.5rem' }}>Upgrade to Professional</h4>\n <p style={{ margin: '0 0 0.75rem', fontSize: '0.9rem' }}>\n Unlock AI Call Transcription, Lead Scoring, and Call Recording.\n </p>\n <a href=\"https://quotedash.io/upgrade\" target=\"_blank\" rel=\"noreferrer\">Upgrade Now →</a>\n </div>\n )}\n </div>\n );\n}\n","import React, { useState, useEffect, useRef, useCallback } from 'react';\nimport { usePluginAPI } from '@emdash-cms/admin';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface TrackingValues {\n gtmEnabled: boolean;\n gtmId: string;\n ga4Enabled: boolean;\n ga4Id: string;\n metaEnabled: boolean;\n metaId: string;\n linkedinEnabled: boolean;\n linkedinId: string;\n tiktokEnabled: boolean;\n tiktokId: string;\n bingEnabled: boolean;\n bingId: string;\n pinterestEnabled: boolean;\n pinterestId: string;\n nextdoorEnabled: boolean;\n nextdoorId: string;\n}\n\ntype PlatformId =\n | 'gtm'\n | 'ga4'\n | 'meta'\n | 'linkedin'\n | 'tiktok'\n | 'bing'\n | 'pinterest'\n | 'nextdoor';\n\ninterface HelpSection {\n title: string;\n content: React.ReactNode;\n}\n\n// ─── Help Content ─────────────────────────────────────────────────────────────\n\nconst HELP_CONTENT: Record<PlatformId, HelpSection[]> = {\n gtm: [\n {\n title: 'What is this?',\n content: (\n <p style={{ margin: 0 }}>\n Google Tag Manager is a free tool from Google that acts as a central control panel for all the\n tracking scripts on your website. Instead of adding each tracking code (Google Analytics, Meta\n Pixel, LinkedIn, etc.) to your site individually, you put them all inside one GTM \"container\"\n and manage them from a single dashboard.\n </p>\n ),\n },\n {\n title: 'Why would I use it?',\n content: (\n <p style={{ margin: 0 }}>\n If you or your agency already use GTM, this is the way to go. It means you can add, edit, or\n remove any tracking code from the GTM dashboard without ever touching your website again.\n It's also the cleanest option if you're running multiple tracking tools and want precise control\n over when and how each one fires.\n </p>\n ),\n },\n {\n title: 'When should I skip this?',\n content: (\n <p style={{ margin: 0 }}>\n If you don't have a GTM account and you're not sure what this is, you probably don't need it.\n You can enable Google Analytics, Meta Pixel, and LinkedIn directly using the toggles below —\n no GTM required.\n </p>\n ),\n },\n {\n title: \"Important — don't double up\",\n content: (\n <p style={{ margin: 0 }}>\n If you turn on GTM here <strong>and</strong> you also enable Google Analytics, Meta Pixel, or\n LinkedIn directly in this plugin, those scripts will load twice. That means every pageview and\n every conversion gets counted twice, which makes your data unreliable. Pick one approach: manage\n everything through GTM, <strong>or</strong> enable each platform individually below. Not both.\n </p>\n ),\n },\n {\n title: 'How to find your GTM ID',\n content: (\n <ol style={{ margin: 0, paddingLeft: '20px' }}>\n <li>Go to tagmanager.google.com and sign in</li>\n <li>Select your account and container</li>\n <li>Your GTM ID is in the top-right corner of the screen — it looks like <strong>GTM-XXXXXXX</strong></li>\n <li>Paste that ID into the field below and flip the toggle to enable</li>\n </ol>\n ),\n },\n ],\n ga4: [\n {\n title: 'What is this?',\n content: (\n <p style={{ margin: 0 }}>\n Google Analytics 4 is Google's analytics platform. It tracks who visits your website, how they\n found you, what they do while they're there, and whether they take actions that matter to your\n business — like filling out a contact form or calling your phone number.\n </p>\n ),\n },\n {\n title: 'Why would I use it?',\n content: (\n <p style={{ margin: 0 }}>\n If you want to understand your website traffic, this is the foundation. GA4 automatically tracks\n page views, session duration, traffic sources, device types, and geographic location. You can add\n more advanced tracking on top of that (form submissions, button clicks, purchases), but the basics\n work right out of the box.\n </p>\n ),\n },\n {\n title: 'When should I skip this?',\n content: (\n <p style={{ margin: 0 }}>\n Skip this toggle if you're already sending GA4 data through Google Tag Manager. Enabling it here\n and in GTM will count every visitor twice, which inflates all of your numbers. If you're not sure,\n check your GTM container for a tag called \"Google Analytics: GA4 Configuration\" or \"Google Tag\" —\n if it's there, leave this toggle off.\n </p>\n ),\n },\n {\n title: 'How to find your Measurement ID',\n content: (\n <ol style={{ margin: 0, paddingLeft: '20px' }}>\n <li>Go to analytics.google.com and sign in</li>\n <li>Select your property (or create one if you haven't set one up yet)</li>\n <li>Go to <strong>Admin → Data Streams</strong> and click on your web stream</li>\n <li>Your Measurement ID is at the top of the screen — it looks like <strong>G-XXXXXXXXXX</strong></li>\n <li>Paste it into the field below and flip the toggle to enable</li>\n </ol>\n ),\n },\n ],\n meta: [\n {\n title: 'What is this?',\n content: (\n <p style={{ margin: 0 }}>\n The Meta Pixel is a small piece of tracking code from Meta that connects your website to your\n Facebook and Instagram advertising accounts. It's how Meta knows what happens on your site after\n someone clicks one of your ads.\n </p>\n ),\n },\n {\n title: 'Why would I use it?',\n content: (\n <p style={{ margin: 0 }}>\n Without the Pixel installed, Meta's ad platform has no idea what happens after someone clicks your\n ad. It can't tell which ads are producing leads, it can't build retargeting audiences from your\n website visitors, and it can't optimize delivery toward people who are likely to convert. If you're\n running Facebook or Instagram ads — or if you plan to — the Pixel is essential.\n <br /><br />\n Even if you're not running ads yet, there's value in installing it now. The Pixel starts collecting\n visitor data immediately, so by the time you're ready to run your first campaign, Meta already has\n an audience to work with.\n </p>\n ),\n },\n {\n title: 'When should I skip this?',\n content: (\n <p style={{ margin: 0 }}>\n If you already have the Meta Pixel firing through Google Tag Manager, don't enable it here. You'd\n be loading it twice. Check your GTM container for a tag called \"Meta Pixel\" or \"Facebook Pixel\" —\n if it exists, leave this toggle off and manage it in GTM.\n </p>\n ),\n },\n {\n title: 'How to find your Pixel ID',\n content: (\n <ol style={{ margin: 0, paddingLeft: '20px' }}>\n <li>Go to business.facebook.com and open <strong>Events Manager</strong></li>\n <li>Select your Pixel (or create one under <strong>Connect Data Sources → Web</strong>)</li>\n <li>Your Pixel ID is the number at the top of the Pixel detail screen — it looks like <strong>123456789012345</strong></li>\n <li>Paste it into the field below and flip the toggle to enable</li>\n </ol>\n ),\n },\n ],\n linkedin: [\n {\n title: 'What is this?',\n content: (\n <p style={{ margin: 0 }}>\n The LinkedIn Insight Tag is LinkedIn's version of a tracking pixel. It's a lightweight script that\n connects your website to your LinkedIn Campaign Manager account.\n </p>\n ),\n },\n {\n title: 'Why would I use it?',\n content: (\n <p style={{ margin: 0 }}>\n The Insight Tag does two things. First, it powers LinkedIn ad features — retargeting website\n visitors, tracking conversions from LinkedIn ads, and optimizing ad delivery. Second, and this is\n the part most people don't know about, it unlocks LinkedIn's <strong>Website Demographics</strong>{' '}\n report. That report shows you the job titles, industries, company sizes, and seniority levels of\n the LinkedIn members visiting your website. If you sell to other businesses, that report alone is\n worth the install — even if you never run a LinkedIn ad.\n </p>\n ),\n },\n {\n title: 'When should I skip this?',\n content: (\n <p style={{ margin: 0 }}>\n If you already have the LinkedIn Insight Tag firing through Google Tag Manager, don't enable it\n here. Check your GTM container for a tag called \"LinkedIn Insight Tag\" before proceeding.\n </p>\n ),\n },\n {\n title: 'How to find your Partner ID',\n content: (\n <ol style={{ margin: 0, paddingLeft: '20px' }}>\n <li>Go to linkedin.com/campaignmanager and sign in</li>\n <li>Select your ad account</li>\n <li>In the left navigation, go to <strong>Analyze → Insight Tag</strong></li>\n <li>Your Partner ID is the number shown on that page — it looks like <strong>1234567</strong></li>\n <li>Paste it into the field below and flip the toggle to enable</li>\n </ol>\n ),\n },\n ],\n tiktok: [\n {\n title: 'What is this?',\n content: (\n <p style={{ margin: 0 }}>\n The TikTok Pixel is a tracking code from TikTok that connects your website to your TikTok Ads\n Manager account. It works the same way the Meta Pixel does for Facebook and Instagram — it watches\n what happens on your site and reports back to TikTok so the ad platform can do its job.\n </p>\n ),\n },\n {\n title: 'Why would I use it?',\n content: (\n <p style={{ margin: 0 }}>\n If you run or plan to run TikTok ads, this is how TikTok knows whether your ads are actually\n working. Without the Pixel, TikTok can't track conversions, can't build retargeting audiences from\n your website visitors, and can't optimize ad delivery toward people who are likely to become\n customers. You're essentially paying for traffic with no feedback loop.\n <br /><br />\n TikTok's ad platform has gotten significantly better at reaching local service audiences —\n homeowners, people searching for contractors, anyone in a buying mindset. If your customers skew\n under 50 and you're spending on any paid social, TikTok is worth testing. Installing the Pixel now\n means the platform is already collecting data when you're ready to launch your first campaign.\n </p>\n ),\n },\n {\n title: 'When should I skip this?',\n content: (\n <p style={{ margin: 0 }}>\n If you already have the TikTok Pixel firing through Google Tag Manager, don't enable it here.\n Check your GTM container for a tag called \"TikTok Pixel\" or a custom HTML tag that references\n analytics.tiktok.com — if it's there, leave this toggle off.\n </p>\n ),\n },\n {\n title: 'How to find your Pixel ID',\n content: (\n <ol style={{ margin: 0, paddingLeft: '20px' }}>\n <li>Go to ads.tiktok.com and sign in to TikTok Ads Manager</li>\n <li>Navigate to <strong>Assets → Events → Web Events</strong></li>\n <li>Select your Pixel (or create one if you haven't set one up yet)</li>\n <li>Your Pixel ID is the number shown at the top of the Pixel detail page — it looks like <strong>CXXXXXXXXXXXXXXXXX</strong></li>\n <li>Paste it into the field below and flip the toggle to enable</li>\n </ol>\n ),\n },\n ],\n bing: [\n {\n title: 'What is this?',\n content: (\n <p style={{ margin: 0 }}>\n The UET (Universal Event Tracking) tag is Microsoft Advertising's tracking pixel. It connects your\n website to your Microsoft Ads account — the platform that runs ads on Bing, Yahoo, DuckDuckGo, and\n across Microsoft's partner network.\n </p>\n ),\n },\n {\n title: 'Why would I use it?',\n content: (\n <p style={{ margin: 0 }}>\n If you're running Microsoft Ads (formerly Bing Ads), this is how the platform tracks conversions\n and builds remarketing audiences. Without it, you can't see which ads and keywords are producing\n leads, and you can't retarget people who visited your site but didn't convert.\n <br /><br />\n A lot of businesses overlook Microsoft Ads because the volume is smaller than Google. But that's\n exactly why it works — less competition usually means cheaper cost-per-click, and the audience\n tends to skew older and higher income. For home services, legal, medical, and professional\n services, it's often one of the best-performing paid channels dollar-for-dollar. If you're already\n running Google Ads, adding Bing is usually an easy win.\n </p>\n ),\n },\n {\n title: 'When should I skip this?',\n content: (\n <p style={{ margin: 0 }}>\n If you already have the UET tag firing through Google Tag Manager, don't enable it here. Check\n your GTM container for a tag called \"Microsoft Advertising Universal Event Tracking\" or a custom\n HTML tag referencing bat.bing.com — if it exists, leave this toggle off.\n </p>\n ),\n },\n {\n title: 'How to find your UET Tag ID',\n content: (\n <ol style={{ margin: 0, paddingLeft: '20px' }}>\n <li>Go to ads.microsoft.com and sign in</li>\n <li>In the top navigation, go to <strong>Tools → UET tag</strong></li>\n <li>Select your tag (or create one if you haven't set one up yet)</li>\n <li>Your UET Tag ID is the number shown in the tag details — it looks like <strong>12345678</strong></li>\n <li>Paste it into the field below and flip the toggle to enable</li>\n </ol>\n ),\n },\n ],\n pinterest: [\n {\n title: 'What is this?',\n content: (\n <p style={{ margin: 0 }}>\n The Pinterest Tag is a tracking code that connects your website to your Pinterest Ads account. It\n tracks what visitors do on your site after interacting with your Pins or ads.\n </p>\n ),\n },\n {\n title: 'Why would I use it?',\n content: (\n <p style={{ margin: 0 }}>\n Pinterest is different from most ad platforms because people use it to plan — they're actively\n searching for ideas, products, and services they intend to buy. That makes Pinterest traffic\n unusually high-intent compared to other social platforms. The Pinterest Tag lets you track which\n Pins and ads are driving real actions on your site, build retargeting audiences from your visitors,\n and help Pinterest's algorithm find more people like your best customers.\n <br /><br />\n This is especially valuable if your business is visual — home renovation, landscaping, interior\n design, custom builds, real estate, or anything where before-and-after photos or project galleries\n are part of how you sell. If your customers pin ideas before they hire, Pinterest is worth your\n attention.\n </p>\n ),\n },\n {\n title: 'When should I skip this?',\n content: (\n <p style={{ margin: 0 }}>\n If you already have the Pinterest Tag firing through Google Tag Manager, don't enable it here.\n Check your GTM container for a tag called \"Pinterest Tag\" or a custom HTML tag referencing\n ct.pinterest.com — if it exists, leave this toggle off.\n </p>\n ),\n },\n {\n title: 'How to find your Pinterest Tag ID',\n content: (\n <ol style={{ margin: 0, paddingLeft: '20px' }}>\n <li>Go to ads.pinterest.com and sign in</li>\n <li>In the top navigation, click <strong>Ads → Conversions</strong></li>\n <li>Select your tag (or create one if you haven't set one up yet)</li>\n <li>Your Tag ID is the number shown in the tag details — it looks like <strong>1234567890123</strong></li>\n <li>Paste it into the field below and flip the toggle to enable</li>\n </ol>\n ),\n },\n ],\n nextdoor: [\n {\n title: 'What is this?',\n content: (\n <p style={{ margin: 0 }}>\n The Nextdoor Conversion Pixel is a tracking code from Nextdoor's advertising platform. It connects\n your website activity to your Nextdoor Ads Manager account.\n </p>\n ),\n },\n {\n title: 'Why would I use it?',\n content: (\n <p style={{ margin: 0 }}>\n Nextdoor is the neighborhood-level social network — and for local service businesses, it's one of\n the most targeted advertising platforms available. The people on Nextdoor are homeowners in specific\n neighborhoods, actively talking about and recommending local businesses. The Conversion Pixel lets\n you track which Nextdoor ads are driving visits and conversions on your website, build retargeting\n audiences, and measure your actual return on Nextdoor ad spend.\n <br /><br />\n If your business serves a specific geographic area — plumbing, HVAC, roofing, landscaping,\n cleaning, legal, dental, or any other local service — Nextdoor puts your ads directly in front of\n the neighbors who need you. The Pixel is how you prove it's working.\n </p>\n ),\n },\n {\n title: 'When should I skip this?',\n content: (\n <p style={{ margin: 0 }}>\n If you already have the Nextdoor Pixel firing through Google Tag Manager, don't enable it here.\n Check your GTM container for a custom HTML tag referencing Nextdoor's tracking domain — if it's\n there, leave this toggle off.\n </p>\n ),\n },\n {\n title: 'How to find your Nextdoor Pixel ID',\n content: (\n <ol style={{ margin: 0, paddingLeft: '20px' }}>\n <li>Go to ads.nextdoor.com and sign in</li>\n <li>Navigate to <strong>Measurement → Nextdoor Pixel</strong></li>\n <li>Select your Pixel (or create one if you haven't set one up yet)</li>\n <li>Your Pixel ID is displayed on the setup page</li>\n <li>Paste it into the field below and flip the toggle to enable</li>\n </ol>\n ),\n },\n ],\n};\n\n// ─── SVG Logos ────────────────────────────────────────────────────────────────\n\nconst GtmLogo = ({ size = 24 }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M12 2L22 7.77V16.22L12 22L2 16.22V7.77L12 2Z\" fill=\"#4285F4\" />\n <circle cx=\"12\" cy=\"12\" r=\"4\" fill=\"white\" />\n </svg>\n);\n\nconst Ga4Logo = ({ size = 24 }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect x=\"3\" y=\"14\" width=\"4\" height=\"6\" rx=\"1\" fill=\"#F4B400\" />\n <rect x=\"10\" y=\"10\" width=\"4\" height=\"10\" rx=\"1\" fill=\"#E37400\" />\n <rect x=\"17\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\" fill=\"#D93025\" />\n </svg>\n);\n\nconst MetaLogo = ({ size = 24 }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M8 8C5.79086 8 4 9.79086 4 12C4 14.2091 5.79086 16 8 16C9.5 16 10.5 15 12 13.5C13.5 12 14.5 8 16 8C18.2091 8 20 9.79086 20 12C20 14.2091 18.2091 16 16 16\"\n stroke=\"#1877F2\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n);\n\nconst LinkedinLogo = ({ size = 24 }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect width=\"24\" height=\"24\" rx=\"4\" fill=\"#0A66C2\" />\n <path d=\"M7 9H5V18H7V9Z\" fill=\"white\" />\n <circle cx=\"6\" cy=\"6.5\" r=\"1.5\" fill=\"white\" />\n <path d=\"M10 9H12V10.5C12.5 9.5 13.5 9 15 9C17.5 9 19 10.5 19 13.5V18H17V13.5C17 12 16.5 11 15 11C13.5 11 12 12 12 13.5V18H10V9Z\" fill=\"white\" />\n </svg>\n);\n\nconst TiktokLogo = ({ size = 24 }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M12 2H15C15 4 16 5 18 5V8C16 8 14 7 13 6V15C13 17.7614 10.7614 20 8 20C5.23858 20 3 17.7614 3 15C3 12.2386 5.23858 10 8 10V13C6.89543 13 6 13.8954 6 15C6 16.1046 6.89543 17 8 17C9.10457 17 10 16.1046 10 15V2H12Z\"\n fill=\"#000000\"\n />\n </svg>\n);\n\nconst BingLogo = ({ size = 24 }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect x=\"3\" y=\"3\" width=\"8\" height=\"8\" fill=\"#F25022\" />\n <rect x=\"13\" y=\"3\" width=\"8\" height=\"8\" fill=\"#7FBA00\" />\n <rect x=\"3\" y=\"13\" width=\"8\" height=\"8\" fill=\"#00A4EF\" />\n <rect x=\"13\" y=\"13\" width=\"8\" height=\"8\" fill=\"#FFB900\" />\n </svg>\n);\n\nconst PinterestLogo = ({ size = 24 }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" fill=\"#E60023\" />\n <path\n d=\"M12 6C9.5 6 8 7.5 8 9.5C8 10.5 8.5 11.5 9.5 11.5C9.8 11.5 10 11.2 10 11C10 10.8 9.8 10.2 9.8 10C9.8 8.5 11 7.5 12.5 7.5C14 7.5 15 8.5 15 10C15 12 14 13.5 12.5 13.5C11.5 13.5 11 12.8 11 12C11 11 11.5 9.5 11.5 8.5C11.5 7.8 11 7 10 7C8.8 7 8 8 8 9.5C8 10.5 8.2 11.2 8.5 12C8.2 13.5 7.5 16 7.5 17.5C7.5 18 7.8 18.5 8 18.5C8.2 18.5 9.5 15 10 13.5C10.5 14.5 11.5 15 12.5 15C15 15 17 12.5 17 10C17 7.5 15 6 12 6Z\"\n fill=\"white\"\n />\n </svg>\n);\n\nconst NextdoorLogo = ({ size = 24 }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M12 3L2 11H5V21H19V11H22L12 3Z\" fill=\"#8ED500\" />\n <path d=\"M12 8L16 11V18H8V11L12 8Z\" fill=\"white\" />\n </svg>\n);\n\nconst PLATFORM_LOGOS: Record<PlatformId, React.FC<{ size?: number }>> = {\n gtm: GtmLogo,\n ga4: Ga4Logo,\n meta: MetaLogo,\n linkedin: LinkedinLogo,\n tiktok: TiktokLogo,\n bing: BingLogo,\n pinterest: PinterestLogo,\n nextdoor: NextdoorLogo,\n};\n\n// ─── Platform Definitions ─────────────────────────────────────────────────────\n\ntype PlatformDef = {\n id: PlatformId;\n title: string;\n color: string;\n placeholder: string;\n toggleKey: keyof TrackingValues;\n idKey: keyof TrackingValues;\n idLabel: string;\n tagline: string;\n quickAnswer: string;\n};\n\nconst PLATFORMS: PlatformDef[] = [\n {\n id: 'gtm',\n title: 'Google Tag Manager (GTM)',\n color: '#4285F4',\n placeholder: 'GTM-XXXXXXX',\n toggleKey: 'gtmEnabled',\n idKey: 'gtmId',\n idLabel: 'Container ID',\n tagline: 'One container to manage all your tracking scripts — for agencies and power users.',\n quickAnswer:\n \"Most small businesses don't need this. If your agency set up Google Tag Manager for you, use this. Otherwise, skip it and enable each platform individually below.\",\n },\n {\n id: 'ga4',\n title: 'Google Analytics 4 (GA4)',\n color: '#E37400',\n placeholder: 'G-XXXXXXXXXX',\n toggleKey: 'ga4Enabled',\n idKey: 'ga4Id',\n idLabel: 'Measurement ID',\n tagline: 'See who visits your website, where they come from, and what they do.',\n quickAnswer:\n \"If you want to understand your website traffic, turn this on. It's the most important tracking tool for any website. Skip it only if your agency manages it through Google Tag Manager.\",\n },\n {\n id: 'meta',\n title: 'Meta (Facebook) Pixel',\n color: '#1877F2',\n placeholder: '123456789012345',\n toggleKey: 'metaEnabled',\n idKey: 'metaId',\n idLabel: 'Pixel ID',\n tagline: 'Connect your website to Facebook & Instagram ads so Meta can track what works.',\n quickAnswer:\n \"Turn this on if you run Facebook or Instagram ads, or plan to. Even if you're not running ads yet, installing it now starts building your audience. Skip it if it's already in your Google Tag Manager.\",\n },\n {\n id: 'linkedin',\n title: 'LinkedIn Insight Tag',\n color: '#0A66C2',\n placeholder: '1234567',\n toggleKey: 'linkedinEnabled',\n idKey: 'linkedinId',\n idLabel: 'Partner ID',\n tagline: 'Track LinkedIn ad performance and see which professionals visit your site.',\n quickAnswer:\n \"Worth enabling if you sell to other businesses — even without ads, you get free data about which professionals visit your site. Skip it if it's already in your Google Tag Manager.\",\n },\n {\n id: 'tiktok',\n title: 'TikTok Pixel',\n color: '#000000',\n placeholder: 'CXXXXXXXXXXXXXXXXX',\n toggleKey: 'tiktokEnabled',\n idKey: 'tiktokId',\n idLabel: 'Pixel ID',\n tagline: 'Let TikTok know which ads are driving real results on your website.',\n quickAnswer:\n \"Enable this if you run TikTok ads or plan to. Skip it if you don't advertise on TikTok or if it's already in your Google Tag Manager.\",\n },\n {\n id: 'bing',\n title: 'Microsoft (Bing) UET Tag',\n color: '#00A4EF',\n placeholder: '12345678',\n toggleKey: 'bingEnabled',\n idKey: 'bingId',\n idLabel: 'UET Tag ID',\n tagline: \"Track conversions from Bing, Yahoo, and Microsoft's ad network.\",\n quickAnswer:\n \"Enable this if you run Microsoft/Bing ads. The audience is smaller than Google but often cheaper and higher-converting for service businesses. Skip if it's already in GTM.\",\n },\n {\n id: 'pinterest',\n title: 'Pinterest Tag',\n color: '#E60023',\n placeholder: '1234567890123',\n toggleKey: 'pinterestEnabled',\n idKey: 'pinterestId',\n idLabel: 'Tag ID',\n tagline: 'Measure which Pins and ads drive visits and actions on your site.',\n quickAnswer:\n \"Enable this if your business is visual and you run Pinterest ads — home services, design, real estate. Skip if you don't use Pinterest for marketing or it's in GTM.\",\n },\n {\n id: 'nextdoor',\n title: 'Nextdoor Conversion Pixel',\n color: '#8ED500',\n placeholder: '12345',\n toggleKey: 'nextdoorEnabled',\n idKey: 'nextdoorId',\n idLabel: 'Pixel ID',\n tagline: 'See which Nextdoor ads are bringing neighbors to your website.',\n quickAnswer:\n \"Great for local service businesses advertising on Nextdoor. Skip if you don't run Nextdoor ads or it's already in your Google Tag Manager.\",\n },\n];\n\n// ─── Sub-components ───────────────────────────────────────────────────────────\n\nfunction AccordionItem({\n title,\n content,\n isLast,\n}: {\n title: string;\n content: React.ReactNode;\n isLast: boolean;\n}) {\n const [isOpen, setIsOpen] = useState(false);\n return (\n <div style={{ borderBottom: isLast ? 'none' : '1px solid #e2e8f0' }}>\n <button\n onClick={() => setIsOpen(!isOpen)}\n style={{\n width: '100%',\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n padding: '16px',\n backgroundColor: isOpen ? '#f8fafc' : '#fff',\n border: 'none',\n cursor: 'pointer',\n textAlign: 'left',\n fontSize: '15px',\n fontWeight: '600',\n color: '#1e293b',\n transition: 'background-color 0.2s ease',\n }}\n >\n {title}\n <span\n style={{\n transform: isOpen ? 'rotate(180deg)' : 'rotate(0deg)',\n transition: 'transform 0.2s ease',\n color: '#64748b',\n }}\n >\n ▼\n </span>\n </button>\n {isOpen && (\n <div\n style={{\n padding: '16px',\n backgroundColor: '#fff',\n fontSize: '14px',\n color: '#475569',\n lineHeight: '1.6',\n }}\n >\n {content}\n </div>\n )}\n </div>\n );\n}\n\nfunction DoubleFiringWarning() {\n return (\n <div\n style={{\n backgroundColor: '#fef2f2',\n border: '1px solid #fca5a5',\n padding: '16px',\n borderRadius: '8px',\n marginBottom: '24px',\n }}\n >\n <h4\n style={{\n margin: '0 0 8px 0',\n fontSize: '15px',\n color: '#991b1b',\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n }}\n >\n <span style={{ fontSize: '18px' }}>⚠️</span> Important: Double-firing risk detected\n </h4>\n <p style={{ margin: 0, fontSize: '14px', color: '#991b1b', lineHeight: '1.5' }}>\n You have Google Tag Manager enabled along with other individual tracking pixels. If those\n pixels are also configured inside your GTM container, they will load twice — inflating your\n analytics and over-reporting conversions. <strong>Pick one approach:</strong> manage everything\n through GTM, or enable each platform individually here. Not both.\n </p>\n </div>\n );\n}\n\nfunction RecommendationCallout({\n platform,\n values,\n}: {\n platform: PlatformDef;\n values: TrackingValues;\n}) {\n const isEnabled = values[platform.toggleKey] as boolean;\n const idValue = values[platform.idKey] as string;\n const isGtmEnabled = values.gtmEnabled;\n const isDoubleFiringRisk = isGtmEnabled && isEnabled && platform.id !== 'gtm';\n const isOtherEnabledWhileGtm =\n platform.id === 'gtm' &&\n isEnabled &&\n (values.ga4Enabled ||\n values.metaEnabled ||\n values.linkedinEnabled ||\n values.tiktokEnabled ||\n values.bingEnabled ||\n values.pinterestEnabled ||\n values.nextdoorEnabled);\n\n return (\n <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>\n {isOtherEnabledWhileGtm && (\n <div\n style={{\n backgroundColor: '#fffbeb',\n border: '1px solid #fde68a',\n padding: '16px',\n borderRadius: '8px',\n }}\n >\n <p style={{ margin: 0, fontSize: '14px', color: '#92400e', lineHeight: '1.5' }}>\n <strong>⚠️ Heads up:</strong> You also have individual tracking pixels enabled below. If\n those same pixels are already configured inside this GTM container, they'll fire twice —\n inflating your data. Either manage everything through GTM, or disable GTM and enable each\n platform individually. Not both.\n </p>\n </div>\n )}\n\n {isDoubleFiringRisk && (\n <div\n style={{\n backgroundColor: '#fef2f2',\n border: '1px solid #fca5a5',\n padding: '16px',\n borderRadius: '8px',\n }}\n >\n <p style={{ margin: 0, fontSize: '14px', color: '#991b1b', lineHeight: '1.5' }}>\n <strong>⚠️ Double-firing risk:</strong> Google Tag Manager is also enabled. If{' '}\n {platform.title} is already configured in your GTM container, you're double-counting.\n Pick one approach.\n </p>\n </div>\n )}\n\n {!isEnabled ? (\n <div\n style={{\n backgroundColor: '#f0f9ff',\n border: '1px solid #bae6fd',\n padding: '16px',\n borderRadius: '8px',\n }}\n >\n <p style={{ margin: 0, fontSize: '14px', color: '#0369a1', lineHeight: '1.5' }}>\n <strong>ℹ️ Should I enable this?</strong> {platform.quickAnswer}\n </p>\n </div>\n ) : idValue ? (\n <div\n style={{\n backgroundColor: '#f0fdf4',\n border: '1px solid #bbf7d0',\n padding: '16px',\n borderRadius: '8px',\n }}\n >\n <p style={{ margin: 0, fontSize: '14px', color: '#166534', lineHeight: '1.5' }}>\n <strong>✓ You're all set.</strong> {platform.title} is active and tracking. Your{' '}\n {platform.idLabel} is configured.\n </p>\n </div>\n ) : (\n <div\n style={{\n backgroundColor: '#fffbeb',\n border: '1px solid #fde68a',\n padding: '16px',\n borderRadius: '8px',\n }}\n >\n <p style={{ margin: 0, fontSize: '14px', color: '#92400e', lineHeight: '1.5' }}>\n <strong>⚠️ Action required:</strong> You've enabled {platform.title} but haven't entered\n your {platform.idLabel} yet. Paste it below to start tracking.\n </p>\n </div>\n )}\n </div>\n );\n}\n\n// ─── Core Settings Page ───────────────────────────────────────────────────────\n\ninterface TrackingSettingsPageProps {\n values: TrackingValues;\n onChange: (values: TrackingValues) => void;\n}\n\nfunction TrackingSettingsPage({ values, onChange }: TrackingSettingsPageProps) {\n const [activePlatform, setActivePlatform] = useState<PlatformId | null>(null);\n\n const handleToggle = (key: keyof TrackingValues, checked: boolean) => {\n onChange({ ...values, [key]: checked });\n };\n\n const handleTextChange = (key: keyof TrackingValues, text: string) => {\n onChange({ ...values, [key]: text });\n };\n\n const isGtmEnabled = values.gtmEnabled;\n const isOtherEnabled =\n values.ga4Enabled ||\n values.metaEnabled ||\n values.linkedinEnabled ||\n values.tiktokEnabled ||\n values.bingEnabled ||\n values.pinterestEnabled ||\n values.nextdoorEnabled;\n const showDoubleFiringWarning = isGtmEnabled && isOtherEnabled;\n\n return (\n <div\n style={{\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif',\n color: '#1e293b',\n maxWidth: '1200px',\n margin: '0 auto',\n padding: '24px',\n boxSizing: 'border-box',\n }}\n >\n <div style={{ marginBottom: '32px' }}>\n <h1 style={{ margin: '0 0 8px 0', fontSize: '24px', fontWeight: '600' }}>\n Tracking Settings\n </h1>\n <p style={{ margin: 0, color: '#64748b', fontSize: '15px' }}>\n Configure your analytics and tracking pixels. Click any platform to manage its settings and\n view setup instructions.\n </p>\n </div>\n\n {activePlatform === null ? (\n // ── Overview grid ──────────────────────────────────────────────────────\n <div>\n {showDoubleFiringWarning && <DoubleFiringWarning />}\n\n <div\n style={{\n display: 'grid',\n gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))',\n gap: '16px',\n }}\n >\n {PLATFORMS.map((platform) => {\n const isEnabled = values[platform.toggleKey] as boolean;\n const idValue = values[platform.idKey] as string;\n const Logo = PLATFORM_LOGOS[platform.id];\n const showGtmActiveNote = isGtmEnabled && platform.id !== 'gtm';\n const showGtmOwnerNote = platform.id === 'gtm' && isEnabled && isOtherEnabled;\n\n return (\n <div\n key={platform.id}\n onClick={() => setActivePlatform(platform.id)}\n style={{\n backgroundColor: '#fff',\n border: '1px solid #e2e8f0',\n borderRadius: '8px',\n padding: '20px',\n cursor: 'pointer',\n boxShadow: '0 1px 2px 0 rgba(0,0,0,0.05)',\n transition: 'all 0.2s ease',\n display: 'flex',\n flexDirection: 'column',\n gap: '12px',\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.borderColor = platform.color;\n e.currentTarget.style.boxShadow =\n '0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -1px rgba(0,0,0,0.06)';\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.borderColor = '#e2e8f0';\n e.currentTarget.style.boxShadow = '0 1px 2px 0 rgba(0,0,0,0.05)';\n }}\n >\n <div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>\n <Logo size={24} />\n <h3 style={{ margin: 0, fontSize: '16px', fontWeight: '600', color: '#1e293b' }}>\n {platform.title}\n </h3>\n </div>\n\n <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>\n <div\n style={{\n width: '8px',\n height: '8px',\n borderRadius: '50%',\n backgroundColor: isEnabled ? '#22c55e' : '#cbd5e1',\n }}\n />\n <span\n style={{\n fontSize: '14px',\n fontWeight: '500',\n color: isEnabled ? '#15803d' : '#64748b',\n }}\n >\n {isEnabled ? 'Active' : 'Not configured'}\n </span>\n </div>\n\n {isEnabled && idValue && (\n <div\n style={{\n marginTop: '4px',\n padding: '6px 10px',\n backgroundColor: '#f8fafc',\n borderRadius: '4px',\n border: '1px solid #e2e8f0',\n fontSize: '13px',\n fontFamily: 'monospace',\n color: '#475569',\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n }}\n >\n {idValue}\n </div>\n )}\n\n {showGtmOwnerNote && (\n <div\n style={{\n padding: '8px 10px',\n backgroundColor: '#fffbeb',\n border: '1px solid #fde68a',\n borderRadius: '6px',\n fontSize: '12px',\n color: '#92400e',\n lineHeight: '1.4',\n }}\n >\n ⚠️ Other pixels are also enabled. If they're inside your GTM container, they'll\n fire twice.\n </div>\n )}\n\n {showGtmActiveNote && (\n <div\n style={{\n padding: '8px 10px',\n backgroundColor: '#f0f9ff',\n border: '1px solid #bae6fd',\n borderRadius: '6px',\n fontSize: '12px',\n color: '#0369a1',\n lineHeight: '1.4',\n }}\n >\n ℹ️ GTM is active. Check your container before enabling — this pixel may already\n be loaded.\n </div>\n )}\n </div>\n );\n })}\n </div>\n </div>\n ) : (\n // ── Detail view ────────────────────────────────────────────────────────\n <div>\n <button\n onClick={() => setActivePlatform(null)}\n style={{\n background: 'none',\n border: 'none',\n color: '#3b82f6',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n gap: '6px',\n padding: 0,\n marginBottom: '24px',\n fontSize: '14px',\n fontWeight: '500',\n }}\n >\n <span style={{ fontSize: '16px' }}>←</span> Back to Overview\n </button>\n\n {(() => {\n const platform = PLATFORMS.find((p) => p.id === activePlatform)!;\n const isEnabled = values[platform.toggleKey] as boolean;\n const idValue = values[platform.idKey] as string;\n const Logo = PLATFORM_LOGOS[platform.id];\n\n return (\n <div style={{ maxWidth: '800px' }}>\n {/* Hero card */}\n <div\n style={{\n backgroundColor: '#fff',\n border: '1px solid #e2e8f0',\n borderRadius: '8px',\n padding: '32px',\n boxShadow: '0 1px 3px 0 rgba(0,0,0,0.1)',\n marginBottom: '24px',\n }}\n >\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '16px',\n marginBottom: '16px',\n }}\n >\n <Logo size={40} />\n <h2 style={{ margin: 0, fontSize: '24px', fontWeight: '600', color: '#1e293b' }}>\n {platform.title}\n </h2>\n </div>\n\n <p\n style={{\n margin: '0 0 24px 0',\n fontSize: '16px',\n color: '#475569',\n lineHeight: '1.5',\n }}\n >\n {platform.tagline}\n </p>\n\n <RecommendationCallout platform={platform} values={values} />\n\n {/* Configuration block */}\n <div\n style={{\n marginTop: '32px',\n paddingTop: '32px',\n borderTop: '1px solid #e2e8f0',\n }}\n >\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n marginBottom: '20px',\n }}\n >\n <h3 style={{ margin: 0, fontSize: '16px', fontWeight: '600', color: '#1e293b' }}>\n Enable Tracking\n </h3>\n\n {/* Toggle switch */}\n <label style={{ display: 'flex', alignItems: 'center', cursor: 'pointer' }}>\n <div style={{ position: 'relative' }}>\n <input\n type=\"checkbox\"\n checked={isEnabled}\n onChange={(e) => handleToggle(platform.toggleKey, e.target.checked)}\n style={{ opacity: 0, width: 0, height: 0, position: 'absolute' }}\n />\n <div\n style={{\n width: '44px',\n height: '24px',\n backgroundColor: isEnabled ? '#22c55e' : '#cbd5e1',\n borderRadius: '9999px',\n transition: 'background-color 0.2s ease',\n position: 'relative',\n }}\n >\n <div\n style={{\n position: 'absolute',\n top: '2px',\n left: isEnabled ? '22px' : '2px',\n width: '20px',\n height: '20px',\n backgroundColor: '#fff',\n borderRadius: '50%',\n transition: 'left 0.2s ease',\n boxShadow: '0 1px 3px rgba(0,0,0,0.1)',\n }}\n />\n </div>\n </div>\n <span\n style={{\n marginLeft: '12px',\n fontSize: '14px',\n fontWeight: '500',\n color: isEnabled ? '#15803d' : '#64748b',\n }}\n >\n {isEnabled ? 'Active' : 'Disabled'}\n </span>\n </label>\n </div>\n\n <div\n style={{ opacity: isEnabled ? 1 : 0.6, transition: 'opacity 0.2s ease' }}\n >\n <label\n style={{\n display: 'block',\n marginBottom: '8px',\n fontSize: '14px',\n fontWeight: '500',\n color: '#475569',\n }}\n >\n {platform.idLabel}\n </label>\n <input\n type=\"text\"\n value={idValue}\n onChange={(e) => handleTextChange(platform.idKey, e.target.value)}\n disabled={!isEnabled}\n placeholder={platform.placeholder}\n style={{\n width: '100%',\n padding: '10px 12px',\n fontSize: '14px',\n border: '1px solid #cbd5e1',\n borderRadius: '6px',\n backgroundColor: isEnabled ? '#fff' : '#f1f5f9',\n color: isEnabled ? '#0f172a' : '#94a3b8',\n outline: 'none',\n boxSizing: 'border-box',\n transition: 'border-color 0.2s ease, box-shadow 0.2s ease',\n }}\n onFocus={(e) => {\n if (isEnabled) {\n e.target.style.borderColor = platform.color;\n e.target.style.boxShadow = `0 0 0 3px ${platform.color}20`;\n }\n }}\n onBlur={(e) => {\n e.target.style.borderColor = '#cbd5e1';\n e.target.style.boxShadow = 'none';\n }}\n />\n {!isEnabled && (\n <p style={{ margin: '8px 0 0 0', fontSize: '13px', color: '#94a3b8' }}>\n Enable this platform to enter your ID.\n </p>\n )}\n </div>\n </div>\n </div>\n\n {/* Setup guide accordions */}\n <h3 style={{ margin: '0 0 16px 0', fontSize: '18px', fontWeight: '600', color: '#1e293b' }}>\n Setup Guide\n </h3>\n <div\n style={{\n border: '1px solid #e2e8f0',\n borderRadius: '8px',\n overflow: 'hidden',\n }}\n >\n {HELP_CONTENT[platform.id].map((section, idx) => (\n <AccordionItem\n key={idx}\n title={section.title}\n content={section.content}\n isLast={idx === HELP_CONTENT[platform.id].length - 1}\n />\n ))}\n </div>\n </div>\n );\n })()}\n </div>\n )}\n </div>\n );\n}\n\n// ─── Admin Wrapper — loads/saves via plugin API ───────────────────────────────\n\ntype SaveStatus = 'idle' | 'saving' | 'saved' | 'error';\n\ntype TrackingSettingsPayload = TrackingValues & { settingsRevision: number };\n\ntype TrackingSaveResponse =\n | { ok: true; settingsRevision: number }\n | { ok: false; conflict: true; settingsRevision: number };\n\nexport function TrackingSettingsAdmin() {\n const api = usePluginAPI();\n const [values, setValues] = useState<TrackingValues | null>(null);\n const [saveStatus, setSaveStatus] = useState<SaveStatus>('idle');\n\n // Serialized save queue: only one request in-flight at a time.\n // If values change while a request is in-flight, the latest snapshot is held\n // in savePending and dispatched immediately when the current request settles.\n const saveTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const saveInFlight = useRef(false);\n const savePending = useRef<TrackingValues | null>(null);\n // Server revision for optimistic concurrency (rejects out-of-order snapshots).\n const serverRevisionRef = useRef(0);\n // Latest form state for conflict retries (always matches what the user sees).\n const latestValuesRef = useRef<TrackingValues | null>(null);\n // Ignore late responses from superseded HTTP requests.\n const saveRequestId = useRef(0);\n\n useEffect(() => {\n void api\n .get<TrackingValues & { settingsRevision?: number }>('tracking/settings')\n .then((data) => {\n const { settingsRevision, ...rest } = data;\n serverRevisionRef.current = settingsRevision ?? 0;\n latestValuesRef.current = rest;\n setValues(rest);\n });\n }, [api]);\n\n const dispatchSave = useCallback(\n (snapshot: TrackingValues) => {\n const requestId = ++saveRequestId.current;\n saveInFlight.current = true;\n savePending.current = null;\n const payload: TrackingSettingsPayload = {\n ...snapshot,\n settingsRevision: serverRevisionRef.current,\n };\n void api\n .post<TrackingSaveResponse>('tracking/save', payload)\n .then((res) => {\n if (requestId !== saveRequestId.current) {\n return;\n }\n if (res.ok === false && res.conflict) {\n serverRevisionRef.current = res.settingsRevision;\n saveInFlight.current = false;\n const latest = latestValuesRef.current;\n if (latest) {\n dispatchSave(latest);\n }\n return;\n }\n if (!res.ok || typeof res.settingsRevision !== 'number') {\n saveInFlight.current = false;\n if (!savePending.current) savePending.current = snapshot;\n setSaveStatus('error');\n return;\n }\n serverRevisionRef.current = res.settingsRevision;\n saveInFlight.current = false;\n const queued = savePending.current;\n if (queued) {\n dispatchSave(queued);\n } else {\n setSaveStatus('saved');\n setTimeout(() => setSaveStatus('idle'), 2000);\n }\n })\n .catch(() => {\n if (requestId !== saveRequestId.current) {\n return;\n }\n saveInFlight.current = false;\n if (!savePending.current) savePending.current = snapshot;\n setSaveStatus('error');\n });\n },\n [api],\n );\n\n const handleChange = useCallback(\n (next: TrackingValues) => {\n latestValuesRef.current = next;\n setValues(next);\n setSaveStatus('saving');\n\n if (saveTimer.current) clearTimeout(saveTimer.current);\n saveTimer.current = setTimeout(() => {\n if (saveInFlight.current) {\n savePending.current = next;\n } else {\n dispatchSave(next);\n }\n }, 600);\n },\n [dispatchSave],\n );\n\n if (!values) {\n return (\n <div\n style={{\n padding: '48px 24px',\n textAlign: 'center',\n color: '#94a3b8',\n fontSize: '14px',\n }}\n >\n Loading tracking settings…\n </div>\n );\n }\n\n return (\n <div>\n {/* Save status banner */}\n {saveStatus !== 'idle' && (\n <div\n style={{\n position: 'sticky',\n top: 0,\n zIndex: 50,\n padding: '10px 24px',\n fontSize: '13px',\n fontWeight: '500',\n textAlign: 'center',\n backgroundColor:\n saveStatus === 'error'\n ? '#fef2f2'\n : saveStatus === 'saved'\n ? '#f0fdf4'\n : '#f8fafc',\n color:\n saveStatus === 'error'\n ? '#991b1b'\n : saveStatus === 'saved'\n ? '#166534'\n : '#64748b',\n borderBottom: '1px solid',\n borderColor:\n saveStatus === 'error'\n ? '#fca5a5'\n : saveStatus === 'saved'\n ? '#bbf7d0'\n : '#e2e8f0',\n }}\n >\n {saveStatus === 'saving' && 'Saving…'}\n {saveStatus === 'saved' && '✓ Changes saved'}\n {saveStatus === 'error' && '⚠ Error saving — please try again'}\n </div>\n )}\n\n <TrackingSettingsPage values={values} onChange={handleChange} />\n </div>\n );\n}\n","/**\n * Admin UI entry point — loaded in the browser by EmDash's admin panel.\n * Exports `pages` (keyed by path) and `widgets` (keyed by ID).\n * Paths must match the `admin.pages` declared in src/index.ts.\n */\nimport { AdminDashboard } from './admin/Dashboard';\nimport { SettingsPage } from './admin/Settings';\nimport { TrackingSettingsAdmin } from './admin/TrackingSettings';\n\nexport const pages = {\n '/dashboard': AdminDashboard,\n '/settings': SettingsPage,\n '/tracking': TrackingSettingsAdmin,\n};\n\nexport const widgets = {};\n"],"mappings":"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"}
1
+ {"version":3,"file":"admin.mjs","names":[],"sources":["../src/lib/usePluginAPI.ts","../src/admin/Dashboard.tsx","../src/admin/TrackingSettings.tsx","../src/admin/Settings.tsx","../src/admin/PluginLayout.tsx","../src/admin.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { API_BASE, apiFetch, parseApiResponse } from '@emdash-cms/admin';\n\nconst PLUGIN_ID = 'roi-insights';\n\n/**\n * Shim for usePluginAPI — @emdash-cms/admin 0.1.0 does not export this hook.\n * Builds a stable { get, post } helper that routes through the plugin API\n * at /_emdash/api/plugins/roi-insights/<path> using the admin apiFetch\n * wrapper (adds CSRF header) and parseApiResponse (handles error shapes).\n *\n * Remove this file once @emdash-cms/admin exports usePluginAPI natively.\n */\nexport function usePluginAPI() {\n return useMemo(() => ({\n get<T>(path: string): Promise<T> {\n return apiFetch(`${API_BASE}/plugins/${PLUGIN_ID}/${path}`)\n .then(r => parseApiResponse<T>(r));\n },\n post<T>(path: string, body: unknown): Promise<T> {\n return apiFetch(`${API_BASE}/plugins/${PLUGIN_ID}/${path}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(body),\n }).then(r => parseApiResponse<T>(r));\n },\n }), []);\n}\n","import React, { useEffect, useState } from 'react';\nimport { usePluginAPI } from '../lib/usePluginAPI';\nimport type { LicenseData } from '../types';\n\nconst DASHBOARD_BASE_URL = 'https://my.roiknowledge.com/embed';\n\ninterface AdminDashboardProps {\n onNavigateToSettings?: () => void;\n}\n\nexport function AdminDashboard({ onNavigateToSettings }: AdminDashboardProps) {\n const api = usePluginAPI();\n const [dashboardUrl, setDashboardUrl] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n void (async () => {\n try {\n const license = await api.get<LicenseData>('license/status');\n\n if (!license.isValid) {\n setError('No active license. Configure your license key in Settings, then click Activate.');\n return;\n }\n if (license.isFallback) {\n setError('Dashboard unavailable — could not reach the MosierData backend. Tracking scripts are still active.');\n return;\n }\n if (!license.sessionToken) {\n setError('Session token missing. Re-activate your license in Settings.');\n return;\n }\n\n const url = new URL(DASHBOARD_BASE_URL);\n url.searchParams.set('token', license.sessionToken);\n url.searchParams.set('tier', license.tier ?? 'free');\n setDashboardUrl(url.toString());\n } catch {\n setError('Failed to load license status.');\n }\n })();\n }, [api]);\n\n if (error) {\n return (\n <div style={{ padding: '2rem', color: '#555' }}>\n <h3>ROI Insights Dashboard</h3>\n <p>{error}</p>\n {onNavigateToSettings && (\n <button\n onClick={onNavigateToSettings}\n style={{ background: 'none', border: 'none', color: '#2563eb', cursor: 'pointer', padding: 0, fontSize: 'inherit' }}\n >\n Go to License &amp; Google →\n </button>\n )}\n </div>\n );\n }\n\n if (!dashboardUrl) {\n return <div style={{ padding: '2rem', color: '#888' }}>Loading ROI Insights Dashboard…</div>;\n }\n\n return (\n <div style={{ width: '100%', height: 'calc(100vh - 64px)', overflow: 'hidden' }}>\n <iframe\n src={dashboardUrl}\n style={{ width: '100%', height: '100%', border: 'none', display: 'block' }}\n title=\"ROI Insights Dashboard\"\n sandbox=\"allow-scripts allow-same-origin allow-forms allow-popups\"\n />\n </div>\n );\n}\n","import React, { useState, useEffect, useRef, useCallback } from 'react';\nimport { usePluginAPI } from '../lib/usePluginAPI';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport interface TrackingValues {\n gtmEnabled: boolean;\n gtmId: string;\n ga4Enabled: boolean;\n ga4Id: string;\n metaEnabled: boolean;\n metaId: string;\n linkedinEnabled: boolean;\n linkedinId: string;\n tiktokEnabled: boolean;\n tiktokId: string;\n bingEnabled: boolean;\n bingId: string;\n pinterestEnabled: boolean;\n pinterestId: string;\n nextdoorEnabled: boolean;\n nextdoorId: string;\n}\n\ntype PlatformId =\n | 'gtm'\n | 'ga4'\n | 'meta'\n | 'linkedin'\n | 'tiktok'\n | 'bing'\n | 'pinterest'\n | 'nextdoor';\n\ninterface HelpSection {\n title: string;\n content: React.ReactNode;\n}\n\n// ─── Help Content ─────────────────────────────────────────────────────────────\n\nconst HELP_CONTENT: Record<PlatformId, HelpSection[]> = {\n gtm: [\n {\n title: 'What is this?',\n content: (\n <p style={{ margin: 0 }}>\n Google Tag Manager is a free tool from Google that acts as a central control panel for all the\n tracking scripts on your website. Instead of adding each tracking code (Google Analytics, Meta\n Pixel, LinkedIn, etc.) to your site individually, you put them all inside one GTM \"container\"\n and manage them from a single dashboard.\n </p>\n ),\n },\n {\n title: 'Why would I use it?',\n content: (\n <p style={{ margin: 0 }}>\n If you or your agency already use GTM, this is the way to go. It means you can add, edit, or\n remove any tracking code from the GTM dashboard without ever touching your website again.\n It's also the cleanest option if you're running multiple tracking tools and want precise control\n over when and how each one fires.\n </p>\n ),\n },\n {\n title: 'When should I skip this?',\n content: (\n <p style={{ margin: 0 }}>\n If you don't have a GTM account and you're not sure what this is, you probably don't need it.\n You can enable Google Analytics, Meta Pixel, and LinkedIn directly using the toggles below —\n no GTM required.\n </p>\n ),\n },\n {\n title: \"Important — don't double up\",\n content: (\n <p style={{ margin: 0 }}>\n If you turn on GTM here <strong>and</strong> you also enable Google Analytics, Meta Pixel, or\n LinkedIn directly in this plugin, those scripts will load twice. That means every pageview and\n every conversion gets counted twice, which makes your data unreliable. Pick one approach: manage\n everything through GTM, <strong>or</strong> enable each platform individually below. Not both.\n </p>\n ),\n },\n {\n title: 'How to find your GTM ID',\n content: (\n <ol style={{ margin: 0, paddingLeft: '20px' }}>\n <li>Go to tagmanager.google.com and sign in</li>\n <li>Select your account and container</li>\n <li>Your GTM ID is in the top-right corner of the screen — it looks like <strong>GTM-XXXXXXX</strong></li>\n <li>Paste that ID into the field below and flip the toggle to enable</li>\n </ol>\n ),\n },\n ],\n ga4: [\n {\n title: 'What is this?',\n content: (\n <p style={{ margin: 0 }}>\n Google Analytics 4 is Google's analytics platform. It tracks who visits your website, how they\n found you, what they do while they're there, and whether they take actions that matter to your\n business — like filling out a contact form or calling your phone number.\n </p>\n ),\n },\n {\n title: 'Why would I use it?',\n content: (\n <p style={{ margin: 0 }}>\n If you want to understand your website traffic, this is the foundation. GA4 automatically tracks\n page views, session duration, traffic sources, device types, and geographic location. You can add\n more advanced tracking on top of that (form submissions, button clicks, purchases), but the basics\n work right out of the box.\n </p>\n ),\n },\n {\n title: 'When should I skip this?',\n content: (\n <p style={{ margin: 0 }}>\n Skip this toggle if you're already sending GA4 data through Google Tag Manager. Enabling it here\n and in GTM will count every visitor twice, which inflates all of your numbers. If you're not sure,\n check your GTM container for a tag called \"Google Analytics: GA4 Configuration\" or \"Google Tag\" —\n if it's there, leave this toggle off.\n </p>\n ),\n },\n {\n title: 'How to find your Measurement ID',\n content: (\n <ol style={{ margin: 0, paddingLeft: '20px' }}>\n <li>Go to analytics.google.com and sign in</li>\n <li>Select your property (or create one if you haven't set one up yet)</li>\n <li>Go to <strong>Admin → Data Streams</strong> and click on your web stream</li>\n <li>Your Measurement ID is at the top of the screen — it looks like <strong>G-XXXXXXXXXX</strong></li>\n <li>Paste it into the field below and flip the toggle to enable</li>\n </ol>\n ),\n },\n ],\n meta: [\n {\n title: 'What is this?',\n content: (\n <p style={{ margin: 0 }}>\n The Meta Pixel is a small piece of tracking code from Meta that connects your website to your\n Facebook and Instagram advertising accounts. It's how Meta knows what happens on your site after\n someone clicks one of your ads.\n </p>\n ),\n },\n {\n title: 'Why would I use it?',\n content: (\n <p style={{ margin: 0 }}>\n Without the Pixel installed, Meta's ad platform has no idea what happens after someone clicks your\n ad. It can't tell which ads are producing leads, it can't build retargeting audiences from your\n website visitors, and it can't optimize delivery toward people who are likely to convert. If you're\n running Facebook or Instagram ads — or if you plan to — the Pixel is essential.\n <br /><br />\n Even if you're not running ads yet, there's value in installing it now. The Pixel starts collecting\n visitor data immediately, so by the time you're ready to run your first campaign, Meta already has\n an audience to work with.\n </p>\n ),\n },\n {\n title: 'When should I skip this?',\n content: (\n <p style={{ margin: 0 }}>\n If you already have the Meta Pixel firing through Google Tag Manager, don't enable it here. You'd\n be loading it twice. Check your GTM container for a tag called \"Meta Pixel\" or \"Facebook Pixel\" —\n if it exists, leave this toggle off and manage it in GTM.\n </p>\n ),\n },\n {\n title: 'How to find your Pixel ID',\n content: (\n <ol style={{ margin: 0, paddingLeft: '20px' }}>\n <li>Go to business.facebook.com and open <strong>Events Manager</strong></li>\n <li>Select your Pixel (or create one under <strong>Connect Data Sources → Web</strong>)</li>\n <li>Your Pixel ID is the number at the top of the Pixel detail screen — it looks like <strong>123456789012345</strong></li>\n <li>Paste it into the field below and flip the toggle to enable</li>\n </ol>\n ),\n },\n ],\n linkedin: [\n {\n title: 'What is this?',\n content: (\n <p style={{ margin: 0 }}>\n The LinkedIn Insight Tag is LinkedIn's version of a tracking pixel. It's a lightweight script that\n connects your website to your LinkedIn Campaign Manager account.\n </p>\n ),\n },\n {\n title: 'Why would I use it?',\n content: (\n <p style={{ margin: 0 }}>\n The Insight Tag does two things. First, it powers LinkedIn ad features — retargeting website\n visitors, tracking conversions from LinkedIn ads, and optimizing ad delivery. Second, and this is\n the part most people don't know about, it unlocks LinkedIn's <strong>Website Demographics</strong>{' '}\n report. That report shows you the job titles, industries, company sizes, and seniority levels of\n the LinkedIn members visiting your website. If you sell to other businesses, that report alone is\n worth the install — even if you never run a LinkedIn ad.\n </p>\n ),\n },\n {\n title: 'When should I skip this?',\n content: (\n <p style={{ margin: 0 }}>\n If you already have the LinkedIn Insight Tag firing through Google Tag Manager, don't enable it\n here. Check your GTM container for a tag called \"LinkedIn Insight Tag\" before proceeding.\n </p>\n ),\n },\n {\n title: 'How to find your Partner ID',\n content: (\n <ol style={{ margin: 0, paddingLeft: '20px' }}>\n <li>Go to linkedin.com/campaignmanager and sign in</li>\n <li>Select your ad account</li>\n <li>In the left navigation, go to <strong>Analyze → Insight Tag</strong></li>\n <li>Your Partner ID is the number shown on that page — it looks like <strong>1234567</strong></li>\n <li>Paste it into the field below and flip the toggle to enable</li>\n </ol>\n ),\n },\n ],\n tiktok: [\n {\n title: 'What is this?',\n content: (\n <p style={{ margin: 0 }}>\n The TikTok Pixel is a tracking code from TikTok that connects your website to your TikTok Ads\n Manager account. It works the same way the Meta Pixel does for Facebook and Instagram — it watches\n what happens on your site and reports back to TikTok so the ad platform can do its job.\n </p>\n ),\n },\n {\n title: 'Why would I use it?',\n content: (\n <p style={{ margin: 0 }}>\n If you run or plan to run TikTok ads, this is how TikTok knows whether your ads are actually\n working. Without the Pixel, TikTok can't track conversions, can't build retargeting audiences from\n your website visitors, and can't optimize ad delivery toward people who are likely to become\n customers. You're essentially paying for traffic with no feedback loop.\n <br /><br />\n TikTok's ad platform has gotten significantly better at reaching local service audiences —\n homeowners, people searching for contractors, anyone in a buying mindset. If your customers skew\n under 50 and you're spending on any paid social, TikTok is worth testing. Installing the Pixel now\n means the platform is already collecting data when you're ready to launch your first campaign.\n </p>\n ),\n },\n {\n title: 'When should I skip this?',\n content: (\n <p style={{ margin: 0 }}>\n If you already have the TikTok Pixel firing through Google Tag Manager, don't enable it here.\n Check your GTM container for a tag called \"TikTok Pixel\" or a custom HTML tag that references\n analytics.tiktok.com — if it's there, leave this toggle off.\n </p>\n ),\n },\n {\n title: 'How to find your Pixel ID',\n content: (\n <ol style={{ margin: 0, paddingLeft: '20px' }}>\n <li>Go to ads.tiktok.com and sign in to TikTok Ads Manager</li>\n <li>Navigate to <strong>Assets → Events → Web Events</strong></li>\n <li>Select your Pixel (or create one if you haven't set one up yet)</li>\n <li>Your Pixel ID is the number shown at the top of the Pixel detail page — it looks like <strong>CXXXXXXXXXXXXXXXXX</strong></li>\n <li>Paste it into the field below and flip the toggle to enable</li>\n </ol>\n ),\n },\n ],\n bing: [\n {\n title: 'What is this?',\n content: (\n <p style={{ margin: 0 }}>\n The UET (Universal Event Tracking) tag is Microsoft Advertising's tracking pixel. It connects your\n website to your Microsoft Ads account — the platform that runs ads on Bing, Yahoo, DuckDuckGo, and\n across Microsoft's partner network.\n </p>\n ),\n },\n {\n title: 'Why would I use it?',\n content: (\n <p style={{ margin: 0 }}>\n If you're running Microsoft Ads (formerly Bing Ads), this is how the platform tracks conversions\n and builds remarketing audiences. Without it, you can't see which ads and keywords are producing\n leads, and you can't retarget people who visited your site but didn't convert.\n <br /><br />\n A lot of businesses overlook Microsoft Ads because the volume is smaller than Google. But that's\n exactly why it works — less competition usually means cheaper cost-per-click, and the audience\n tends to skew older and higher income. For home services, legal, medical, and professional\n services, it's often one of the best-performing paid channels dollar-for-dollar. If you're already\n running Google Ads, adding Bing is usually an easy win.\n </p>\n ),\n },\n {\n title: 'When should I skip this?',\n content: (\n <p style={{ margin: 0 }}>\n If you already have the UET tag firing through Google Tag Manager, don't enable it here. Check\n your GTM container for a tag called \"Microsoft Advertising Universal Event Tracking\" or a custom\n HTML tag referencing bat.bing.com — if it exists, leave this toggle off.\n </p>\n ),\n },\n {\n title: 'How to find your UET Tag ID',\n content: (\n <ol style={{ margin: 0, paddingLeft: '20px' }}>\n <li>Go to ads.microsoft.com and sign in</li>\n <li>In the top navigation, go to <strong>Tools → UET tag</strong></li>\n <li>Select your tag (or create one if you haven't set one up yet)</li>\n <li>Your UET Tag ID is the number shown in the tag details — it looks like <strong>12345678</strong></li>\n <li>Paste it into the field below and flip the toggle to enable</li>\n </ol>\n ),\n },\n ],\n pinterest: [\n {\n title: 'What is this?',\n content: (\n <p style={{ margin: 0 }}>\n The Pinterest Tag is a tracking code that connects your website to your Pinterest Ads account. It\n tracks what visitors do on your site after interacting with your Pins or ads.\n </p>\n ),\n },\n {\n title: 'Why would I use it?',\n content: (\n <p style={{ margin: 0 }}>\n Pinterest is different from most ad platforms because people use it to plan — they're actively\n searching for ideas, products, and services they intend to buy. That makes Pinterest traffic\n unusually high-intent compared to other social platforms. The Pinterest Tag lets you track which\n Pins and ads are driving real actions on your site, build retargeting audiences from your visitors,\n and help Pinterest's algorithm find more people like your best customers.\n <br /><br />\n This is especially valuable if your business is visual — home renovation, landscaping, interior\n design, custom builds, real estate, or anything where before-and-after photos or project galleries\n are part of how you sell. If your customers pin ideas before they hire, Pinterest is worth your\n attention.\n </p>\n ),\n },\n {\n title: 'When should I skip this?',\n content: (\n <p style={{ margin: 0 }}>\n If you already have the Pinterest Tag firing through Google Tag Manager, don't enable it here.\n Check your GTM container for a tag called \"Pinterest Tag\" or a custom HTML tag referencing\n ct.pinterest.com — if it exists, leave this toggle off.\n </p>\n ),\n },\n {\n title: 'How to find your Pinterest Tag ID',\n content: (\n <ol style={{ margin: 0, paddingLeft: '20px' }}>\n <li>Go to ads.pinterest.com and sign in</li>\n <li>In the top navigation, click <strong>Ads → Conversions</strong></li>\n <li>Select your tag (or create one if you haven't set one up yet)</li>\n <li>Your Tag ID is the number shown in the tag details — it looks like <strong>1234567890123</strong></li>\n <li>Paste it into the field below and flip the toggle to enable</li>\n </ol>\n ),\n },\n ],\n nextdoor: [\n {\n title: 'What is this?',\n content: (\n <p style={{ margin: 0 }}>\n The Nextdoor Conversion Pixel is a tracking code from Nextdoor's advertising platform. It connects\n your website activity to your Nextdoor Ads Manager account.\n </p>\n ),\n },\n {\n title: 'Why would I use it?',\n content: (\n <p style={{ margin: 0 }}>\n Nextdoor is the neighborhood-level social network — and for local service businesses, it's one of\n the most targeted advertising platforms available. The people on Nextdoor are homeowners in specific\n neighborhoods, actively talking about and recommending local businesses. The Conversion Pixel lets\n you track which Nextdoor ads are driving visits and conversions on your website, build retargeting\n audiences, and measure your actual return on Nextdoor ad spend.\n <br /><br />\n If your business serves a specific geographic area — plumbing, HVAC, roofing, landscaping,\n cleaning, legal, dental, or any other local service — Nextdoor puts your ads directly in front of\n the neighbors who need you. The Pixel is how you prove it's working.\n </p>\n ),\n },\n {\n title: 'When should I skip this?',\n content: (\n <p style={{ margin: 0 }}>\n If you already have the Nextdoor Pixel firing through Google Tag Manager, don't enable it here.\n Check your GTM container for a custom HTML tag referencing Nextdoor's tracking domain — if it's\n there, leave this toggle off.\n </p>\n ),\n },\n {\n title: 'How to find your Nextdoor Pixel ID',\n content: (\n <ol style={{ margin: 0, paddingLeft: '20px' }}>\n <li>Go to ads.nextdoor.com and sign in</li>\n <li>Navigate to <strong>Measurement → Nextdoor Pixel</strong></li>\n <li>Select your Pixel (or create one if you haven't set one up yet)</li>\n <li>Your Pixel ID is displayed on the setup page</li>\n <li>Paste it into the field below and flip the toggle to enable</li>\n </ol>\n ),\n },\n ],\n};\n\n// ─── SVG Logos ────────────────────────────────────────────────────────────────\n\nconst GtmLogo = ({ size = 24 }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M12 2L22 7.77V16.22L12 22L2 16.22V7.77L12 2Z\" fill=\"#4285F4\" />\n <circle cx=\"12\" cy=\"12\" r=\"4\" fill=\"white\" />\n </svg>\n);\n\nconst Ga4Logo = ({ size = 24 }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect x=\"3\" y=\"14\" width=\"4\" height=\"6\" rx=\"1\" fill=\"#F4B400\" />\n <rect x=\"10\" y=\"10\" width=\"4\" height=\"10\" rx=\"1\" fill=\"#E37400\" />\n <rect x=\"17\" y=\"4\" width=\"4\" height=\"16\" rx=\"1\" fill=\"#D93025\" />\n </svg>\n);\n\nconst MetaLogo = ({ size = 24 }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M8 8C5.79086 8 4 9.79086 4 12C4 14.2091 5.79086 16 8 16C9.5 16 10.5 15 12 13.5C13.5 12 14.5 8 16 8C18.2091 8 20 9.79086 20 12C20 14.2091 18.2091 16 16 16\"\n stroke=\"#1877F2\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n);\n\nconst LinkedinLogo = ({ size = 24 }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect width=\"24\" height=\"24\" rx=\"4\" fill=\"#0A66C2\" />\n <path d=\"M7 9H5V18H7V9Z\" fill=\"white\" />\n <circle cx=\"6\" cy=\"6.5\" r=\"1.5\" fill=\"white\" />\n <path d=\"M10 9H12V10.5C12.5 9.5 13.5 9 15 9C17.5 9 19 10.5 19 13.5V18H17V13.5C17 12 16.5 11 15 11C13.5 11 12 12 12 13.5V18H10V9Z\" fill=\"white\" />\n </svg>\n);\n\nconst TiktokLogo = ({ size = 24 }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M12 2H15C15 4 16 5 18 5V8C16 8 14 7 13 6V15C13 17.7614 10.7614 20 8 20C5.23858 20 3 17.7614 3 15C3 12.2386 5.23858 10 8 10V13C6.89543 13 6 13.8954 6 15C6 16.1046 6.89543 17 8 17C9.10457 17 10 16.1046 10 15V2H12Z\"\n fill=\"#000000\"\n />\n </svg>\n);\n\nconst BingLogo = ({ size = 24 }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <rect x=\"3\" y=\"3\" width=\"8\" height=\"8\" fill=\"#F25022\" />\n <rect x=\"13\" y=\"3\" width=\"8\" height=\"8\" fill=\"#7FBA00\" />\n <rect x=\"3\" y=\"13\" width=\"8\" height=\"8\" fill=\"#00A4EF\" />\n <rect x=\"13\" y=\"13\" width=\"8\" height=\"8\" fill=\"#FFB900\" />\n </svg>\n);\n\nconst PinterestLogo = ({ size = 24 }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" fill=\"#E60023\" />\n <path\n d=\"M12 6C9.5 6 8 7.5 8 9.5C8 10.5 8.5 11.5 9.5 11.5C9.8 11.5 10 11.2 10 11C10 10.8 9.8 10.2 9.8 10C9.8 8.5 11 7.5 12.5 7.5C14 7.5 15 8.5 15 10C15 12 14 13.5 12.5 13.5C11.5 13.5 11 12.8 11 12C11 11 11.5 9.5 11.5 8.5C11.5 7.8 11 7 10 7C8.8 7 8 8 8 9.5C8 10.5 8.2 11.2 8.5 12C8.2 13.5 7.5 16 7.5 17.5C7.5 18 7.8 18.5 8 18.5C8.2 18.5 9.5 15 10 13.5C10.5 14.5 11.5 15 12.5 15C15 15 17 12.5 17 10C17 7.5 15 6 12 6Z\"\n fill=\"white\"\n />\n </svg>\n);\n\nconst NextdoorLogo = ({ size = 24 }) => (\n <svg width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M12 3L2 11H5V21H19V11H22L12 3Z\" fill=\"#8ED500\" />\n <path d=\"M12 8L16 11V18H8V11L12 8Z\" fill=\"white\" />\n </svg>\n);\n\nconst PLATFORM_LOGOS: Record<PlatformId, React.FC<{ size?: number }>> = {\n gtm: GtmLogo,\n ga4: Ga4Logo,\n meta: MetaLogo,\n linkedin: LinkedinLogo,\n tiktok: TiktokLogo,\n bing: BingLogo,\n pinterest: PinterestLogo,\n nextdoor: NextdoorLogo,\n};\n\n// ─── Platform Definitions ─────────────────────────────────────────────────────\n\ntype PlatformDef = {\n id: PlatformId;\n title: string;\n color: string;\n placeholder: string;\n toggleKey: keyof TrackingValues;\n idKey: keyof TrackingValues;\n idLabel: string;\n tagline: string;\n quickAnswer: string;\n};\n\nconst PLATFORMS: PlatformDef[] = [\n {\n id: 'gtm',\n title: 'Google Tag Manager (GTM)',\n color: '#4285F4',\n placeholder: 'GTM-XXXXXXX',\n toggleKey: 'gtmEnabled',\n idKey: 'gtmId',\n idLabel: 'Container ID',\n tagline: 'One container to manage all your tracking scripts — for agencies and power users.',\n quickAnswer:\n \"Most small businesses don't need this. If your agency set up Google Tag Manager for you, use this. Otherwise, skip it and enable each platform individually below.\",\n },\n {\n id: 'ga4',\n title: 'Google Analytics 4 (GA4)',\n color: '#E37400',\n placeholder: 'G-XXXXXXXXXX',\n toggleKey: 'ga4Enabled',\n idKey: 'ga4Id',\n idLabel: 'Measurement ID',\n tagline: 'See who visits your website, where they come from, and what they do.',\n quickAnswer:\n \"If you want to understand your website traffic, turn this on. It's the most important tracking tool for any website. Skip it only if your agency manages it through Google Tag Manager.\",\n },\n {\n id: 'meta',\n title: 'Meta (Facebook) Pixel',\n color: '#1877F2',\n placeholder: '123456789012345',\n toggleKey: 'metaEnabled',\n idKey: 'metaId',\n idLabel: 'Pixel ID',\n tagline: 'Connect your website to Facebook & Instagram ads so Meta can track what works.',\n quickAnswer:\n \"Turn this on if you run Facebook or Instagram ads, or plan to. Even if you're not running ads yet, installing it now starts building your audience. Skip it if it's already in your Google Tag Manager.\",\n },\n {\n id: 'linkedin',\n title: 'LinkedIn Insight Tag',\n color: '#0A66C2',\n placeholder: '1234567',\n toggleKey: 'linkedinEnabled',\n idKey: 'linkedinId',\n idLabel: 'Partner ID',\n tagline: 'Track LinkedIn ad performance and see which professionals visit your site.',\n quickAnswer:\n \"Worth enabling if you sell to other businesses — even without ads, you get free data about which professionals visit your site. Skip it if it's already in your Google Tag Manager.\",\n },\n {\n id: 'tiktok',\n title: 'TikTok Pixel',\n color: '#000000',\n placeholder: 'CXXXXXXXXXXXXXXXXX',\n toggleKey: 'tiktokEnabled',\n idKey: 'tiktokId',\n idLabel: 'Pixel ID',\n tagline: 'Let TikTok know which ads are driving real results on your website.',\n quickAnswer:\n \"Enable this if you run TikTok ads or plan to. Skip it if you don't advertise on TikTok or if it's already in your Google Tag Manager.\",\n },\n {\n id: 'bing',\n title: 'Microsoft (Bing) UET Tag',\n color: '#00A4EF',\n placeholder: '12345678',\n toggleKey: 'bingEnabled',\n idKey: 'bingId',\n idLabel: 'UET Tag ID',\n tagline: \"Track conversions from Bing, Yahoo, and Microsoft's ad network.\",\n quickAnswer:\n \"Enable this if you run Microsoft/Bing ads. The audience is smaller than Google but often cheaper and higher-converting for service businesses. Skip if it's already in GTM.\",\n },\n {\n id: 'pinterest',\n title: 'Pinterest Tag',\n color: '#E60023',\n placeholder: '1234567890123',\n toggleKey: 'pinterestEnabled',\n idKey: 'pinterestId',\n idLabel: 'Tag ID',\n tagline: 'Measure which Pins and ads drive visits and actions on your site.',\n quickAnswer:\n \"Enable this if your business is visual and you run Pinterest ads — home services, design, real estate. Skip if you don't use Pinterest for marketing or it's in GTM.\",\n },\n {\n id: 'nextdoor',\n title: 'Nextdoor Conversion Pixel',\n color: '#8ED500',\n placeholder: '12345',\n toggleKey: 'nextdoorEnabled',\n idKey: 'nextdoorId',\n idLabel: 'Pixel ID',\n tagline: 'See which Nextdoor ads are bringing neighbors to your website.',\n quickAnswer:\n \"Great for local service businesses advertising on Nextdoor. Skip if you don't run Nextdoor ads or it's already in your Google Tag Manager.\",\n },\n];\n\n// ─── Sub-components ───────────────────────────────────────────────────────────\n\nfunction AccordionItem({\n title,\n content,\n isLast,\n}: {\n title: string;\n content: React.ReactNode;\n isLast: boolean;\n}) {\n const [isOpen, setIsOpen] = useState(false);\n return (\n <div style={{ borderBottom: isLast ? 'none' : '1px solid #e2e8f0' }}>\n <button\n onClick={() => setIsOpen(!isOpen)}\n style={{\n width: '100%',\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n padding: '16px',\n backgroundColor: isOpen ? '#f8fafc' : '#fff',\n border: 'none',\n cursor: 'pointer',\n textAlign: 'left',\n fontSize: '15px',\n fontWeight: '600',\n color: '#1e293b',\n transition: 'background-color 0.2s ease',\n }}\n >\n {title}\n <span\n style={{\n transform: isOpen ? 'rotate(180deg)' : 'rotate(0deg)',\n transition: 'transform 0.2s ease',\n color: '#64748b',\n }}\n >\n ▼\n </span>\n </button>\n {isOpen && (\n <div\n style={{\n padding: '16px',\n backgroundColor: '#fff',\n fontSize: '14px',\n color: '#475569',\n lineHeight: '1.6',\n }}\n >\n {content}\n </div>\n )}\n </div>\n );\n}\n\nfunction DoubleFiringWarning() {\n return (\n <div\n style={{\n backgroundColor: '#fef2f2',\n border: '1px solid #fca5a5',\n padding: '16px',\n borderRadius: '8px',\n marginBottom: '24px',\n }}\n >\n <h4\n style={{\n margin: '0 0 8px 0',\n fontSize: '15px',\n color: '#991b1b',\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n }}\n >\n <span style={{ fontSize: '18px' }}>⚠️</span> Important: Double-firing risk detected\n </h4>\n <p style={{ margin: 0, fontSize: '14px', color: '#991b1b', lineHeight: '1.5' }}>\n You have Google Tag Manager enabled along with other individual tracking pixels. If those\n pixels are also configured inside your GTM container, they will load twice — inflating your\n analytics and over-reporting conversions. <strong>Pick one approach:</strong> manage everything\n through GTM, or enable each platform individually here. Not both.\n </p>\n </div>\n );\n}\n\nfunction RecommendationCallout({\n platform,\n values,\n}: {\n platform: PlatformDef;\n values: TrackingValues;\n}) {\n const isEnabled = values[platform.toggleKey] as boolean;\n const idValue = values[platform.idKey] as string;\n const isGtmEnabled = values.gtmEnabled;\n const isDoubleFiringRisk = isGtmEnabled && isEnabled && platform.id !== 'gtm';\n const isOtherEnabledWhileGtm =\n platform.id === 'gtm' &&\n isEnabled &&\n (values.ga4Enabled ||\n values.metaEnabled ||\n values.linkedinEnabled ||\n values.tiktokEnabled ||\n values.bingEnabled ||\n values.pinterestEnabled ||\n values.nextdoorEnabled);\n\n return (\n <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>\n {isOtherEnabledWhileGtm && (\n <div\n style={{\n backgroundColor: '#fffbeb',\n border: '1px solid #fde68a',\n padding: '16px',\n borderRadius: '8px',\n }}\n >\n <p style={{ margin: 0, fontSize: '14px', color: '#92400e', lineHeight: '1.5' }}>\n <strong>⚠️ Heads up:</strong> You also have individual tracking pixels enabled below. If\n those same pixels are already configured inside this GTM container, they'll fire twice —\n inflating your data. Either manage everything through GTM, or disable GTM and enable each\n platform individually. Not both.\n </p>\n </div>\n )}\n\n {isDoubleFiringRisk && (\n <div\n style={{\n backgroundColor: '#fef2f2',\n border: '1px solid #fca5a5',\n padding: '16px',\n borderRadius: '8px',\n }}\n >\n <p style={{ margin: 0, fontSize: '14px', color: '#991b1b', lineHeight: '1.5' }}>\n <strong>⚠️ Double-firing risk:</strong> Google Tag Manager is also enabled. If{' '}\n {platform.title} is already configured in your GTM container, you're double-counting.\n Pick one approach.\n </p>\n </div>\n )}\n\n {!isEnabled ? (\n <div\n style={{\n backgroundColor: '#f0f9ff',\n border: '1px solid #bae6fd',\n padding: '16px',\n borderRadius: '8px',\n }}\n >\n <p style={{ margin: 0, fontSize: '14px', color: '#0369a1', lineHeight: '1.5' }}>\n <strong>ℹ️ Should I enable this?</strong> {platform.quickAnswer}\n </p>\n </div>\n ) : idValue ? (\n <div\n style={{\n backgroundColor: '#f0fdf4',\n border: '1px solid #bbf7d0',\n padding: '16px',\n borderRadius: '8px',\n }}\n >\n <p style={{ margin: 0, fontSize: '14px', color: '#166534', lineHeight: '1.5' }}>\n <strong>✓ You're all set.</strong> {platform.title} is active and tracking. Your{' '}\n {platform.idLabel} is configured.\n </p>\n </div>\n ) : (\n <div\n style={{\n backgroundColor: '#fffbeb',\n border: '1px solid #fde68a',\n padding: '16px',\n borderRadius: '8px',\n }}\n >\n <p style={{ margin: 0, fontSize: '14px', color: '#92400e', lineHeight: '1.5' }}>\n <strong>⚠️ Action required:</strong> You've enabled {platform.title} but haven't entered\n your {platform.idLabel} yet. Paste it below to start tracking.\n </p>\n </div>\n )}\n </div>\n );\n}\n\n// ─── Core Settings Page ───────────────────────────────────────────────────────\n\ninterface TrackingSettingsPageProps {\n values: TrackingValues;\n onChange: (values: TrackingValues) => void;\n}\n\nfunction TrackingSettingsPage({ values, onChange }: TrackingSettingsPageProps) {\n const [activePlatform, setActivePlatform] = useState<PlatformId | null>(null);\n\n const handleToggle = (key: keyof TrackingValues, checked: boolean) => {\n onChange({ ...values, [key]: checked });\n };\n\n const handleTextChange = (key: keyof TrackingValues, text: string) => {\n onChange({ ...values, [key]: text });\n };\n\n const isGtmEnabled = values.gtmEnabled;\n const isOtherEnabled =\n values.ga4Enabled ||\n values.metaEnabled ||\n values.linkedinEnabled ||\n values.tiktokEnabled ||\n values.bingEnabled ||\n values.pinterestEnabled ||\n values.nextdoorEnabled;\n const showDoubleFiringWarning = isGtmEnabled && isOtherEnabled;\n\n return (\n <div\n style={{\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif',\n color: '#1e293b',\n maxWidth: '1200px',\n margin: '0 auto',\n padding: '24px',\n boxSizing: 'border-box',\n }}\n >\n <div style={{ marginBottom: '32px' }}>\n <h1 style={{ margin: '0 0 8px 0', fontSize: '24px', fontWeight: '600' }}>\n Tracking Settings\n </h1>\n <p style={{ margin: 0, color: '#64748b', fontSize: '15px' }}>\n Configure your analytics and tracking pixels. Click any platform to manage its settings and\n view setup instructions.\n </p>\n </div>\n\n {activePlatform === null ? (\n // ── Overview grid ──────────────────────────────────────────────────────\n <div>\n {showDoubleFiringWarning && <DoubleFiringWarning />}\n\n <div\n style={{\n display: 'grid',\n gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))',\n gap: '16px',\n }}\n >\n {PLATFORMS.map((platform) => {\n const isEnabled = values[platform.toggleKey] as boolean;\n const idValue = values[platform.idKey] as string;\n const Logo = PLATFORM_LOGOS[platform.id];\n const showGtmActiveNote = isGtmEnabled && platform.id !== 'gtm';\n const showGtmOwnerNote = platform.id === 'gtm' && isEnabled && isOtherEnabled;\n\n return (\n <div\n key={platform.id}\n onClick={() => setActivePlatform(platform.id)}\n style={{\n backgroundColor: '#fff',\n border: '1px solid #e2e8f0',\n borderRadius: '8px',\n padding: '20px',\n cursor: 'pointer',\n boxShadow: '0 1px 2px 0 rgba(0,0,0,0.05)',\n transition: 'all 0.2s ease',\n display: 'flex',\n flexDirection: 'column',\n gap: '12px',\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.borderColor = platform.color;\n e.currentTarget.style.boxShadow =\n '0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -1px rgba(0,0,0,0.06)';\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.borderColor = '#e2e8f0';\n e.currentTarget.style.boxShadow = '0 1px 2px 0 rgba(0,0,0,0.05)';\n }}\n >\n <div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>\n <Logo size={24} />\n <h3 style={{ margin: 0, fontSize: '16px', fontWeight: '600', color: '#1e293b' }}>\n {platform.title}\n </h3>\n </div>\n\n <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>\n <div\n style={{\n width: '8px',\n height: '8px',\n borderRadius: '50%',\n backgroundColor: isEnabled ? '#22c55e' : '#cbd5e1',\n }}\n />\n <span\n style={{\n fontSize: '14px',\n fontWeight: '500',\n color: isEnabled ? '#15803d' : '#64748b',\n }}\n >\n {isEnabled ? 'Active' : 'Not configured'}\n </span>\n </div>\n\n {isEnabled && idValue && (\n <div\n style={{\n marginTop: '4px',\n padding: '6px 10px',\n backgroundColor: '#f8fafc',\n borderRadius: '4px',\n border: '1px solid #e2e8f0',\n fontSize: '13px',\n fontFamily: 'monospace',\n color: '#475569',\n whiteSpace: 'nowrap',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n }}\n >\n {idValue}\n </div>\n )}\n\n {showGtmOwnerNote && (\n <div\n style={{\n padding: '8px 10px',\n backgroundColor: '#fffbeb',\n border: '1px solid #fde68a',\n borderRadius: '6px',\n fontSize: '12px',\n color: '#92400e',\n lineHeight: '1.4',\n }}\n >\n ⚠️ Other pixels are also enabled. If they're inside your GTM container, they'll\n fire twice.\n </div>\n )}\n\n {showGtmActiveNote && (\n <div\n style={{\n padding: '8px 10px',\n backgroundColor: '#f0f9ff',\n border: '1px solid #bae6fd',\n borderRadius: '6px',\n fontSize: '12px',\n color: '#0369a1',\n lineHeight: '1.4',\n }}\n >\n ℹ️ GTM is active. Check your container before enabling — this pixel may already\n be loaded.\n </div>\n )}\n </div>\n );\n })}\n </div>\n </div>\n ) : (\n // ── Detail view ────────────────────────────────────────────────────────\n <div>\n <button\n onClick={() => setActivePlatform(null)}\n style={{\n background: 'none',\n border: 'none',\n color: '#3b82f6',\n cursor: 'pointer',\n display: 'flex',\n alignItems: 'center',\n gap: '6px',\n padding: 0,\n marginBottom: '24px',\n fontSize: '14px',\n fontWeight: '500',\n }}\n >\n <span style={{ fontSize: '16px' }}>←</span> Back to Overview\n </button>\n\n {(() => {\n const platform = PLATFORMS.find((p) => p.id === activePlatform)!;\n const isEnabled = values[platform.toggleKey] as boolean;\n const idValue = values[platform.idKey] as string;\n const Logo = PLATFORM_LOGOS[platform.id];\n\n return (\n <div style={{ maxWidth: '800px' }}>\n {/* Hero card */}\n <div\n style={{\n backgroundColor: '#fff',\n border: '1px solid #e2e8f0',\n borderRadius: '8px',\n padding: '32px',\n boxShadow: '0 1px 3px 0 rgba(0,0,0,0.1)',\n marginBottom: '24px',\n }}\n >\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: '16px',\n marginBottom: '16px',\n }}\n >\n <Logo size={40} />\n <h2 style={{ margin: 0, fontSize: '24px', fontWeight: '600', color: '#1e293b' }}>\n {platform.title}\n </h2>\n </div>\n\n <p\n style={{\n margin: '0 0 24px 0',\n fontSize: '16px',\n color: '#475569',\n lineHeight: '1.5',\n }}\n >\n {platform.tagline}\n </p>\n\n <RecommendationCallout platform={platform} values={values} />\n\n {/* Configuration block */}\n <div\n style={{\n marginTop: '32px',\n paddingTop: '32px',\n borderTop: '1px solid #e2e8f0',\n }}\n >\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n marginBottom: '20px',\n }}\n >\n <h3 style={{ margin: 0, fontSize: '16px', fontWeight: '600', color: '#1e293b' }}>\n Enable Tracking\n </h3>\n\n {/* Toggle switch */}\n <label style={{ display: 'flex', alignItems: 'center', cursor: 'pointer' }}>\n <div style={{ position: 'relative' }}>\n <input\n type=\"checkbox\"\n checked={isEnabled}\n onChange={(e) => handleToggle(platform.toggleKey, e.target.checked)}\n style={{ opacity: 0, width: 0, height: 0, position: 'absolute' }}\n />\n <div\n style={{\n width: '44px',\n height: '24px',\n backgroundColor: isEnabled ? '#22c55e' : '#cbd5e1',\n borderRadius: '9999px',\n transition: 'background-color 0.2s ease',\n position: 'relative',\n }}\n >\n <div\n style={{\n position: 'absolute',\n top: '2px',\n left: isEnabled ? '22px' : '2px',\n width: '20px',\n height: '20px',\n backgroundColor: '#fff',\n borderRadius: '50%',\n transition: 'left 0.2s ease',\n boxShadow: '0 1px 3px rgba(0,0,0,0.1)',\n }}\n />\n </div>\n </div>\n <span\n style={{\n marginLeft: '12px',\n fontSize: '14px',\n fontWeight: '500',\n color: isEnabled ? '#15803d' : '#64748b',\n }}\n >\n {isEnabled ? 'Active' : 'Disabled'}\n </span>\n </label>\n </div>\n\n <div\n style={{ opacity: isEnabled ? 1 : 0.6, transition: 'opacity 0.2s ease' }}\n >\n <label\n style={{\n display: 'block',\n marginBottom: '8px',\n fontSize: '14px',\n fontWeight: '500',\n color: '#475569',\n }}\n >\n {platform.idLabel}\n </label>\n <input\n type=\"text\"\n value={idValue}\n onChange={(e) => handleTextChange(platform.idKey, e.target.value)}\n disabled={!isEnabled}\n placeholder={platform.placeholder}\n style={{\n width: '100%',\n padding: '10px 12px',\n fontSize: '14px',\n border: '1px solid #cbd5e1',\n borderRadius: '6px',\n backgroundColor: isEnabled ? '#fff' : '#f1f5f9',\n color: isEnabled ? '#0f172a' : '#94a3b8',\n outline: 'none',\n boxSizing: 'border-box',\n transition: 'border-color 0.2s ease, box-shadow 0.2s ease',\n }}\n onFocus={(e) => {\n if (isEnabled) {\n e.target.style.borderColor = platform.color;\n e.target.style.boxShadow = `0 0 0 3px ${platform.color}20`;\n }\n }}\n onBlur={(e) => {\n e.target.style.borderColor = '#cbd5e1';\n e.target.style.boxShadow = 'none';\n }}\n />\n {!isEnabled && (\n <p style={{ margin: '8px 0 0 0', fontSize: '13px', color: '#94a3b8' }}>\n Enable this platform to enter your ID.\n </p>\n )}\n </div>\n </div>\n </div>\n\n {/* Setup guide accordions */}\n <h3 style={{ margin: '0 0 16px 0', fontSize: '18px', fontWeight: '600', color: '#1e293b' }}>\n Setup Guide\n </h3>\n <div\n style={{\n border: '1px solid #e2e8f0',\n borderRadius: '8px',\n overflow: 'hidden',\n }}\n >\n {HELP_CONTENT[platform.id].map((section, idx) => (\n <AccordionItem\n key={idx}\n title={section.title}\n content={section.content}\n isLast={idx === HELP_CONTENT[platform.id].length - 1}\n />\n ))}\n </div>\n </div>\n );\n })()}\n </div>\n )}\n </div>\n );\n}\n\n// ─── Admin Wrapper — loads/saves via plugin API ───────────────────────────────\n\ntype SaveStatus = 'idle' | 'saving' | 'saved' | 'error';\n\ntype TrackingSettingsPayload = TrackingValues & { settingsRevision: number };\n\ntype TrackingSaveResponse =\n | { ok: true; settingsRevision: number }\n | { ok: false; conflict: true; settingsRevision: number };\n\nexport function TrackingSettingsAdmin() {\n const api = usePluginAPI();\n const [values, setValues] = useState<TrackingValues | null>(null);\n const [saveStatus, setSaveStatus] = useState<SaveStatus>('idle');\n\n // Serialized save queue: only one request in-flight at a time.\n // If values change while a request is in-flight, the latest snapshot is held\n // in savePending and dispatched immediately when the current request settles.\n const saveTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n const saveInFlight = useRef(false);\n const savePending = useRef<TrackingValues | null>(null);\n // Server revision for optimistic concurrency (rejects out-of-order snapshots).\n const serverRevisionRef = useRef(0);\n // Latest form state for conflict retries (always matches what the user sees).\n const latestValuesRef = useRef<TrackingValues | null>(null);\n // Ignore late responses from superseded HTTP requests.\n const saveRequestId = useRef(0);\n\n useEffect(() => {\n void api\n .get<TrackingValues & { settingsRevision?: number }>('tracking/settings')\n .then((data) => {\n const { settingsRevision, ...rest } = data;\n serverRevisionRef.current = settingsRevision ?? 0;\n latestValuesRef.current = rest;\n setValues(rest);\n });\n }, [api]);\n\n const dispatchSave = useCallback(\n (snapshot: TrackingValues) => {\n const requestId = ++saveRequestId.current;\n saveInFlight.current = true;\n savePending.current = null;\n const payload: TrackingSettingsPayload = {\n ...snapshot,\n settingsRevision: serverRevisionRef.current,\n };\n void api\n .post<TrackingSaveResponse>('tracking/save', payload)\n .then((res) => {\n if (requestId !== saveRequestId.current) {\n return;\n }\n if (res.ok === false && res.conflict) {\n serverRevisionRef.current = res.settingsRevision;\n saveInFlight.current = false;\n const latest = latestValuesRef.current;\n if (latest) {\n dispatchSave(latest);\n }\n return;\n }\n if (!res.ok || typeof res.settingsRevision !== 'number') {\n saveInFlight.current = false;\n if (!savePending.current) savePending.current = snapshot;\n setSaveStatus('error');\n return;\n }\n serverRevisionRef.current = res.settingsRevision;\n saveInFlight.current = false;\n const queued = savePending.current;\n if (queued) {\n dispatchSave(queued);\n } else {\n setSaveStatus('saved');\n setTimeout(() => setSaveStatus('idle'), 2000);\n }\n })\n .catch(() => {\n if (requestId !== saveRequestId.current) {\n return;\n }\n saveInFlight.current = false;\n if (!savePending.current) savePending.current = snapshot;\n setSaveStatus('error');\n });\n },\n [api],\n );\n\n const handleChange = useCallback(\n (next: TrackingValues) => {\n latestValuesRef.current = next;\n setValues(next);\n setSaveStatus('saving');\n\n if (saveTimer.current) clearTimeout(saveTimer.current);\n saveTimer.current = setTimeout(() => {\n if (saveInFlight.current) {\n savePending.current = next;\n } else {\n dispatchSave(next);\n }\n }, 600);\n },\n [dispatchSave],\n );\n\n if (!values) {\n return (\n <div\n style={{\n padding: '48px 24px',\n textAlign: 'center',\n color: '#94a3b8',\n fontSize: '14px',\n }}\n >\n Loading tracking settings…\n </div>\n );\n }\n\n return (\n <div>\n {/* Save status banner */}\n {saveStatus !== 'idle' && (\n <div\n style={{\n position: 'sticky',\n top: 0,\n zIndex: 50,\n padding: '10px 24px',\n fontSize: '13px',\n fontWeight: '500',\n textAlign: 'center',\n backgroundColor:\n saveStatus === 'error'\n ? '#fef2f2'\n : saveStatus === 'saved'\n ? '#f0fdf4'\n : '#f8fafc',\n color:\n saveStatus === 'error'\n ? '#991b1b'\n : saveStatus === 'saved'\n ? '#166534'\n : '#64748b',\n borderBottom: '1px solid',\n borderColor:\n saveStatus === 'error'\n ? '#fca5a5'\n : saveStatus === 'saved'\n ? '#bbf7d0'\n : '#e2e8f0',\n }}\n >\n {saveStatus === 'saving' && 'Saving…'}\n {saveStatus === 'saved' && '✓ Changes saved'}\n {saveStatus === 'error' && '⚠ Error saving — please try again'}\n </div>\n )}\n\n <TrackingSettingsPage values={values} onChange={handleChange} />\n </div>\n );\n}\n","import React, { useState, useEffect, useCallback } from 'react';\nimport { usePluginAPI } from '../lib/usePluginAPI';\nimport type { LicenseData } from '../types';\n\ninterface AdvancedSettings {\n dniSwapNumber: string;\n dniScriptUrl: string;\n customHeadCode: string;\n customFooterCode: string;\n debug: boolean;\n}\n\nexport function SettingsPage() {\n const api = usePluginAPI();\n const [license, setLicense] = useState<LicenseData | null>(null);\n const [googleConnected, setGoogleConnected] = useState(false);\n const [activating, setActivating] = useState(false);\n const [message, setMessage] = useState('');\n const [licenseKeyInput, setLicenseKeyInput] = useState('');\n const [savingKey, setSavingKey] = useState(false);\n const [advanced, setAdvanced] = useState<AdvancedSettings>({\n dniSwapNumber: '',\n dniScriptUrl: '',\n customHeadCode: '',\n customFooterCode: '',\n debug: false,\n });\n const [savingAdvanced, setSavingAdvanced] = useState(false);\n\n // Load current license status, Google connection state, and advanced settings on mount\n useEffect(() => {\n void Promise.all([\n api.get<LicenseData>('license/status').then(setLicense),\n api.get<{ connected: boolean }>('google-oauth/status').then(r => setGoogleConnected(r.connected)),\n api.get<AdvancedSettings>('settings/load').then(setAdvanced),\n ]);\n }, [api]);\n\n // Handle OAuth redirect callback — persist the connected flag via route\n useEffect(() => {\n const params = new URLSearchParams(window.location.search);\n if (params.get('oauth_callback') !== '1' || params.get('google_connected') !== 'true') return;\n\n void api.post<{ connected: boolean }>('google-oauth/connected', {}).then(() => {\n setGoogleConnected(true);\n setMessage('Google Services connected successfully.');\n });\n }, [api]);\n\n const handleActivateLicense = useCallback(async () => {\n setActivating(true);\n setMessage('');\n try {\n const fresh = await api.get<LicenseData>('license/validate');\n setLicense(fresh);\n setMessage(fresh.isValid ? 'License activated.' : `License invalid: ${fresh.reason ?? 'unknown'}`);\n } catch {\n setMessage('Error validating license.');\n } finally {\n setActivating(false);\n }\n }, [api]);\n\n const handleSaveLicenseKey = useCallback(async () => {\n if (!licenseKeyInput.trim()) return;\n setSavingKey(true);\n setMessage('');\n try {\n await api.post('settings/save', { licenseKey: licenseKeyInput.trim() });\n setMessage('License key saved. Click Activate to validate.');\n setLicenseKeyInput('');\n } catch {\n setMessage('Error saving license key.');\n } finally {\n setSavingKey(false);\n }\n }, [api, licenseKeyInput]);\n\n const handleSaveAdvanced = useCallback(async () => {\n setSavingAdvanced(true);\n setMessage('');\n try {\n await api.post('settings/save', advanced);\n setMessage('Settings saved.');\n } catch {\n setMessage('Error saving settings.');\n } finally {\n setSavingAdvanced(false);\n }\n }, [api, advanced]);\n\n const handleGoogleOAuth = useCallback(async () => {\n try {\n const result = await api.post<{ authUrl?: string; error?: string }>('google-oauth/initiate', {});\n if (result.error) { setMessage(result.error); return; }\n if (result.authUrl) window.location.href = result.authUrl;\n } catch {\n setMessage('Error initiating Google connection.');\n }\n }, [api]);\n\n const licenseStatusLabel = (): string => {\n if (!license) return 'Not checked';\n if (license.isFallback) return 'Active (offline)';\n if (!license.isValid) return `Invalid — ${license.reason ?? 'unknown'}`;\n return `Active — ${license.tier ?? 'free'} tier`;\n };\n\n return (\n <div style={{ maxWidth: 640, padding: '1.5rem' }}>\n <h2>License &amp; Google</h2>\n\n <section style={{ marginBottom: '1.5rem' }}>\n <h3>License Key</h3>\n <p style={{ fontSize: '0.9rem', color: '#555', marginBottom: '0.75rem' }}>\n Paste your license key from the MosierData portal. Prefix: <code>qdsh_</code>\n </p>\n <div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>\n <input\n type=\"password\"\n value={licenseKeyInput}\n onChange={e => setLicenseKeyInput(e.target.value)}\n placeholder=\"qdsh_…\"\n style={{ flex: 1, padding: '0.5rem 0.75rem', border: '1px solid #d1d5db', borderRadius: 4, fontSize: '0.9rem' }}\n />\n <button onClick={() => void handleSaveLicenseKey()} disabled={savingKey || !licenseKeyInput.trim()}>\n {savingKey ? 'Saving…' : 'Save Key'}\n </button>\n </div>\n </section>\n\n <section style={{ marginBottom: '1.5rem' }}>\n <h3>License</h3>\n {license && (\n <p style={{ fontSize: '0.9rem', marginBottom: '0.75rem' }}>\n Status: <strong>{licenseStatusLabel()}</strong>\n </p>\n )}\n <button onClick={() => void handleActivateLicense()} disabled={activating}>\n {activating ? 'Activating…' : 'Activate License'}\n </button>\n </section>\n\n <section style={{ marginBottom: '1.5rem' }}>\n <h3>Google Services (Free Tier)</h3>\n {googleConnected ? (\n <p>Connected to Google Analytics &amp; Search Console ✅</p>\n ) : (\n <>\n <p style={{ fontSize: '0.9rem', color: '#555' }}>\n Grant read access to GA4 and Search Console. Tokens are stored securely on the MosierData backend.\n </p>\n <button onClick={() => void handleGoogleOAuth()}>Connect Google Services</button>\n </>\n )}\n </section>\n\n <section style={{ marginBottom: '1.5rem' }}>\n <h3>Call Tracking (AvidTrak DNI)</h3>\n <p style={{ fontSize: '0.9rem', color: '#555', marginBottom: '0.75rem' }}>\n Dynamic Number Insertion replaces a phone number on your site with a tracked number.\n </p>\n <label style={{ display: 'block', fontSize: '0.875rem', marginBottom: '0.25rem' }}>\n Phone Number to Swap\n </label>\n <input\n type=\"text\"\n value={advanced.dniSwapNumber}\n onChange={e => setAdvanced(a => ({ ...a, dniSwapNumber: e.target.value }))}\n placeholder=\"e.g. (555) 867-5309\"\n style={{ width: '100%', padding: '0.5rem 0.75rem', border: '1px solid #d1d5db', borderRadius: 4, fontSize: '0.9rem', marginBottom: '0.75rem', boxSizing: 'border-box' }}\n />\n <label style={{ display: 'block', fontSize: '0.875rem', marginBottom: '0.25rem' }}>\n AvidTrak Script URL\n </label>\n <input\n type=\"text\"\n value={advanced.dniScriptUrl}\n onChange={e => setAdvanced(a => ({ ...a, dniScriptUrl: e.target.value }))}\n placeholder=\"https://…\"\n style={{ width: '100%', padding: '0.5rem 0.75rem', border: '1px solid #d1d5db', borderRadius: 4, fontSize: '0.9rem', boxSizing: 'border-box' }}\n />\n </section>\n\n <section style={{ marginBottom: '1.5rem' }}>\n <h3>Custom Code</h3>\n <label style={{ display: 'block', fontSize: '0.875rem', marginBottom: '0.25rem' }}>\n Custom &lt;head&gt; Code\n </label>\n <textarea\n value={advanced.customHeadCode}\n onChange={e => setAdvanced(a => ({ ...a, customHeadCode: e.target.value }))}\n rows={4}\n style={{ width: '100%', padding: '0.5rem 0.75rem', border: '1px solid #d1d5db', borderRadius: 4, fontSize: '0.85rem', fontFamily: 'monospace', marginBottom: '0.75rem', boxSizing: 'border-box' }}\n />\n <label style={{ display: 'block', fontSize: '0.875rem', marginBottom: '0.25rem' }}>\n Custom Footer Code\n </label>\n <textarea\n value={advanced.customFooterCode}\n onChange={e => setAdvanced(a => ({ ...a, customFooterCode: e.target.value }))}\n rows={4}\n style={{ width: '100%', padding: '0.5rem 0.75rem', border: '1px solid #d1d5db', borderRadius: 4, fontSize: '0.85rem', fontFamily: 'monospace', boxSizing: 'border-box' }}\n />\n </section>\n\n <section style={{ marginBottom: '1.5rem' }}>\n <h3>Developer</h3>\n <label style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', fontSize: '0.9rem', cursor: 'pointer' }}>\n <input\n type=\"checkbox\"\n checked={advanced.debug}\n onChange={e => setAdvanced(a => ({ ...a, debug: e.target.checked }))}\n />\n Debug Mode\n </label>\n <p style={{ fontSize: '0.8rem', color: '#888', marginTop: '0.25rem' }}>\n Logs injected script activity to the browser console on every page load.\n </p>\n </section>\n\n <button onClick={() => void handleSaveAdvanced()} disabled={savingAdvanced}>\n {savingAdvanced ? 'Saving…' : 'Save Settings'}\n </button>\n\n {message && (\n <p style={{ fontSize: '0.9rem', color: '#444', marginTop: '1rem' }}>{message}</p>\n )}\n\n {license?.isValid && license.tier === 'free' && (\n <div style={{ marginTop: '2rem', padding: '1rem', background: '#f0f7ff', border: '1px solid #c0d8f5', borderRadius: 6 }}>\n <h4 style={{ margin: '0 0 0.5rem' }}>Upgrade to Professional</h4>\n <p style={{ margin: '0 0 0.75rem', fontSize: '0.9rem' }}>\n Unlock AI Call Transcription, Lead Scoring, and Call Recording.\n </p>\n <a href=\"https://quotedash.io/upgrade\" target=\"_blank\" rel=\"noreferrer\">Upgrade Now →</a>\n </div>\n )}\n </div>\n );\n}\n","import React, { useState, useEffect } from 'react';\nimport { AdminDashboard } from './Dashboard';\nimport { TrackingSettingsAdmin } from './TrackingSettings';\nimport { SettingsPage } from './Settings';\n\ntype Tab = 'dashboard' | 'tracking' | 'settings';\n\nconst TABS: { id: Tab; label: string }[] = [\n { id: 'dashboard', label: 'Marketing ROI' },\n { id: 'tracking', label: 'Tracking Pixels' },\n { id: 'settings', label: 'License & Google' },\n];\n\nfunction getTabFromHash(): Tab {\n const hash = window.location.hash.slice(1) as Tab;\n return hash === 'tracking' || hash === 'settings' ? hash : 'dashboard';\n}\n\nexport function PluginLayout() {\n const [activeTab, setActiveTab] = useState<Tab>(getTabFromHash);\n\n // Keep hash in sync when tab changes\n const handleTabChange = (tab: Tab) => {\n setActiveTab(tab);\n window.location.hash = tab === 'dashboard' ? '' : tab;\n };\n\n // Sync tab when hash changes externally (back/forward navigation)\n useEffect(() => {\n const onHashChange = () => setActiveTab(getTabFromHash());\n window.addEventListener('hashchange', onHashChange);\n return () => window.removeEventListener('hashchange', onHashChange);\n }, []);\n\n return (\n <div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>\n <div\n role=\"tablist\"\n style={{\n display: 'flex',\n borderBottom: '1px solid #e2e8f0',\n background: '#fff',\n padding: '0 1.25rem',\n gap: '0.25rem',\n flexShrink: 0,\n }}\n >\n {TABS.map(tab => (\n <button\n key={tab.id}\n role=\"tab\"\n aria-selected={activeTab === tab.id}\n onClick={() => handleTabChange(tab.id)}\n style={{\n padding: '0.75rem 1rem',\n border: 'none',\n borderBottom: activeTab === tab.id ? '2px solid #2563eb' : '2px solid transparent',\n background: 'none',\n cursor: 'pointer',\n fontSize: '0.875rem',\n fontWeight: activeTab === tab.id ? 600 : 400,\n color: activeTab === tab.id ? '#2563eb' : '#64748b',\n marginBottom: '-1px',\n }}\n >\n {tab.label}\n </button>\n ))}\n </div>\n\n <div style={{ flex: 1, overflow: 'auto' }}>\n {/* Always rendered to avoid remounting the dashboard iframe on tab switch */}\n <div style={{ display: activeTab === 'dashboard' ? 'block' : 'none', height: '100%' }}>\n <AdminDashboard onNavigateToSettings={() => handleTabChange('settings')} />\n </div>\n <div style={{ display: activeTab === 'tracking' ? 'block' : 'none' }}>\n <TrackingSettingsAdmin />\n </div>\n <div style={{ display: activeTab === 'settings' ? 'block' : 'none' }}>\n <SettingsPage />\n </div>\n </div>\n </div>\n );\n}\n","/**\n * Admin UI entry point — loaded in the browser by EmDash's admin panel.\n * Exports `pages` (keyed by path) and `widgets` (keyed by ID).\n * All plugin tabs (Dashboard, Tracking Pixels, License & Google) are rendered\n * inside PluginLayout so only one sidebar entry appears.\n */\nimport { PluginLayout } from './admin/PluginLayout';\n\nexport const pages = {\n '/dashboard': PluginLayout,\n};\n\nexport const widgets = {};\n"],"mappings":"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"}
@@ -9,23 +9,11 @@ function roiInsightsPlugin() {
9
9
  version: "1.0.0",
10
10
  entrypoint: "@mosierdata/emdash-plugin-analytics",
11
11
  adminEntry: "@mosierdata/emdash-plugin-analytics/admin",
12
- adminPages: [
13
- {
14
- path: "/dashboard",
15
- label: "Marketing ROI",
16
- icon: "chart"
17
- },
18
- {
19
- path: "/tracking",
20
- label: "Tracking Pixels",
21
- icon: "tracking"
22
- },
23
- {
24
- path: "/settings",
25
- label: "License & Google",
26
- icon: "settings"
27
- }
28
- ],
12
+ adminPages: [{
13
+ path: "/dashboard",
14
+ label: "Marketing ROI",
15
+ icon: "chart"
16
+ }],
29
17
  adminWidgets: []
30
18
  };
31
19
  }
@@ -1 +1 @@
1
- {"version":3,"file":"descriptor.mjs","names":[],"sources":["../src/descriptor.ts"],"sourcesContent":["import type { PluginDescriptor } from 'emdash';\n\n/**\n * Build-time plugin descriptor. Import this in astro.config.mjs.\n * Runs inside Vite — must be side-effect-free and cannot use runtime APIs.\n */\nexport function roiInsightsPlugin(): PluginDescriptor {\n return {\n id: 'roi-insights',\n version: '1.0.0',\n entrypoint: '@mosierdata/emdash-plugin-analytics',\n adminEntry: '@mosierdata/emdash-plugin-analytics/admin',\n adminPages: [\n { path: '/dashboard', label: 'Marketing ROI', icon: 'chart' },\n { path: '/tracking', label: 'Tracking Pixels', icon: 'tracking' },\n { path: '/settings', label: 'License & Google', icon: 'settings' }\n ],\n adminWidgets: []\n };\n}\n\nexport default roiInsightsPlugin;\n"],"mappings":";;;;;AAMA,SAAgB,oBAAsC;AACpD,QAAO;EACL,IAAI;EACJ,SAAS;EACT,YAAY;EACZ,YAAY;EACZ,YAAY;GACV;IAAE,MAAM;IAAc,OAAO;IAAiB,MAAM;IAAS;GAC7D;IAAE,MAAM;IAAa,OAAO;IAAmB,MAAM;IAAY;GACjE;IAAE,MAAM;IAAa,OAAO;IAAoB,MAAM;IAAY;GACnE;EACD,cAAc,EAAE;EACjB"}
1
+ {"version":3,"file":"descriptor.mjs","names":[],"sources":["../src/descriptor.ts"],"sourcesContent":["import type { PluginDescriptor } from 'emdash';\n\n/**\n * Build-time plugin descriptor. Import this in astro.config.mjs.\n * Runs inside Vite — must be side-effect-free and cannot use runtime APIs.\n */\nexport function roiInsightsPlugin(): PluginDescriptor {\n return {\n id: 'roi-insights',\n version: '1.0.0',\n entrypoint: '@mosierdata/emdash-plugin-analytics',\n adminEntry: '@mosierdata/emdash-plugin-analytics/admin',\n adminPages: [\n { path: '/dashboard', label: 'Marketing ROI', icon: 'chart' },\n ],\n adminWidgets: []\n };\n}\n\nexport default roiInsightsPlugin;\n"],"mappings":";;;;;AAMA,SAAgB,oBAAsC;AACpD,QAAO;EACL,IAAI;EACJ,SAAS;EACT,YAAY;EACZ,YAAY;EACZ,YAAY,CACV;GAAE,MAAM;GAAc,OAAO;GAAiB,MAAM;GAAS,CAC9D;EACD,cAAc,EAAE;EACjB"}