Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/blog/src/components/navigation-wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { WebNavigation } from "@prisma-docs/ui/components/web-navigation";
import { useEffect, useState } from "react";
import { getUtmParams, hasUtmParams, type UtmParams } from "@/lib/utm";
import { getUtmParams, hasUtmParams, type UtmParams } from "@prisma-docs/ui/lib/utm";

interface Link {
text: string;
Expand Down
115 changes: 4 additions & 111 deletions apps/blog/src/components/utm-persistence.tsx
Original file line number Diff line number Diff line change
@@ -1,116 +1,9 @@
"use client";

import { useEffect } from "react";
import { usePathname, useRouter } from "next/navigation";
import { BLOG_PREFIX } from "@/lib/url";
import {
clearStoredUtmParams,
CONSOLE_HOST,
getUtmParams,
hasUtmParams,
syncUtmParams,
writeStoredUtmParams,
} from "@/lib/utm";
import { UtmPersistence as SharedUtmPersistence } from "@prisma-docs/ui/components/utm-persistence";

export function UtmPersistence() {
const pathname = usePathname();
const router = useRouter();

useEffect(() => {
const currentUtmParams = getUtmParams(
new URLSearchParams(window.location.search),
);

if (hasUtmParams(currentUtmParams)) {
writeStoredUtmParams(currentUtmParams);
return;
}

clearStoredUtmParams();
}, [pathname]);

useEffect(() => {
function handleClick(event: MouseEvent) {
if (event.defaultPrevented || event.button !== 0) {
return;
}

const anchor = (event.target as HTMLElement).closest<HTMLAnchorElement>(
"a[href]",
);

if (!anchor) {
return;
}

const href = anchor.getAttribute("href");

if (
!href ||
href.startsWith("#") ||
href.startsWith("mailto:") ||
href.startsWith("tel:") ||
anchor.hasAttribute("download")
) {
return;
}

const activeUtmParams = getUtmParams(
new URLSearchParams(window.location.search),
);

if (!hasUtmParams(activeUtmParams)) {
return;
}

const targetUrl = new URL(anchor.href, window.location.href);
const isInternalLink = targetUrl.origin === window.location.origin;
const isConsoleLink = targetUrl.hostname === CONSOLE_HOST;

if (!isInternalLink && !isConsoleLink) {
return;
}

if (!syncUtmParams(targetUrl, activeUtmParams)) {
return;
}

const nextHref = `${targetUrl.pathname}${targetUrl.search}${targetUrl.hash}`;
const isBlogPath =
targetUrl.pathname === BLOG_PREFIX ||
targetUrl.pathname.startsWith(`${BLOG_PREFIX}/`);
const isModifiedClick =
event.metaKey || event.ctrlKey || event.shiftKey || event.altKey;

if (
isInternalLink &&
isBlogPath &&
anchor.target !== "_blank" &&
!isModifiedClick
) {
const internalPathname =
targetUrl.pathname === BLOG_PREFIX
? "/"
: targetUrl.pathname.replace(
new RegExp(`^${BLOG_PREFIX}(?:/|$)`),
"/",
);
event.preventDefault();
router.push(
`${internalPathname}${targetUrl.search}${targetUrl.hash}`,
);
return;
}

anchor.setAttribute(
"href",
isInternalLink ? nextHref : targetUrl.toString(),
);
}

document.addEventListener("click", handleClick, true);
return () => document.removeEventListener("click", handleClick, true);
}, [router]);

return null;
return (
<SharedUtmPersistence storageKey="blog_utm_params" basePath="/blog" />
);
}
2 changes: 2 additions & 0 deletions apps/docs/src/components/provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { ReactNode } from "react";
import { source } from "@/lib/source";
import { TreeContextProvider } from "fumadocs-ui/contexts/tree";
import { TrackingProvider } from "@/components/tracking-provider";
import { UtmPersistence } from "@/components/utm-persistence";

const KAPA_INTEGRATION_ID = "1b51bb03-43cc-4ef4-95f1-93288a91b560";

Expand All @@ -29,6 +30,7 @@ export function Provider({ children }: { children: ReactNode }) {
}}
>
<TrackingProvider />
<UtmPersistence />
{children}
</RootProvider>
</KapaProvider>
Expand Down
9 changes: 9 additions & 0 deletions apps/docs/src/components/utm-persistence.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"use client";

