Skip to content

chore: schema on breadcrumbs#60

Merged
hmbanan666 merged 3 commits intomainfrom
breadcrumbs
Dec 16, 2025
Merged

chore: schema on breadcrumbs#60
hmbanan666 merged 3 commits intomainfrom
breadcrumbs

Conversation

@hmbanan666
Copy link
Contributor

@hmbanan666 hmbanan666 commented Dec 16, 2025


Summary by cubic

Added Schema.org BreadcrumbList to page breadcrumbs to improve SEO and enable rich snippets. Also made the Person schema explicit in ProfileCard and updated LocalBusiness schema with priceRange and embedded aggregate rating.

  • New Features

    • Added BreadcrumbList schema on [pageSlug]/index, /points, and /reviews.
    • Each breadcrumb now includes position, name, and item URL for search engines.
  • Refactors

    • Centralized breadcrumb items as a computed value used by both UI and schema.
    • Added explicit '@type': 'Person' in ProfileCard schema.
    • Moved AggregateRating into LocalBusiness and added priceRange and reviewCount on [pageSlug].

Written for commit dd64b43. Summary will update automatically on new commits.

Summary by CodeRabbit

  • Improvements
    • Breadcrumb navigation now derives from a computed source and includes structured BreadcrumbList markup across multiple pages for clearer navigation and search visibility.
    • Schema.org metadata updated: Person entries now include explicit type information; business metadata consolidated and enriched with price range and nested aggregate review details for richer search presentation.

✏️ Tip: You can customize this high-level summary in your review settings.

@hmbanan666 hmbanan666 self-assigned this Dec 16, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 16, 2025

Caution

Review failed

The pull request is closed.

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Refactors breadcrumbs in three page components to derive items from a computed array and emit a schema.org BreadcrumbList; makes ProfileCard Person schema include explicit @type: "Person"; consolidates aggregateRating and priceRange into a single LocalBusiness schema on the page root component.

Changes

Cohort / File(s) Summary
Page components — Breadcrumb + Schema
apps/web-app/app/pages/[pageSlug]/index.vue, apps/web-app/app/pages/[pageSlug]/points.vue, apps/web-app/app/pages/[pageSlug]/reviews.vue
Adds a computed breadcrumbItems array, switches breadcrumb setup to useBreadcrumb().setItems(breadcrumbItems.value), and adds a schema.org BreadcrumbList built from breadcrumbItems.value (ListItem entries with position, name, item when present).
Profile component — Person schema
apps/web-app/app/components/user/ProfileCard.vue
Updates the structured data object to include explicit @type: 'Person' and uses quoted keys for name, image, and url while keeping the same value expressions (e.g., user.name, user.avatarUrl).
Page root — LocalBusiness schema consolidation
apps/web-app/app/pages/[pageSlug].vue
Replaces separate LocalBusiness/AggregateRating calls with a single LocalBusiness/Organization schema that includes priceRange: '₽' and nests rating fields under aggregateRating (ratingValue, ratingCount, reviewCount, bestRating, worstRating); updates URL source to use page.value?.slug.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Inspect ProfileCard.vue schema keys and quoting for correctness.
  • Verify breadcrumb item ordering, URLs, and that useBreadcrumb().setItems receives the intended array shape.
  • Confirm LocalBusiness.aggregateRating shape matches schema.org expectations and no duplicate schema registrations remain.

Possibly related PRs

Poem

🥕 I hop through crumbs with tiny cheer,
I label folks so they appear.
ListItems line up, neat and true,
Ratings tucked and price in view.
A rabbit cheers — the schema's clear. 🐰

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding schema.org markup to breadcrumbs across multiple pages, plus schema improvements to ProfileCard.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 44354f8 and dd64b43.

📒 Files selected for processing (1)
  • apps/web-app/app/pages/[pageSlug].vue (1 hunks)

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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
apps/web-app/app/pages/[pageSlug]/index.vue (1)

37-47: Schema.org requires absolute URLs for breadcrumb items.

Same issue as in reviews.vue: the item property should contain absolute URLs rather than relative paths for proper structured data validation.

