feat(seo): centralize sitewide JSON-LD in SEO.astro with Organization reviews
E2E Test Suite / Smoke Tests (P0) (push) Failing after 9m27s
E2E Test Suite / Critical User Journeys (push) Has been skipped
E2E Test Suite / API Integration Tests (push) Has been skipped
E2E Test Suite / Security Header Tests (push) Failing after 10m21s
E2E Test Suite / Mobile Device Tests (push) Failing after 10m27s
E2E Test Suite / Cross-Browser Regression (webkit) (push) Failing after 11m13s
E2E Test Suite / Cross-Browser Regression (firefox) (push) Failing after 12m5s
E2E Test Suite / Cross-Browser Regression (chromium) (push) Failing after 12m58s
E2E Test Suite / Destructive & Chaos Tests (push) Failing after 13m50s
E2E Test Suite / Form Interaction Tests (push) Failing after 14m43s
E2E Test Suite / Test Report Summary (push) Has been cancelled
Deploy to Production / Build & Verify (push) Waiting to run
Deploy to Production / Pre-Deploy Tests (push) Blocked by required conditions
Deploy to Production / Deploy to Railway (push) Has been cancelled
Deploy to Production / Deploy to Render (push) Has been cancelled
Deploy to Production / Deploy to VPS (PM2) (push) Has been cancelled
Deploy to Production / Deploy to Fly.io (push) Has been cancelled
Deploy to Production / Post-Deploy Verification (push) Has been cancelled
Deploy to Production / Notify on Failure (push) Has been cancelled
E2E Test Suite / Smoke Tests (P0) (push) Failing after 9m27s
E2E Test Suite / Critical User Journeys (push) Has been skipped
E2E Test Suite / API Integration Tests (push) Has been skipped
E2E Test Suite / Security Header Tests (push) Failing after 10m21s
E2E Test Suite / Mobile Device Tests (push) Failing after 10m27s
E2E Test Suite / Cross-Browser Regression (webkit) (push) Failing after 11m13s
E2E Test Suite / Cross-Browser Regression (firefox) (push) Failing after 12m5s
E2E Test Suite / Cross-Browser Regression (chromium) (push) Failing after 12m58s
E2E Test Suite / Destructive & Chaos Tests (push) Failing after 13m50s
E2E Test Suite / Form Interaction Tests (push) Failing after 14m43s
E2E Test Suite / Test Report Summary (push) Has been cancelled
Deploy to Production / Build & Verify (push) Waiting to run
Deploy to Production / Pre-Deploy Tests (push) Blocked by required conditions
Deploy to Production / Deploy to Railway (push) Has been cancelled
Deploy to Production / Deploy to Render (push) Has been cancelled
Deploy to Production / Deploy to VPS (PM2) (push) Has been cancelled
Deploy to Production / Deploy to Fly.io (push) Has been cancelled
Deploy to Production / Post-Deploy Verification (push) Has been cancelled
Deploy to Production / Notify on Failure (push) Has been cancelled
Move the base Organization + LocalBusiness and WebSite structured data out of BaseLayout's inline scripts into SEO.astro behind a new `base` prop, rendered once from BaseLayout's <head> so every route (incl. pages without a page-specific <SEO>) carries the canonical company schema exactly once. - Organization/LocalBusiness now includes aggregateRating + Review nodes built from the three real homepage testimonials (Ajay Pruthi, Dhirendra Singh, Dilip Raj — all 5-star, reviewCount 3, rating 5.0). - foundingDate 2022, telephone +91-9561417403, email admin@workroot.in, areaServed Bihar (AdministrativeArea) + India (Country). - Description keeps the canonical proof numbers identical to the site: 20+ projects, 11 districts, 50,000+ records, 99.9% uptime. - Reusable Service node now links provider via @id to #organization and serves Bihar/India; page-specific schema (Service/FAQ/WebPage/...) stays opt-in and is gated off the base instance to prevent duplication. - Homepage still emits a single FAQPage (from HomepageFaq) — no duplicate. - Add jsdom devDependency required by scripts/validate-schema.js. Verified: build succeeds; scripts/validate-schema.js passes (24 schemas, 0 issues, 0 warnings); rendered ld+json inspected on homepage, services, and schema-less pages (privacy/terms/cookies/404). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
be80d63f04
commit
dccc5d3b7f
+202
-11
@@ -9,6 +9,11 @@ interface BreadcrumbItem {
|
||||
|
||||
interface Props {
|
||||
type?: 'WebPage' | 'BlogPosting' | 'Article' | 'Service' | 'AboutPage' | 'ContactPage' | 'FAQPage';
|
||||
// When true this instance emits ONLY the site-wide base schema
|
||||
// (Organization + LocalBusiness and WebSite). Rendered once from
|
||||
// BaseLayout's <head> so every route carries the canonical company data,
|
||||
// independently of whether the page adds its own page-specific <SEO>.
|
||||
base?: boolean;
|
||||
breadcrumbs?: BreadcrumbItem[];
|
||||
article?: {
|
||||
author: string;
|
||||
@@ -29,11 +34,173 @@ interface Props {
|
||||
}>;
|
||||
}
|
||||
|
||||
const { type = 'WebPage', breadcrumbs, article, service, faq } = Astro.props;
|
||||
const { type = 'WebPage', base = false, breadcrumbs, article, service, faq } = Astro.props;
|
||||
|
||||
const baseUrl = 'https://workroot.in';
|
||||
const currentUrl = Astro.url.href;
|
||||
|
||||
const orgName = 'WorkRoot IT Solutions LLP';
|
||||
const orgId = `${baseUrl}/#organization`;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Site-wide base schema — Organization + LocalBusiness and WebSite.
|
||||
//
|
||||
// Emitted once per page via `<SEO base />` rendered from BaseLayout's <head>,
|
||||
// so every route (including pages without their own page-specific <SEO>)
|
||||
// carries WorkRoot's canonical structured data.
|
||||
//
|
||||
// `review` / `aggregateRating` are built from the three real, attributable
|
||||
// client testimonials shown on the homepage (see the `testimonials` array in
|
||||
// src/pages/index.astro). All three are 5★, so the aggregate rating is 5.0
|
||||
// over reviewCount = 3.
|
||||
//
|
||||
// Headline proof numbers in the description (20+ projects · 11 districts ·
|
||||
// 50,000+ records · 99.9% uptime) mirror the canonical set in
|
||||
// ProofNumbers.astro and the /services stats — keep them identical site-wide.
|
||||
// ---------------------------------------------------------------------------
|
||||
const testimonialReviews = [
|
||||
{
|
||||
author: 'Ajay Pruthi',
|
||||
body: 'WorkRoot IT Solutions is very professional and responsive. They built PLNR Financing platform with great attention to detail. The Angular + Spring Boot stack works flawlessly and their ongoing support is excellent.',
|
||||
},
|
||||
{
|
||||
author: 'Dhirendra Singh',
|
||||
body: 'The FPC Cart e-commerce platform delivered by WorkRoot exceeded our expectations. Built with Astro and Golang, it loads incredibly fast and handles our product catalog seamlessly. Their team understood our business needs perfectly.',
|
||||
},
|
||||
{
|
||||
author: 'Dilip Raj',
|
||||
body: 'WorkRoot delivered our University Management System for IIT Patna on time and within budget. The Next.js + Spring Boot architecture is robust and scalable. They are a reliable technology partner for complex enterprise projects.',
|
||||
},
|
||||
];
|
||||
|
||||
const reviewNodes = testimonialReviews.map((review) => ({
|
||||
"@type": "Review",
|
||||
"author": {
|
||||
"@type": "Person",
|
||||
"name": review.author
|
||||
},
|
||||
"reviewRating": {
|
||||
"@type": "Rating",
|
||||
"ratingValue": "5",
|
||||
"bestRating": "5",
|
||||
"worstRating": "1"
|
||||
},
|
||||
"reviewBody": review.body
|
||||
}));
|
||||
|
||||
const organizationSchema = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": ["Organization", "LocalBusiness"],
|
||||
"@id": orgId,
|
||||
"name": orgName,
|
||||
"alternateName": ["WorkRoot", "WorkRoot IT Solutions"],
|
||||
"url": baseUrl,
|
||||
"logo": {
|
||||
"@type": "ImageObject",
|
||||
"url": `${baseUrl}/logo.png`,
|
||||
"width": "512",
|
||||
"height": "512"
|
||||
},
|
||||
"image": `${baseUrl}/og-image.jpg`,
|
||||
"description": "WorkRoot IT Solutions LLP builds production-grade web, mobile, ERP, and government IT systems — 20+ projects delivered across 11 districts, 50,000+ records managed, and 99.9% uptime on mission-critical government deployments.",
|
||||
"foundingDate": "2022",
|
||||
"founder": {
|
||||
"@type": "Person",
|
||||
"name": "Ajay Ghanwat"
|
||||
},
|
||||
"address": {
|
||||
"@type": "PostalAddress",
|
||||
"streetAddress": "At Post Rajapur Shantinagar, Taluka Khatav",
|
||||
"addressLocality": "Satara",
|
||||
"addressRegion": "MH",
|
||||
"postalCode": "415503",
|
||||
"addressCountry": "IN"
|
||||
},
|
||||
"geo": {
|
||||
"@type": "GeoCoordinates",
|
||||
"latitude": "17.7733632",
|
||||
"longitude": "74.3309312"
|
||||
},
|
||||
"hasMap": "https://maps.app.goo.gl/9bZ5ez1pjbU74ATq5",
|
||||
"telephone": "+91-9561417403",
|
||||
"email": "admin@workroot.in",
|
||||
"contactPoint": [{
|
||||
"@type": "ContactPoint",
|
||||
"telephone": "+91-9561417403",
|
||||
"contactType": "customer service",
|
||||
"email": "admin@workroot.in",
|
||||
"availableLanguage": ["English", "Hindi", "Marathi"],
|
||||
"areaServed": "IN"
|
||||
}, {
|
||||
"@type": "ContactPoint",
|
||||
"telephone": "+91-9561417403",
|
||||
"contactType": "sales",
|
||||
"email": "admin@workroot.in",
|
||||
"availableLanguage": ["English", "Hindi", "Marathi"],
|
||||
"areaServed": "IN"
|
||||
}],
|
||||
"openingHoursSpecification": [
|
||||
{
|
||||
"@type": "OpeningHoursSpecification",
|
||||
"dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
|
||||
"opens": "10:00",
|
||||
"closes": "18:00"
|
||||
}
|
||||
],
|
||||
"priceRange": "$$",
|
||||
"currenciesAccepted": "INR",
|
||||
"paymentAccepted": "Cash, Credit Card, Bank Transfer",
|
||||
"sameAs": [
|
||||
"https://x.com/GhanwatAjay",
|
||||
"https://www.linkedin.com/company/workroot",
|
||||
"https://git.workroot.in/WorkRoot/",
|
||||
"https://www.facebook.com/profile.php?id=61576503897897"
|
||||
],
|
||||
"areaServed": [
|
||||
{
|
||||
"@type": "AdministrativeArea",
|
||||
"name": "Bihar"
|
||||
},
|
||||
{
|
||||
"@type": "Country",
|
||||
"name": "India"
|
||||
}
|
||||
],
|
||||
"serviceType": ["Web Development", "Mobile App Development", "ERP & Enterprise Systems", "Government IT Solutions"],
|
||||
"knowsAbout": ["Web Development", "Software Engineering", "Cloud Computing", "Artificial Intelligence", "Machine Learning", "Mobile Development"],
|
||||
"slogan": "Transforming Ideas Into Digital Reality",
|
||||
"aggregateRating": {
|
||||
"@type": "AggregateRating",
|
||||
"ratingValue": "5.0",
|
||||
"reviewCount": String(reviewNodes.length),
|
||||
"bestRating": "5",
|
||||
"worstRating": "1"
|
||||
},
|
||||
"review": reviewNodes
|
||||
};
|
||||
|
||||
const websiteSchema = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "WebSite",
|
||||
"name": orgName,
|
||||
"url": baseUrl,
|
||||
"description": "Professional IT services including web development, cloud solutions, and digital transformation for businesses.",
|
||||
"publisher": {
|
||||
"@type": "Organization",
|
||||
"@id": orgId,
|
||||
"name": orgName
|
||||
},
|
||||
"potentialAction": {
|
||||
"@type": "SearchAction",
|
||||
"target": {
|
||||
"@type": "EntryPoint",
|
||||
"urlTemplate": "https://workroot.in/blog?search={search_term_string}"
|
||||
},
|
||||
"query-input": "required name=search_term_string"
|
||||
},
|
||||
"inLanguage": "en-US"
|
||||
};
|
||||
|
||||
// Get page title and description from parent layout props
|
||||
const pageTitle = (Astro.props as any).title || 'WorkRoot IT Solutions';
|
||||
const pageDescription = (Astro.props as any).description || 'Professional IT services including web development, cloud solutions, and digital transformation for businesses.';
|
||||
@@ -90,7 +257,9 @@ const blogPostingSchema = article && type === 'BlogPosting' ? {
|
||||
"inLanguage": "en-US"
|
||||
} : null;
|
||||
|
||||
// Generate Service schema
|
||||
// Generate Service schema — reusable node; each service page passes its own
|
||||
// name/description via the `service` prop. Provider is linked by @id to the
|
||||
// site-wide Organization node so the structured-data graph stays connected.
|
||||
const serviceSchema = service ? {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Service",
|
||||
@@ -98,10 +267,20 @@ const serviceSchema = service ? {
|
||||
"description": service.description,
|
||||
"provider": {
|
||||
"@type": "Organization",
|
||||
"name": "WorkRoot IT Solutions",
|
||||
"@id": orgId,
|
||||
"name": orgName,
|
||||
"url": baseUrl
|
||||
},
|
||||
"areaServed": "Worldwide",
|
||||
"areaServed": [
|
||||
{
|
||||
"@type": "AdministrativeArea",
|
||||
"name": "Bihar"
|
||||
},
|
||||
{
|
||||
"@type": "Country",
|
||||
"name": "India"
|
||||
}
|
||||
],
|
||||
"serviceType": service.name
|
||||
} : null;
|
||||
|
||||
@@ -214,30 +393,42 @@ const contactPageSchema = type === 'ContactPage' ? {
|
||||
} : null;
|
||||
---
|
||||
|
||||
{breadcrumbSchema && (
|
||||
{/* Site-wide base schema — emitted only by the single `<SEO base />` rendered
|
||||
from BaseLayout's <head>, so it appears exactly once on every page. */}
|
||||
{base && (
|
||||
<script type="application/ld+json" set:html={JSON.stringify(organizationSchema)} />
|
||||
)}
|
||||
|
||||
{base && (
|
||||
<script type="application/ld+json" set:html={JSON.stringify(websiteSchema)} />
|
||||
)}
|
||||
|
||||
{/* Page-specific schema — never emitted by the base instance, so there is no
|
||||
duplication with the site-wide Organization/WebSite nodes above. */}
|
||||
{!base && breadcrumbSchema && (
|
||||
<script type="application/ld+json" set:html={JSON.stringify(breadcrumbSchema)} />
|
||||
)}
|
||||
|
||||
{blogPostingSchema && (
|
||||
{!base && blogPostingSchema && (
|
||||
<script type="application/ld+json" set:html={JSON.stringify(blogPostingSchema)} />
|
||||
)}
|
||||
|
||||
{serviceSchema && (
|
||||
{!base && serviceSchema && (
|
||||
<script type="application/ld+json" set:html={JSON.stringify(serviceSchema)} />
|
||||
)}
|
||||
|
||||
{webPageSchema && (
|
||||
{!base && webPageSchema && (
|
||||
<script type="application/ld+json" set:html={JSON.stringify(webPageSchema)} />
|
||||
)}
|
||||
|
||||
{faqPageSchema && (
|
||||
{!base && faqPageSchema && (
|
||||
<script type="application/ld+json" set:html={JSON.stringify(faqPageSchema)} />
|
||||
)}
|
||||
|
||||
{aboutPageSchema && (
|
||||
{!base && aboutPageSchema && (
|
||||
<script type="application/ld+json" set:html={JSON.stringify(aboutPageSchema)} />
|
||||
)}
|
||||
|
||||
{contactPageSchema && (
|
||||
{!base && contactPageSchema && (
|
||||
<script type="application/ld+json" set:html={JSON.stringify(contactPageSchema)} />
|
||||
)}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import Header from '../components/Header.astro';
|
||||
import Footer from '../components/Footer.astro';
|
||||
import SEO from '../components/SEO.astro';
|
||||
import Analytics from '../components/Analytics.astro';
|
||||
import PWAInstallPrompt from '../components/PWAInstallPrompt.astro';
|
||||
import WhatsAppFab from '../components/WhatsAppFab.astro';
|
||||
@@ -103,111 +104,14 @@ const fullTitle = title === 'Home' ? siteName : `${title} | ${siteName}`;
|
||||
<meta name="theme-color" media="(prefers-color-scheme: light)" content="#0891b2" />
|
||||
<meta name="theme-color" media="(prefers-color-scheme: dark)" content="#0f172a" />
|
||||
|
||||
<!-- JSON-LD Structured Data - Organization + LocalBusiness Schema -->
|
||||
<script type="application/ld+json" set:html={JSON.stringify({
|
||||
"@context": "https://schema.org",
|
||||
"@type": ["Organization", "LocalBusiness"],
|
||||
"name": "WorkRoot IT Solutions LLP",
|
||||
"alternateName": ["WorkRoot", "WorkRoot IT Solutions"],
|
||||
"url": "https://workroot.in",
|
||||
"logo": {
|
||||
"@type": "ImageObject",
|
||||
"url": "https://workroot.in/logo.png",
|
||||
"width": "512",
|
||||
"height": "512"
|
||||
},
|
||||
"image": "https://workroot.in/og-image.jpg",
|
||||
"description": "Professional IT services including web development, cloud solutions, and digital transformation for businesses.",
|
||||
"foundingDate": "2022",
|
||||
"founder": {
|
||||
"@type": "Person",
|
||||
"name": "Ajay Ghanwat"
|
||||
},
|
||||
"address": {
|
||||
"@type": "PostalAddress",
|
||||
"streetAddress": "At Post Rajapur Shantinagar, Taluka Khatav",
|
||||
"addressLocality": "Satara",
|
||||
"addressRegion": "MH",
|
||||
"postalCode": "415503",
|
||||
"addressCountry": "IN"
|
||||
},
|
||||
"geo": {
|
||||
"@type": "GeoCoordinates",
|
||||
"latitude": "17.7733632",
|
||||
"longitude": "74.3309312"
|
||||
},
|
||||
"hasMap": "https://maps.app.goo.gl/9bZ5ez1pjbU74ATq5",
|
||||
"telephone": "+91-9561417403",
|
||||
"email": "admin@workroot.in",
|
||||
"contactPoint": [{
|
||||
"@type": "ContactPoint",
|
||||
"telephone": "+91-9561417403",
|
||||
"contactType": "customer service",
|
||||
"email": "admin@workroot.in",
|
||||
"availableLanguage": ["English", "Hindi", "Marathi"],
|
||||
"areaServed": "Worldwide"
|
||||
}, {
|
||||
"@type": "ContactPoint",
|
||||
"telephone": "+91-9561417403",
|
||||
"contactType": "sales",
|
||||
"email": "admin@workroot.in",
|
||||
"availableLanguage": ["English", "Hindi", "Marathi"],
|
||||
"areaServed": "Worldwide"
|
||||
}],
|
||||
"openingHoursSpecification": [
|
||||
{
|
||||
"@type": "OpeningHoursSpecification",
|
||||
"dayOfWeek": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
|
||||
"opens": "10:00",
|
||||
"closes": "18:00"
|
||||
}
|
||||
],
|
||||
"priceRange": "$$",
|
||||
"currenciesAccepted": "INR",
|
||||
"paymentAccepted": "Cash, Credit Card, Bank Transfer",
|
||||
"sameAs": [
|
||||
"https://x.com/GhanwatAjay",
|
||||
"https://www.linkedin.com/company/workroot",
|
||||
"https://git.workroot.in/WorkRoot/",
|
||||
"https://www.facebook.com/profile.php?id=61576503897897"
|
||||
],
|
||||
"areaServed": {
|
||||
"@type": "GeoShape",
|
||||
"name": "Worldwide"
|
||||
},
|
||||
"serviceType": ["Web Development", "Mobile App Development", "AI & Machine Learning", "Cloud Services"],
|
||||
"knowsAbout": ["Web Development", "Software Engineering", "Cloud Computing", "Artificial Intelligence", "Machine Learning", "Mobile Development"],
|
||||
"slogan": "Transforming Ideas Into Digital Reality",
|
||||
"aggregateRating": {
|
||||
"@type": "AggregateRating",
|
||||
"ratingValue": "5.0",
|
||||
"reviewCount": "20",
|
||||
"bestRating": "5",
|
||||
"worstRating": "1"
|
||||
}
|
||||
})} />
|
||||
|
||||
<!-- JSON-LD Structured Data - WebSite Schema -->
|
||||
<script type="application/ld+json" set:html={JSON.stringify({
|
||||
"@context": "https://schema.org",
|
||||
"@type": "WebSite",
|
||||
"name": "WorkRoot IT Solutions LLP",
|
||||
"url": "https://workroot.in",
|
||||
"description": "Professional IT services including web development, cloud solutions, and digital transformation for businesses.",
|
||||
"publisher": {
|
||||
"@type": "Organization",
|
||||
"name": "WorkRoot IT Solutions LLP"
|
||||
},
|
||||
"potentialAction": {
|
||||
"@type": "SearchAction",
|
||||
"target": {
|
||||
"@type": "EntryPoint",
|
||||
"urlTemplate": "https://workroot.in/blog?search={search_term_string}"
|
||||
},
|
||||
"query-input": "required name=search_term_string"
|
||||
},
|
||||
"inLanguage": "en-US"
|
||||
})} />
|
||||
<!-- JSON-LD Structured Data — site-wide base schema (Organization +
|
||||
LocalBusiness and WebSite). Centralised in SEO.astro and emitted here
|
||||
in `base` mode so it appears exactly once on EVERY route, including
|
||||
pages that don't add their own page-specific <SEO> (404, privacy,
|
||||
terms, cookies, offline). Page-specific schema (Service, FAQ,
|
||||
BlogPosting, breadcrumbs, …) is opt-in via the slotted <SEO> each
|
||||
page renders into the head. -->
|
||||
<SEO base />
|
||||
|
||||
<!-- Preconnect to Google Fonts (must come before font stylesheet) -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
|
||||
Reference in New Issue
Block a user