import { UtmPersistence as SharedUtmPersistence } from "@prisma-docs/ui/components/utm-persistence";

export function UtmPersistence() {
return (
<SharedUtmPersistence storageKey="docs_utm_params" basePath="/docs" />
);
}
2 changes: 1 addition & 1 deletion apps/site/src/components/console-cta-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { useEffect, useState } from "react";
import { Button, type ButtonProps } from "@prisma/eclipse";
import { getUtmParams, hasUtmParams, type UtmParams } from "@/lib/utm";
import { getUtmParams, hasUtmParams, type UtmParams } from "@prisma-docs/ui/lib/utm";

interface ConsoleCtaButtonProps extends Omit<ButtonProps, "asChild"> {
consolePath: "/login" | "/sign-up";
Expand Down
2 changes: 1 addition & 1 deletion apps/site/src/components/navigation-wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
getUtmParams,
hasUtmParams,
type UtmParams,
} from "@/lib/utm";
} from "@prisma-docs/ui/lib/utm";

interface Link {
text: string;
Expand Down
117 changes: 9 additions & 108 deletions apps/site/src/components/utm-persistence.tsx
Original file line number Diff line number Diff line change
@@ -1,113 +1,14 @@
"use client";

import { useEffect } from "react";
import { usePathname, useRouter } from "next/navigation";
import {
clearStoredUtmParams,
CONSOLE_HOST,
getUtmParams,
hasUtmParams,
syncUtmParams,
writeStoredUtmParams,
} from "@/lib/utm";
import { UtmPersistence as SharedUtmPersistence } from "@prisma-docs/ui/components/utm-persistence";

export function UtmPersistence() {
const pathname = usePathname();
const router = useRouter();

useEffect(() => {
const currentUtmParams = getUtmParams(
new URLSearchParams(window.location.search),
);

if (hasUtmParams(currentUtmParams)) {
writeStoredUtmParams(currentUtmParams);
return;
}

clearStoredUtmParams();
}, [pathname]);

useEffect(() => {
function handleClick(event: MouseEvent) {
if (event.defaultPrevented || event.button !== 0) {
return;
}

const anchor = (event.target as HTMLElement).closest<HTMLAnchorElement>(
"a[href]",
);

if (!anchor) {
return;
}

const href = anchor.getAttribute("href");

if (
!href ||
href.startsWith("#") ||
href.startsWith("mailto:") ||
href.startsWith("tel:") ||
anchor.hasAttribute("download")
) {
return;
}

const activeUtmParams = getUtmParams(
new URLSearchParams(window.location.search),
);

if (!hasUtmParams(activeUtmParams)) {
return;
}
const PROXIED_PATHS = ["/docs", "/blog"];

const targetUrl = new URL(anchor.href, window.location.href);
const isInternalLink = targetUrl.origin === window.location.origin;
const isConsoleLink = targetUrl.hostname === CONSOLE_HOST;

if (!isInternalLink && !isConsoleLink) {
return;
}

const updated = syncUtmParams(targetUrl, activeUtmParams);

if (!updated) {
return;
}

const nextHref = `${targetUrl.pathname}${targetUrl.search}${targetUrl.hash}`;
const isModifiedClick =
event.metaKey || event.ctrlKey || event.shiftKey || event.altKey;

// Paths proxied to other apps via rewrites — must use full navigation
// so the server-side rewrite kicks in instead of client-side routing.
const isProxiedPath =
targetUrl.pathname === "/docs" ||
targetUrl.pathname.startsWith("/docs/") ||
targetUrl.pathname === "/blog" ||
targetUrl.pathname.startsWith("/blog/");

if (
isInternalLink &&
!isProxiedPath &&
anchor.target !== "_blank" &&
!isModifiedClick
) {
event.preventDefault();
router.push(nextHref);
return;
}

anchor.setAttribute(
"href",
isInternalLink ? nextHref : targetUrl.toString(),
);
}

document.addEventListener("click", handleClick, true);
return () => document.removeEventListener("click", handleClick, true);
}, [router]);

return null;
export function UtmPersistence() {
return (
<SharedUtmPersistence
storageKey="site_utm_params"
proxiedPaths={PROXIED_PATHS}
/>
);
}
101 changes: 0 additions & 101 deletions apps/site/src/lib/utm.ts

This file was deleted.

Loading
Loading