Apply the same fix as suggested in the reviews.vue file to construct absolute URLs using app.url.

apps/web-app/app/pages/[pageSlug]/points.vue (1)

48-58: Schema.org requires absolute URLs for breadcrumb items.

Same issue as in reviews.vue and index.vue: the item property should contain absolute URLs rather than relative paths for proper structured data validation.

Apply the same fix as suggested in the reviews.vue file to construct absolute URLs using app.url.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b216a5f and 454e1d5.

📒 Files selected for processing (4)
  • apps/web-app/app/components/user/ProfileCard.vue (1 hunks)
  • apps/web-app/app/pages/[pageSlug]/index.vue (2 hunks)
  • apps/web-app/app/pages/[pageSlug]/points.vue (2 hunks)
  • apps/web-app/app/pages/[pageSlug]/reviews.vue (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: build
🔇 Additional comments (4)
apps/web-app/app/pages/[pageSlug]/reviews.vue (1)

56-72: LGTM! Breadcrumb items are well-structured.

The computed breadcrumb items array is cleanly organized with proper labels, icons, and routing.

apps/web-app/app/components/user/ProfileCard.vue (1)

38-42: LGTM! Schema.org Person definition is correctly structured.

The explicit @type and quoted keys align with schema.org conventions, and the url property correctly uses an absolute URL.

apps/web-app/app/pages/[pageSlug]/index.vue (1)

22-33: LGTM! Breadcrumb items are well-structured.

The computed breadcrumb items array is cleanly organized for the main page view.

apps/web-app/app/pages/[pageSlug]/points.vue (1)

28-44: LGTM! Breadcrumb items are well-structured.

The computed breadcrumb items array is cleanly organized for the points page view.

Comment on lines +76 to +86
useSchemaOrg([
defineBreadcrumb({
'@type': 'BreadcrumbList',
'itemListElement': breadcrumbItems.value.map((item, index) => ({
'@type': 'ListItem',
'position': index + 1,
'name': item.label,
'item': item.to ?? undefined,
})),
}),
])
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Schema.org requires absolute URLs for breadcrumb items.

The item property in BreadcrumbList should contain absolute URLs (e.g., https://example.com/page) rather than relative paths. This impacts SEO and structured data validation by search engines.

Consider constructing absolute URLs using the app configuration:

+const { app } = useAppConfig()
+
 useSchemaOrg([
   defineBreadcrumb({
     '@type': 'BreadcrumbList',
     'itemListElement': breadcrumbItems.value.map((item, index) => ({
       '@type': 'ListItem',
       'position': index + 1,
       'name': item.label,
-      'item': item.to ?? undefined,
+      'item': item.to ? `${app.url}${item.to}` : undefined,
     })),
   }),
 ])

Alternatively, omit the item property entirely when to is undefined:

 useSchemaOrg([
   defineBreadcrumb({
     '@type': 'BreadcrumbList',
     'itemListElement': breadcrumbItems.value.map((item, index) => ({
       '@type': 'ListItem',
       'position': index + 1,
       'name': item.label,
-      'item': item.to ?? undefined,
+      ...(item.to && { 'item': `${app.url}${item.to}` }),
     })),
   }),
 ])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
useSchemaOrg([
defineBreadcrumb({
'@type': 'BreadcrumbList',
'itemListElement': breadcrumbItems.value.map((item, index) => ({
'@type': 'ListItem',
'position': index + 1,
'name': item.label,
'item': item.to ?? undefined,
})),
}),
])
const { app } = useAppConfig()
useSchemaOrg([
defineBreadcrumb({
'@type': 'BreadcrumbList',
'itemListElement': breadcrumbItems.value.map((item, index) => ({
'@type': 'ListItem',
'position': index + 1,
'name': item.label,
'item': item.to ? `${app.url}${item.to}` : undefined,
})),
}),
])
🤖 Prompt for AI Agents
In apps/web-app/app/pages/[pageSlug]/reviews.vue around lines 76 to 86, the
BreadcrumbList currently sets each item's "item" to the raw `to` (which may be a
relative path); change this to produce absolute URLs or omit the property when
`to` is undefined: obtain the site base URL from runtime config (e.g.
useRuntimeConfig().public.siteUrl or equivalent), then map breadcrumbItems so
that for entries with a `to` value you build an absolute URL (e.g. new
URL(item.to, base).toString()) and assign that to `item`, and for entries
without `to` either set `item` to undefined or omit the `item` key entirely so
only absolute URLs appear in the Schema.org BreadcrumbList.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 4 files

Prompt for AI agents (all 1 issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="apps/web-app/app/pages/[pageSlug]/index.vue">

<violation number="1" location="apps/web-app/app/pages/[pageSlug]/index.vue:35">
P2: The `computed()` property&#39;s reactivity is not being used. `breadcrumbItems.value` is read once during setup, so changes to `page` or `mainCategory` won&#39;t update the breadcrumbs. Consider using `watch` or `watchEffect` if reactivity is needed, or remove `computed()` if it&#39;s not.</violation>
</file>

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

},
])

useBreadcrumb().setItems(breadcrumbItems.value)
Copy link

@cubic-dev-ai cubic-dev-ai bot Dec 16, 2025

Choose a reason for hiding this comment

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

P2: The computed() property's reactivity is not being used. breadcrumbItems.value is read once during setup, so changes to page or mainCategory won't update the breadcrumbs. Consider using watch or watchEffect if reactivity is needed, or remove computed() if it's not.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web-app/app/pages/[pageSlug]/index.vue, line 35:

<comment>The `computed()` property&#39;s reactivity is not being used. `breadcrumbItems.value` is read once during setup, so changes to `page` or `mainCategory` won&#39;t update the breadcrumbs. Consider using `watch` or `watchEffect` if reactivity is needed, or remove `computed()` if it&#39;s not.</comment>

<file context>
@@ -32,6 +32,20 @@ useBreadcrumb().setItems([
   },
 ])
 
+useBreadcrumb().setItems(breadcrumbItems.value)
+
+useSchemaOrg([
</file context>
Suggested change
useBreadcrumb().setItems(breadcrumbItems.value)
watch(breadcrumbItems, (items) => useBreadcrumb().setItems(items), { immediate: true })
Fix with Cubic

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
apps/web-app/app/pages/[pageSlug].vue (2)

88-95: Consider handling undefined rating data and clarifying ratingCount vs reviewCount.

Two observations:

  1. ratingCount and reviewCount are both assigned page.value?.reviewsCount. Per Schema.org, these have different semantics—ratingCount is total ratings, reviewCount is total reviews. If users can rate without writing a review, these values may differ. If your data model doesn't distinguish them, this is fine.

  2. If page.value?.rating or page.value?.reviewsCount is undefined, the schema will emit undefined values, which may produce invalid structured data. Consider conditionally including aggregateRating only when the data exists:

 useSchemaOrg([
   defineLocalBusiness({
     name: page.value?.title,
     url: `${app.url}/${params.pageSlug}`,
     priceRange: '₽',
-    aggregateRating: {
+    aggregateRating: page.value?.rating ? {
       '@type': 'AggregateRating',
       'ratingValue': page.value?.rating,
       'ratingCount': page.value?.reviewsCount,
       'reviewCount': page.value?.reviewsCount,
       'bestRating': 5,
       'worstRating': 1,
-    },
+    } : undefined,
   }),
 ])

87-87: Verify priceRange value aligns with Schema.org recommendations.

The priceRange value '₽' is the currency symbol but doesn't indicate relative price level. Schema.org typically expects indicators like '$', '$$', '$$$' to convey cheap/moderate/expensive. Consider using '₽₽' or similar if you want to convey a price tier, or confirm this is the intended representation for your use case.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 454e1d5 and 44354f8.

📒 Files selected for processing (1)
  • apps/web-app/app/pages/[pageSlug].vue (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build

@hmbanan666 hmbanan666 merged commit 7713518 into main Dec 16, 2025
1 of 2 checks passed
@hmbanan666 hmbanan666 deleted the breadcrumbs branch December 16, 2025 09:43
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