Skip to content

feat: direct links to individual contracts — /contract/<id> (#611)#612

Merged
rdmueller merged 2 commits into
LLM-Coding:mainfrom
raifdmueller:feat/611-contract-pages
Jun 12, 2026
Merged

feat: direct links to individual contracts — /contract/<id> (#611)#612
rdmueller merged 2 commits into
LLM-Coding:mainfrom
raifdmueller:feat/611-contract-pages

Conversation

@raifdmueller

@raifdmueller raifdmueller commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements #611: every semantic contract gets its own pre-rendered page at /contract/<id> plus a German variant at /de/contract/<id> — the same treatment anchors got in #597. Contracts were the only first-class catalog entity without their own URLs.

Implementation

Piece Change
render-contracts.js Emits per-contract detail fragments (EN + DE from contracts.json's existing German fields) alongside the overview fragment; overview cards link to detail pages; DE pages link DE anchor pages where they exist
prerender-routes.js New prerenderContractPages() mirroring the anchor loop: own title, description, canonical, og/twitter tags, hreflang pair per page via applyHead
SPA router /contract/<id> renders the contracts page scrolled to the highlighted card; document title comes from the card heading (the real contract title, not a de-kebab-cased slug); ids are regex-validated before entering DOM selectors
generate-sitemap.js +38 URLs (19 EN + 19 DE), total now 377
generate-jsonld.js Contracts join the DefinedTermSet as DefinedTerm entities with their /contract/<id> URLs (same GEO rationale as #579/#597)

Verification

  • TDD: 5 new router unit tests written first (render, title, scroll, unsafe-id guard, /de prefix) — 107/107 tests pass
  • Production build: 19 contract pages (EN + DE) pre-rendered; spot-checked dist/contract/specification/ and dist/de/contract/specification/ for title ("Specification" / "Spezifikation"), canonical, per-page og:url, lang="de", and full no-JS content
  • Lint + Prettier clean
  • App version bumped to 0.7.0

Closes #611

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Neue Funktionen

    • Vorgerenderte Contract-Detailseiten unter /contract/ und /de/contract/
    • Karten auf /contracts verlinken auf Detailseiten; SPA öffnet Contracts-Seite mit Fokus auf markierter Karte und korrektem Tab‑Titel
    • Generierung sprachabhängiger Detailfragmente (EN/DE)
  • Bug Fixes

    • Freigaben zeigen Preview des realen Contracts statt einer generischen Übersicht
  • SEO / Strukturierte Daten

    • Contracts in Sitemap (inkl. DE‑Varianten) und als DefinedTerm‑Entitäten ergänzt
  • Tests

    • Router‑Tests für Contract‑URLs und DE‑Routing erweitert
  • Sonstiges

    • App‑Version auf 0.7.0 erhöht

Contracts were the only first-class catalog entity without their own
URLs. Each of the 19 contracts now gets a real pre-rendered page:

- render-contracts.js emits per-contract detail fragments (EN + DE)
  alongside the existing /contracts overview fragment; overview cards
  link to the detail pages
- prerender-routes.js gains prerenderContractPages(), mirroring the
  anchor-pages loop from LLM-Coding#597: /contract/<id> plus /de/contract/<id>,
  each with own title, description, canonical URL, og/twitter tags and
  hreflang pair via applyHead
- the SPA router resolves /contract/<id> to the contracts page,
  scrolled to the highlighted card, with the real contract title (not
  a de-kebab-cased slug) in the document title; ids are
  regex-validated before entering selectors (5 new unit tests, TDD)
- sitemap gains the 38 contract URLs; contracts join the JSON-LD
  DefinedTermSet with their /contract/<id> URLs

App version 0.6.1 -> 0.7.0.

Closes LLM-Coding#611

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 2fe55cee-8d83-4ecd-a60c-4d7024c1c817

📥 Commits

Reviewing files that changed from the base of the PR and between 863740a and ef85607.

📒 Files selected for processing (2)
  • scripts/prerender-routes.js
  • scripts/render-contracts.js
🚧 Files skipped from review as they are similar to previous changes (2)
  • scripts/prerender-routes.js
  • scripts/render-contracts.js

Walkthrough

Die PR erzeugt pro Contract vorgerenderte EN/DE-Detailfragmente, pre-rendert statische Seiten /contract/<id> und /de/contract/<id>, erweitert Router-SPA-Handling zum Fokussieren/Hervorheben der Karte, und bringt Contracts in Sitemap und JSON‑LD DefinedTermSet; App-Version wurde auf 0.7.0 erhöht.

Changes

Direct Contract Links Feature

Layer / File(s) Summary
Contract Detail Fragment Generation
scripts/render-contracts.js
Erzeugt per-Contract-HTML-Fragmente unter website/public/docs/contracts/<id>.html und <id>.de.html; führt DETAIL_DIR/BASE/SAFE_ID_PATTERN ein, rendert wiederverwendbare Anchor‑Chips mit DE/EN-Fallbacks und verlinkt Contract-Titel auf /contract/<id>.
Static Page Pre-Rendering
scripts/prerender-routes.js
Neue Funktion prerenderContractPages(shell) lädt contracts.json, validiert IDs, prüft EN-Fragment-Existenz, und schreibt EN/DE Route‑Varianten via writeRouteVariant; main() ruft die Funktion auf und Logs zählen erzeugte/übersprungene Seiten.
SPA Route Handling and Navigation
website/src/utils/router.js, website/src/utils/router.test.js
Router erkennt /contract/:id (kebab-case), schließt Anchor-Modal, ruft /contracts-Handler, tracked Pageview und führt asynchron Titelsetzung, Scroll-zu-Card via data-contract-id und temporäre Hervorhebung durch; Tests prüfen Validierung, Titelsetzung, Scroll & /de/contract/:id.
SEO Integration: Sitemap and Structured Data
scripts/generate-sitemap.js, scripts/generate-jsonld.js
Sitemap erhält für jeden Contract EN- und DE-URL-Einträge; generate-jsonld.js lädt optional contracts.json und fügt Contract‑Einträge als DefinedTerm mit /contract/<id>-@id und description zum DefinedTermSet hinzu; Beschreibungstext erweitert auf „semantic anchors and semantic contracts“.
Changelog and Version Bump
docs/changelog.adoc, website/src/main.js
Changelog ergänzt 2026-06-12 Eintrag zu Direct Links to Contracts; APP_VERSION auf 0.7.0 gesetzt.

Sequence Diagram(s)

sequenceDiagram
  participant Browser as Browser/User
  participant Router as handleRoute()
  participant Modal as Anchor Modal
  participant ContractsHandler as /contracts handler
  participant DOM as Contract Card (data-contract-id)
  participant Display as Document Title / Scroll / Highlight

  Browser->>Router: navigate to /contract/:id
  Router->>Router: validate id (kebab-case)
  Router->>Modal: close if open
  Router->>ContractsHandler: invoke /contracts handler
  Router->>ContractsHandler: update pageview tracking
  Router->>DOM: find card element by data-contract-id
  Router->>Display: set document.title from card heading
  Router->>Display: scrollIntoView card
  Router->>Display: apply temporary blue ring highlight
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% 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 Der Pull-Request-Titel beschreibt prägnant die Hauptänderung: direkte Links zu einzelnen Verträgen über das URL-Muster /contract/. Der Titel ist konkret, aussagekräftig und zusammenhängend.
Linked Issues check ✅ Passed Alle Anforderungen aus Issue #611 sind erfolgreich umgesetzt: Vertragsdetail-Seiten (#597-muster), Router-Handling, Sitemap-Integration, JSON-LD-Unterstützung, Tests und Versionsbump.
Out of Scope Changes check ✅ Passed Alle Änderungen sind direkt auf die Umsetzung der verlinkten Issue #611 bezogen. Es gibt keine erkennbaren Out-of-Scope-Änderungen oder unzusammenhängenden Modifikationen.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

@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: 1

🤖 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 `@scripts/render-contracts.js`:
- Around line 138-149: Add a SAFE_ID_PATTERN constant using the same regex
/^[a-z0-9]+(?:-[a-z0-9]+)*$/ and validate every contract.id before using it in
path.join or template literals; in scripts/render-contracts.js (inside the loop
that calls renderContractDetail(contract, 'en'/'de')) and in
scripts/prerender-routes.js (the section that builds URLs/filenames from
contract.id) check SAFE_ID_PATTERN.test(contract.id) and skip or log and
continue for invalid IDs to avoid path-traversal, ensuring all writes and URL
templates only occur for IDs that pass the check.
🪄 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.yml

Review profile: CHILL

Plan: Pro

Run ID: 1ad9fafc-c2d9-4eb2-88ad-4304e5886254

📥 Commits

Reviewing files that changed from the base of the PR and between d2b835c and 863740a.

📒 Files selected for processing (8)
  • docs/changelog.adoc
  • scripts/generate-jsonld.js
  • scripts/generate-sitemap.js
  • scripts/prerender-routes.js
  • scripts/render-contracts.js
  • website/src/main.js
  • website/src/utils/router.js
  • website/src/utils/router.test.js

Comment thread scripts/render-contracts.js
Both render-contracts.js and prerenderContractPages() now check
contract.id against the same SAFE_ID_PATTERN the SPA router enforces,
and skip (with a warning) any entry that fails — defense-in-depth so a
malformed contracts.json entry cannot reach path.join() or URL
templates. Found by CodeRabbit review.

Refs LLM-Coding#611

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

feat: direct links to individual contracts (/contract/<id>)

2 participants