Skip to content

Growth #4: SSR public report summaries + make analyze pages indexable#2

Merged
alexandermayes merged 2 commits into
mainfrom
growth/ssr-indexable-reports
Jul 17, 2026
Merged

Growth #4: SSR public report summaries + make analyze pages indexable#2
alexandermayes merged 2 commits into
mainfrom
growth/ssr-indexable-reports

Conversation

@alexandermayes

@alexandermayes alexandermayes commented Jul 16, 2026

Copy link
Copy Markdown
Owner

The highest-ceiling lever from the funnel analysis. Google is already ParseForge's #1 acquisition channel (631 submitters/30d), yet the ~1,500 monthly analyses generated zero organic search value: analyze pages were robots: index:false and fully client-rendered, with a placeholder Report ABC123 title.

Decision: index public reports, with safeguards + actively sitemap them

Warcraft Logs reports are ephemeral (users delete them / flip to private), so indexing blindly risks soft-404s dragging on domain quality. This PR indexes only reports that load publicly and neutralizes the risk:

Report state Behavior
Public (loads) SSR summary + index:true + canonical + listed in sitemap
Private noindex, client shows the private-log error
Deleted / missing real 404 (notFound()) so Google drops it
Transient WCL error noindex, client retries

Changes

  • lib/report-meta.ts (new) — getReportMeta(), a React-cache()d server fetch returning a discriminated ok/private/not_found/error result. Shares mapReportMeta() with the API route (mapping now in one place). Records each public report for the sitemap.
  • app/analyze/[reportCode]/page.tsx — real per-report <title>/description/OG; conditional indexing; alternates.canonical folds ?fight=&source= permutations; 404 on deleted.
  • app/analyze/[reportCode]/ReportSummary.tsx (new) — server-rendered crawlable content: headline, zone, an at-a-glance stat row (encounters, kills, raiders, combat time), encounter list with kill/wipe, class-colored roster, WCL source link. Below the unchanged interactive app.
  • lib/kv-cache.tsrecordRecentReport/getRecentReports back a Redis sorted set of public report codes (scored by last render). No new dependency; no-ops without shared Redis (local dev).
  • app/sitemap.ts — now async + hourly-revalidated; emits the most recent public reports alongside the static homepage + guides, so Google actively crawls them.
  • app/api/report/[code]/route.ts — refactored to reuse mapReportMeta.

Tradeoffs / notes

  • Analyze-page TTFB now includes a WCL meta fetch (cached 5 min in wclQuery + deduped per-request via cache()); under a WCL outage getReportMeta returns error and falls back to the client instead of hard-failing.
  • Sitemap report URLs only populate when shared Redis (Upstash) is configured — which it is on Vercel. Locally the sitemap is just homepage + guides.
  • Not build-verified locally (no node on PATH in the authoring env) — please confirm npm run build and spot-check on the preview: a public report (summary + indexable), a private one (noindex + error UX), a bogus code (404), and /sitemap.xml.
  • Independent of PR Growth: fix the two funnel leaks (cold-start + viral loop) + paste/error hardening #1 (different files) — no merge conflict.

🤖 Generated with Claude Code

https://claude.ai/code/session_01TG7Bgzws6wZi3hA1P8Gs2i

@vercel

vercel Bot commented Jul 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
parseforge Ready Ready Preview, Comment Jul 16, 2026 10:31pm

alexandermayes and others added 2 commits July 16, 2026 15:36
The biggest SEO lever from the funnel analysis: ~1,500 analyses/mo
generated zero organic search value because analyze pages were
noindex + fully client-rendered, while Google is already the #1
acquisition channel.

- lib/report-meta.ts: cached (React cache) server fetch returning a
  discriminated result (ok/private/not_found/error); shares mapReportMeta
  with the API route so the mapping lives in one place.
- page.tsx: real per-report <title>/description/OG from the actual report
  (was "Report ABC123"); index ONLY when the report loads publicly;
  canonical without query params folds ?fight=&source= variants; deleted
  reports return a real 404; private/errored stay noindex.
- ReportSummary.tsx: server-rendered, crawlable summary (headline, zone,
  encounter list, roster) below the unchanged interactive app.

Safeguards keep the ephemeral-content risk contained: private → noindex,
deleted → 404, transient WCL errors → noindex + client retry.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TG7Bgzws6wZi3hA1P8Gs2i
- kv-cache: recordRecentReport/getRecentReports back a Redis sorted set of
  public report codes (scored by last render). No new dependency; no-ops
  when shared Redis is absent (local dev).
- report-meta: record each public report when it server-renders.
- sitemap: emit the most recent public reports (hourly revalidate) so Google
  actively crawls them, alongside the static homepage + guides.
- ReportSummary: add an at-a-glance stat row (encounters, kills, raiders,
  combat time) to the crawlable summary.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TG7Bgzws6wZi3hA1P8Gs2i
@alexandermayes
alexandermayes force-pushed the growth/ssr-indexable-reports branch from 3ca165c to 0d34e92 Compare July 16, 2026 22:36
@vercel

vercel Bot commented Jul 16, 2026

Copy link
Copy Markdown

Deployment failed with the following error:

The provided GitHub repository does not contain the requested branch or commit reference. Please ensure the repository is not empty.

@alexandermayes
alexandermayes merged commit a4aaf75 into main Jul 17, 2026
1 of 2 checks passed
@alexandermayes
alexandermayes deleted the growth/ssr-indexable-reports branch July 17, 2026 18:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant