Skip to content

feat: SEO & AI visibility - #57

Merged
flvvius merged 2 commits into
mainfrom
feat/seo-batch-2
Jul 12, 2026
Merged

feat: SEO & AI visibility#57
flvvius merged 2 commits into
mainfrom
feat/seo-batch-2

Conversation

@flvvius

@flvvius flvvius commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features
    • The news feed is now available at the home page, with search, filtering, sorting, pagination, and infinite scrolling.
    • Added RSS and Google News sitemap feeds, with site-wide RSS discovery.
    • Improved event-page metadata, titles, descriptions, and structured-data branding.
  • Bug Fixes
    • Updated navigation, onboarding, quiz, bookmarks, and empty states to use the home page.
    • Event return navigation is more reliable when coming from the feed.
    • Romanian is now the default locale fallback.
  • Documentation
    • Added an SEO implementation and verification runbook.

@vercel

vercel Bot commented Jul 12, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
news Error Error Jul 12, 2026 1:54pm
news-web Ready Ready Preview, Comment Jul 12, 2026 1:54pm

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@flvvius, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 41 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c61a0228-e93e-408f-8de5-584bb978b79c

📥 Commits

Reviewing files that changed from the base of the PR and between 71a2a89 and c1c43e7.

📒 Files selected for processing (8)
  • apps/web/src/components/layout/MobileTabBar.tsx
  • apps/web/src/lib/syndication.ts
  • apps/web/src/routes/event.$slug.tsx
  • apps/web/src/routes/index.tsx
  • apps/web/src/routes/news-sitemap[.]xml.ts
  • apps/web/src/routes/rss[.]xml.ts
  • apps/web/src/routes/sitemap[.]xml.ts
  • packages/i18n/src/strings.ts

Walkthrough

The feed moves from /feed to /, with /feed becoming a 308 redirect. The root route gains interactive and crawlable feed rendering, event return navigation changes to session storage, SEO metadata is refined, and RSS and Google News sitemap endpoints are added.

Changes

Root feed and SEO migration

Layer / File(s) Summary
Root feed rendering and archive flow
apps/web/src/routes/index.tsx, apps/web/src/routes/feed.tsx
The root route now renders interactive and paginated feed views, while /feed redirects to / and preserves query parameters.
Event return navigation and metadata
apps/web/src/components/feed/event-card.tsx, apps/web/src/routes/event.$slug.tsx, apps/web/src/lib/seo.ts, apps/web/src/lib/i18n/getLocaleFromMatches.ts
Event return state uses session storage, event pages navigate back through browser history when applicable, and SEO titles, descriptions, locales, and logos are updated.
Syndication feeds and sitemap discovery
apps/web/src/routes/rss[.]xml.ts, apps/web/src/routes/news-sitemap[.]xml.ts, packages/backend/convex/events.ts, apps/web/src/routes/robots[.]txt.ts, apps/web/src/routes/sitemap[.]xml.ts, apps/web/src/routes/__root.tsx
RSS and Google News sitemap routes query syndication events and emit XML; robots, sitemap, and root metadata advertise the updated endpoints.
Navigation targets and redirect compatibility
apps/web/src/components/*, apps/web/src/routes/*.tsx, apps/web/src/router.tsx
Onboarding, navigation, quiz, activity, saved content, source pages, and fallback links now target /; related tests reflect the new destination.
SEO runbook and localized feed copy
docs/seo-batch-2-runbook.md, packages/i18n/src/strings.ts
The runbook records implementation and deployment checks, and Romanian feed metadata copy is revised.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant RootFeed
  participant Convex
  participant EventPage
  Browser->>RootFeed: Request /
  RootFeed->>Convex: Fetch feed events
  Convex-->>RootFeed: Return event data
  RootFeed-->>Browser: Render feed
  Browser->>EventPage: Open event link
  EventPage-->>Browser: Render event metadata and content
Loading

Possibly related PRs

  • flvvius/news#24: Overlaps with feed return handling in bookmark-button.tsx and event-card.tsx.
  • flvvius/news#28: Overlaps with navigation and /feed versus / routing changes.
  • flvvius/news#14: Overlaps with event-page SEO and metadata generation.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 19.44% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and directly reflects the main theme of the change set: SEO improvements and AI visibility enhancements.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/seo-batch-2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/web/src/components/header.tsx`:
- Line 27: Update the isActive checks in the desktop and mobile navigation to
special-case the root route: treat “/” as active only when currentPath exactly
equals “/”, while preserving the existing startsWith behavior for non-root
destinations. Apply the same logic in both navigation locations so the Feed tab
is not active on other routes.

In `@apps/web/src/components/layout/MobileTabBar.tsx`:
- Around line 25-29: Update the Feed tab’s isActive check in MobileTabBar so
archived root URLs such as /?page=2 are not treated as the live Feed
destination. Include the current search/query state in the check or otherwise
ensure selecting Feed navigates to a clean “/” URL while preserving the existing
event-path behavior.

In `@apps/web/src/routes/event`.$slug.tsx:
- Around line 282-303: Reset cameFromFeed to false at the beginning of the
useEffect keyed by slug, before checking sessionStorage, so each event
navigation starts with a clean value. Preserve the existing storage-based
set-to-true behavior and cleanup logic.

In `@apps/web/src/routes/index.tsx`:
- Around line 859-864: Add role="status" and aria-live="polite" to the
no-results section rendered by the isSearching && searchResults?.length === 0
condition, matching the existing search-state accessibility behavior. Keep the
current no-match content and styling unchanged.
- Around line 357-376: Update FeedArchive’s !archive branch to render an error
state with a retry affordance instead of the feed.loading status message. Use
the existing route revalidation/navigation mechanism and translation symbols
available in the surrounding component, while preserving the current archive
rendering when loaderData contains archive.

In `@apps/web/src/routes/news-sitemap`[.]xml.ts:
- Around line 12-34: Extract the duplicated escapeXml helper and
SyndicationEvent type into a shared syndication module, then update both
news-sitemap and rss route implementations to import and reuse those shared
symbols. Remove the local duplicate definitions while preserving their existing
behavior and type usage.

In `@apps/web/src/routes/rss`[.]xml.ts:
- Around line 17-24: Extract the duplicated escapeXml implementation into a
shared XML utility, preserving its current escaping order and behavior. Update
the rss[.]xml, sitemap[.]xml, news-sitemap[.]xml, and shareAssetsNode consumers
to import and reuse the shared escapeXml function, removing their local copies.

In `@packages/i18n/src/strings.ts`:
- Line 295: Update the feed.meta.title string to use an em dash (—) instead of
the regular hyphen (-), matching the formatting used by the other meta-title
keys and seo.siteTitle.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: e2bd5e86-839f-4ea6-8827-ee0c559f4a30

📥 Commits

Reviewing files that changed from the base of the PR and between c505c1f and 71a2a89.

⛔ Files ignored due to path filters (1)
  • apps/web/src/routeTree.gen.ts is excluded by !**/routeTree.gen.ts
