Add schema.org LearningResource markup and individual item landing pages#40
Add schema.org LearningResource markup and individual item landing pages#40jgrethe wants to merge 4 commits intoReproNim:mainfrom
Conversation
- Add schema.org/LearningResource JSON-LD (ItemList) injected into <head> on the browse page - Add individual item landing pages at /#/item/:id with per-item JSON-LD - Add react-router-dom with HashRouter for GitHub Pages compatibility - Extract shared useInventoryData hook - Card titles now link to item pages; external URL links unchanged - identifier field uses repronim.inventory:<id> CURIE format Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request implements routing for the ReproInventory frontend using react-router-dom, introducing a dedicated ItemPage and a useInventoryData hook for centralized data fetching. It also adds utilities to generate and inject Schema.org JSON-LD metadata for improved SEO. Feedback focuses on mitigating potential XSS vulnerabilities during script injection, ensuring type consistency for the last_updated field in the inventory data, and adopting safer TypeScript practices by avoiding the any type in catch blocks.
| "@context": "https://schema.org", | ||
| ...toSchemaOrgLearningResource(entry), | ||
| } | ||
| return JSON.stringify(ld) |
There was a problem hiding this comment.
The generated JSON-LD string is injected into a <script> tag in the document head. If any data field (such as description) contains the string </script>, it will cause the browser to prematurely terminate the script block, which can lead to broken pages or XSS vulnerabilities. Escaping the < character as \u003c prevents this issue.
| return JSON.stringify(ld) | |
| return JSON.stringify(ld).replace(/</g, '\\u003c') |
| "item": toSchemaOrgLearningResource(entry), | ||
| })), | ||
| } | ||
| return JSON.stringify(ld) |
| "Hands-on tutorial / notebooks" | ||
| ], | ||
| "open_dataset": true, | ||
| "last_updated": 2026, |
There was a problem hiding this comment.
The last_updated field is provided as a number (2026) here, while other entries in this file use strings (e.g., "2023"). For consistency and to align with the ReproInventoryEntry interface which defines this field as a string, it should be quoted. This also ensures better compatibility with schema.org consumers that expect string values for dates.
| "last_updated": 2026, | |
| "last_updated": "2026", |
| } catch (e: any) { | ||
| setError(e.message) |
There was a problem hiding this comment.
Using any in a catch block is discouraged in TypeScript. It is safer to use unknown (the default in newer TS versions) and check if the error is an instance of Error before accessing the message property. This prevents potential runtime errors if something other than an Error object is thrown.
| } catch (e: any) { | |
| setError(e.message) | |
| } catch (e) { | |
| setError(e instanceof Error ? e.message : String(e)) |
|
Hi @likeajumprope - I added some functionality to ReproInventory at the SOBP hackathon on a fork - support for schema.org markup and individual landing pages for the entries so that ReproInventory can be found via Google, etc. The indivudal pages have schema.org markup as well. This is running on Github pages from my fork: https://jgrethe.github.io/ReproInventory/ To see the schema.org markup - you can inspect the head element and it is the last item (json-ld content). Also, used this as a test scenario for working with Claude Code. |
Summary
LearningResourceJSON-LD markup (as anItemList) injected into<head>on the main browse page for improved SEO and structured data/#/item/:id(hash routing for GitHub Pages compatibility) with per-itemLearningResourceJSON-LDidentifierfield uses the item's landing page URL (e.g.https://repronim.github.io/ReproInventory/#/item/1)isAccessibleForFreeset totruefor all itemsreact-router-domfor client-side routinguseInventoryDatahook used by both pagesworkflow_dispatchtrigger to the deploy workflow for manual runsTest plan
/#/item/:idlanding pages<head>contains<script type="application/ld+json">with schema.org markup after data loads (check DevTools Elements panel)🤖 Generated with Claude Code