ReviewOS

also looking at this

stacks/pet-store

umbrella: pet-store storefront fixes (rolling)

#3
Merged glennmichael123 wants to merge umbrella/pet-store into main
586 files +38082 -7487

Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.

resources/views/index.stxmodified+31-0
Changes to resources/views/index.stx
@@ -57,9 +57,40 @@ const principles = [
5757 body: 'Every bag has a batch code. Scan it, and you see the farm, the processing date, and the third-party lab report.',
5858 },
5959]
60
61// Org + WebSite JSON-LD for the homepage. Search engines read this to
62// render the brand name + logo as a knowledge panel and to wire
63// sitelinks search if Google decides to offer it. Sticking to one
64// `@graph` keeps the two nodes referenceable from each other (the
65// website's `publisher` points at the org's `@id`). Kept on the
66// homepage only — that's where Google reads org-level signals from.
67const orgSchema = JSON.stringify({
68 '@context': 'https://schema.org',
69 '@graph': [
70 {
71 '@type': 'Organization',
72 '@id': 'https://petstore.example/#org',
73 'name': 'PetStore',
74 'url': 'https://petstore.example/',
75 'logo': 'https://petstore.example/favicons/favicon-512x512.png',
76 'sameAs': [],
77 },
78 {
79 '@type': 'WebSite',
80 '@id': 'https://petstore.example/#site',
81 'url': 'https://petstore.example/',
82 'name': 'PetStore',
83 'publisher': { '@id': 'https://petstore.example/#org' },
84 },
85 ],
86})
6087</script>
6188
6289@section('content')
90 {{-- Org + Website JSON-LD. Mirrors the inline placement of the
91 Product schema on /products/[slug]. STX's script rewriter passes
92 `application/ld+json` through untouched. --}}
93 <script type="application/ld+json">{!! orgSchema !!}</script>
6394 <!-- Hero -->
6495 <section class="overflow-hidden relative">
6596 <div class="grid gap-12 items-center md:grid-cols-2 mx-auto pb-24 pt-20 px-6 max-w-6xl">
resources/views/products.stxmodified+25-0
Changes to resources/views/products.stx
@@ -36,10 +36,35 @@ const products = db.query(`
3636 imageSrcset: productSrcset(p.image_url),
3737 badge: p.inventory_count < 30 ? 'Limited' : '',
3838}))
39
40// CollectionPage + ItemList JSON-LD. Tells Google this URL is a
41// category/collection rather than a single product, and provides
42// the catalogue order so the breadcrumb + search-result list show
43// items in the same order shoppers see them. Generated only when
44// there's at least one product so we don't emit an empty ItemList.
45const collectionSchema = products.length > 0 ? JSON.stringify({
46 '@context': 'https://schema.org',
47 '@type': 'CollectionPage',
48 'name': 'PetStore — All products',
49 'description': 'Single-ingredient, slow-dehydrated treats for dogs.',
50 'mainEntity': {
51 '@type': 'ItemList',
52 'numberOfItems': products.length,
53 'itemListElement': products.map((p, i) => ({
54 '@type': 'ListItem',
55 'position': i + 1,
56 'url': `/products/${p.slug}`,
57 'name': p.name,
58 })),
59 },
60}) : ''
3961</script>
4062
4163@section('content')
4264 <div class="mx-auto px-6 py-12 max-w-6xl">
65 @if (collectionSchema)
66 <script type="application/ld+json">{!! collectionSchema !!}</script>
67 @endif
4368
4469 <div class="flex flex-col gap-4 md:flex-row md:items-end md:justify-between mb-10">
4570 <div>
resources/views/products/[slug].stxmodified+12-0
Changes to resources/views/products/[slug].stx
@@ -156,6 +156,18 @@ const productSchema = product ? JSON.stringify({
156156 non-JS script types (application/ld+json) unchanged. --}}
157157 @if (productSchema)
158158 <script type="application/ld+json">{!! productSchema !!}</script>
159 {{-- BreadcrumbList: Home → Products → <product>. Google uses
160 this to render the trail under the title in search results
161 instead of the raw URL. --}}
162 <script type="application/ld+json">{!! JSON.stringify({
163 '@context': 'https://schema.org',
164 '@type': 'BreadcrumbList',
165 'itemListElement': [
166 { '@type': 'ListItem', position: 1, name: 'Home', item: '/' },
167 { '@type': 'ListItem', position: 2, name: 'Shop', item: '/products' },
168 { '@type': 'ListItem', position: 3, name: product.name, item: `/products/${product.slug}` },
169 ],
170 }) !!}</script>
159171 @endif
160172
161173 @if (!found)