better tracking

This commit is contained in:
2026-02-10 01:52:41 -05:00
parent c1ab51a149
commit b63c62a732
23 changed files with 302 additions and 1 deletions

View File

@@ -24,6 +24,8 @@ function truncate(s: string, n: number) {
data-umami-event-target_id={targetId}
data-umami-event-placement={placement}
data-umami-event-target_url={`/blog/post/${post.slug}`}
data-umami-event-title={truncate(post.title || "", 160)}
data-umami-event-type="blog_post"
>
{post.featuredImageUrl ? <img src={post.featuredImageUrl} alt="" loading="lazy" /> : null}
<div class="blog-card-body">

View File

@@ -7,6 +7,12 @@ type Props = {
};
const { item, placement } = Astro.props;
function truncate(s: string, n: number) {
const t = (s || "").trim();
if (t.length <= n) return t;
return `${t.slice(0, Math.max(0, n - 1)).trimEnd()}…`;
}
const d = new Date(item.publishedAt);
const dateLabel = Number.isFinite(d.valueOf())
? d.toLocaleDateString(undefined, { year: "numeric", month: "short", day: "numeric" })
@@ -19,6 +25,15 @@ try {
} catch {
domain = "";
}
const umamiType =
item.source === "youtube"
? "video"
: item.source === "podcast"
? "podcast_episode"
: undefined;
const umamiTitle = umamiType ? truncate(item.title, 160) : undefined;
---
<a
@@ -30,6 +45,8 @@ try {
data-umami-event-target_id={targetId}
data-umami-event-placement={placement}
data-umami-event-target_url={item.url}
data-umami-event-title={umamiTitle}
data-umami-event-type={umamiType}
data-umami-event-domain={domain || "unknown"}
data-umami-event-source={item.source}
data-umami-event-ui_placement="content_card"

View File

@@ -57,6 +57,8 @@ const pages = wordpressPages(cache);
data-umami-event-target_id={`blog.index.pages.link.${p.slug}`}
data-umami-event-placement="blog.index.pages_preview"
data-umami-event-target_url={`/blog/page/${p.slug}`}
data-umami-event-title={p.title}
data-umami-event-type="blog_page"
>
{p.title}
</a>

View File

@@ -31,6 +31,8 @@ const pages = wordpressPages(cache);
data-umami-event-target_id={`blog.pages.link.${p.slug}`}
data-umami-event-placement="blog.pages.list"
data-umami-event-target_url={`/blog/page/${p.slug}`}
data-umami-event-title={p.title}
data-umami-event-type="blog_page"
>
{p.title}
</a>

View File

@@ -25,6 +25,12 @@ try {
} catch {
episodeDomain = "";
}
function truncate(s: string, n: number) {
const t = (s || "").trim();
if (t.length <= n) return t;
return `${t.slice(0, Math.max(0, n - 1)).trimEnd()}…`;
}
---
<BaseLayout
@@ -62,6 +68,8 @@ try {
data-umami-event-target_id={`podcast_detail.open.${episode.id}`}
data-umami-event-placement="podcast_detail"
data-umami-event-target_url={episode.url}
data-umami-event-title={truncate(episode.title, 160)}
data-umami-event-type="podcast_episode"
data-umami-event-domain={episodeDomain || "unknown"}
data-umami-event-source="podcast"
data-umami-event-ui_placement="podcast_detail"

View File

@@ -26,6 +26,12 @@ try {
} catch {
videoDomain = "";
}
function truncate(s: string, n: number) {
const t = (s || "").trim();
if (t.length <= n) return t;
return `${t.slice(0, Math.max(0, n - 1)).trimEnd()}…`;
}
---
<BaseLayout
@@ -63,6 +69,8 @@ try {
data-umami-event-target_id={`video_detail.watch.${video.id}`}
data-umami-event-placement="video_detail"
data-umami-event-target_url={video.url}
data-umami-event-title={truncate(video.title, 160)}
data-umami-event-type="video"
data-umami-event-domain={videoDomain || "unknown"}
data-umami-event-source="youtube"
data-umami-event-ui_placement="video_detail"

View File

@@ -0,0 +1,46 @@
import { readFile } from "node:fs/promises";
import path from "node:path";
import { describe, expect, it } from "vitest";
async function read(rel: string) {
return await readFile(path.join(process.cwd(), rel), "utf8");
}
describe("content link umami title/type attributes", () => {
it("adds title/type on outbound content cards (youtube/podcast) without forcing instagram", async () => {
const src = await read("src/components/ContentCard.astro");
expect(src).toContain("data-umami-event-title");
expect(src).toContain("data-umami-event-type");
expect(src).toContain('item.source === "youtube"');
expect(src).toContain('item.source === "podcast"');
});
it("adds title/type on video detail outbound CTA", async () => {
const src = await read("src/pages/videos/[id].astro");
expect(src).toContain('data-umami-event-title={truncate(video.title, 160)}');
expect(src).toContain('data-umami-event-type="video"');
});
it("adds title/type on podcast detail outbound CTA", async () => {
const src = await read("src/pages/podcast/[id].astro");
expect(src).toContain('data-umami-event-title={truncate(episode.title, 160)}');
expect(src).toContain('data-umami-event-type="podcast_episode"');
});
it("adds title/type on blog post cards and pages links", async () => {
const cardSrc = await read("src/components/BlogPostCard.astro");
expect(cardSrc).toContain('data-umami-event-type="blog_post"');
expect(cardSrc).toContain("data-umami-event-title");
const blogIndexSrc = await read("src/pages/blog/index.astro");
expect(blogIndexSrc).toContain('data-umami-event-type="blog_page"');
expect(blogIndexSrc).toContain("data-umami-event-title={p.title}");
const blogPagesSrc = await read("src/pages/blog/pages.astro");
expect(blogPagesSrc).toContain('data-umami-event-type="blog_page"');
expect(blogPagesSrc).toContain("data-umami-event-title={p.title}");
});
});