📒 Files selected for processing (28)
  • apps/web/src/components/MiezOnboarding.test.tsx
  • apps/web/src/components/MiezOnboarding.tsx
  • apps/web/src/components/bookmark-button.tsx
  • apps/web/src/components/feed/event-card.tsx
  • apps/web/src/components/header.tsx
  • apps/web/src/components/layout/MobileTabBar.tsx
  • apps/web/src/components/quiz-hidden.test.tsx
  • apps/web/src/lib/feature-flags.ts
  • apps/web/src/lib/i18n/getLocaleFromMatches.ts
  • apps/web/src/lib/seo.ts
  • apps/web/src/router.tsx
  • apps/web/src/routes/__root.tsx
  • apps/web/src/routes/activitate.tsx
  • apps/web/src/routes/event.$slug.tsx
  • apps/web/src/routes/feed.tsx
  • apps/web/src/routes/index.tsx
  • apps/web/src/routes/llms[.]txt.ts
  • apps/web/src/routes/news-sitemap[.]xml.ts
  • apps/web/src/routes/quiz.tsx
  • apps/web/src/routes/robots[.]txt.ts
  • apps/web/src/routes/rss[.]xml.ts
  • apps/web/src/routes/salvate.tsx
  • apps/web/src/routes/sitemap[.]xml.ts
  • apps/web/src/routes/source.$sourceId.tsx
  • docs/seo-batch-2-runbook.md
  • packages/backend/convex/events.ts
  • packages/backend/convex/sitemap.ts
  • packages/i18n/src/strings.ts


const allLinks = [
{ to: "/feed", key: "tabs.feed", icon: Newspaper },
{ to: "/", key: "tabs.feed", icon: Newspaper },

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Feed tab is always active: currentPath.startsWith("/") matches every route.

Changing to from "/feed" to "/" at line 27 breaks the isActive check at lines 72 and 129. currentPath.startsWith(${to}/) becomes currentPath.startsWith("/"), which is true for every path. The feed tab will appear active on /quiz, /salvate, /activitate, /profil, and event pages simultaneously with the actual active tab.

As per path instructions for apps/web/**: verify proper React patterns and check TanStack Router usage patterns.

🐛 Proposed fix: special-case the root path in `isActive`
 const isActive =
-  currentPath === to || currentPath.startsWith(`${to}/`);
+  to === "/"
+    ? currentPath === "/"
+    : currentPath === to || currentPath.startsWith(`${to}/`);

Apply this fix at both line 72 (desktop nav) and line 129 (mobile nav).

Also applies to: 72-72, 129-129

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/components/header.tsx` at line 27, Update the isActive checks in
the desktop and mobile navigation to special-case the root route: treat “/” as
active only when currentPath exactly equals “/”, while preserving the existing
startsWith behavior for non-root destinations. Apply the same logic in both
navigation locations so the Feed tab is not active on other routes.

Source: Path instructions

Comment thread apps/web/src/components/layout/MobileTabBar.tsx
Comment thread apps/web/src/routes/event.$slug.tsx
Comment thread apps/web/src/routes/index.tsx
Comment thread apps/web/src/routes/index.tsx
Comment thread apps/web/src/routes/news-sitemap[.]xml.ts Outdated
Comment thread apps/web/src/routes/rss[.]xml.ts Outdated
Comment thread packages/i18n/src/strings.ts Outdated
- MobileTabBar: keep Feed tab inactive on the /?page=N archive and route
  taps back to a clean "/" instead of a scroll-to-top no-op
- event.$slug: reset cameFromFeed at the start of the slug effect so
  client-side jumps don't inherit the previous event's flag
- feed archive: show an error state with retry (router.invalidate) when
  the loader query throws, instead of a permanent loading spinner
- search no-results: add role=status/aria-live=polite
- dedupe escapeXml + SyndicationEvent into @/lib/syndication across the
  rss/sitemap/news-sitemap routes
- feed.meta.title: use em dash to match the other meta titles

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@flvvius
flvvius merged commit e24c456 into main Jul 12, 2026
4 of 5 checks passed
@flvvius
flvvius deleted the feat/seo-batch-2 branch July 12, 2026 13:54
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