feat(seo): robots.txt, richer sitemap, llms.txt, manifest, OG image, JSON-LD
Some checks are pending
CI/CD / test-and-build (push) Waiting to run
CI/CD / deploy (push) Blocked by required conditions

- Add app/robots.ts (allow crawl, disallow admin/api, sitemap URL)
- Expand sitemap to industries/[slug], accessories/[slug] and more static routes
- Add public/llms.txt (AI summary), site.webmanifest, 1200x630 og.jpg
- Layout: default OG/Twitter image, manifest, robots meta, Organization + WebSite JSON-LD
- Robot pages: canonical, OG image, Product + BreadcrumbList JSON-LD; home canonical

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Najjar 2026-07-06 13:45:38 +04:00
parent 873026a4a9
commit 60d50e15e4
10 changed files with 263 additions and 16 deletions

34
public/llms.txt Normal file
View File

@ -0,0 +1,34 @@
# YS Lootah Robotics
> YS Lootah Robotics is the United Arab Emirates destination for selected Unitree and Pudu robotics. We sell, configure, demonstrate and deploy humanoid, quadruped, delivery, hospitality and cleaning robots for businesses across Dubai and the UAE. Part of the Yousuf Saeed Lootah Investment Group.
## Important Pages
- [Home](https://yslootahrobotics.com/)
- [All robots](https://yslootahrobotics.com/robots/)
- [Configure your robot (3D)](https://yslootahrobotics.com/configure/)
- [Brands](https://yslootahrobotics.com/brands/)
- [Industries](https://yslootahrobotics.com/industries/)
- [Accessories](https://yslootahrobotics.com/accessories/)
- [Book a demo](https://yslootahrobotics.com/book-demo/)
- [Contact](https://yslootahrobotics.com/contact/)
- [About](https://yslootahrobotics.com/about/)
## What we offer
- Robot sales in the UAE: humanoids (Unitree G1, H1), quadrupeds (Unitree Go2, B2), and Pudu delivery, reception and cleaning robots.
- 3D configurator: choose persona, attire, colours and accessories, preview live, then request a UAE quotation.
- Live demonstrations and showroom visits in Dubai.
- On-site deployment, integration and support across the UAE.
- Accessories and add-ons for Unitree and Pudu platforms.
## Brands
- [Unitree Robotics](https://yslootahrobotics.com/robots/): humanoid and quadruped robots.
- [Pudu Robotics](https://yslootahrobotics.com/robots/): service, delivery and cleaning robots.
## Industries served
Hospitality, restaurants and cafes, hotels and resorts, shopping malls, healthcare, education, security and surveillance.
## Contact
- Email: info@yslootahrobotics.com
- Phone: +971 55 948 2728, +971 4 349 9319
- WhatsApp: https://wa.me/971559482728
- Location: Office 408, City Bay Business Center, Dubai, United Arab Emirates

BIN
public/og.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

16
public/site.webmanifest Normal file
View File

@ -0,0 +1,16 @@
{
"name": "YS Lootah Robotics",
"short_name": "Lootah Robotics",
"description": "The UAE destination for selected Unitree and Pudu robotics. Explore, configure and book a demo in Dubai.",
"start_url": "/",
"display": "standalone",
"background_color": "#0b0b0b",
"theme_color": "#0b0b0b",
"lang": "en",
"icons": [
{ "src": "/icon-192.webp", "sizes": "192x192", "type": "image/webp", "purpose": "any" },
{ "src": "/icon-512.webp", "sizes": "512x512", "type": "image/webp", "purpose": "any" },
{ "src": "/icon-192-maskable.webp", "sizes": "192x192", "type": "image/webp", "purpose": "maskable" },
{ "src": "/icon-512-maskable.webp", "sizes": "512x512", "type": "image/webp", "purpose": "maskable" }
]
}

View File

@ -1,6 +1,8 @@
import type { Metadata, Viewport } from "next"; import type { Metadata, Viewport } from "next";
import { ErrorBoundary } from "@/components/ErrorBoundary"; import { ErrorBoundary } from "@/components/ErrorBoundary";
import { I18nProvider } from "@/components/I18nProvider"; import { I18nProvider } from "@/components/I18nProvider";
import JsonLd from "@/components/JsonLd";
import { OG_IMAGE, siteName, organizationJsonLd, websiteJsonLd } from "@/lib/seo";
import "./globals.css"; import "./globals.css";
export const metadata: Metadata = { export const metadata: Metadata = {
@ -28,17 +30,32 @@ export const metadata: Metadata = {
], ],
openGraph: { openGraph: {
type: 'website', type: 'website',
url: '/',
title: 'YS Lootah Robotics UAE Destination for Unitree & Pudu Robotics', title: 'YS Lootah Robotics UAE Destination for Unitree & Pudu Robotics',
description: description:
'Advanced robotics. UAE access. Selected Unitree and Pudu solutions available through YS Lootah Robotics in Dubai.', 'Advanced robotics. UAE access. Selected Unitree and Pudu solutions available through YS Lootah Robotics in Dubai.',
locale: 'en_AE', locale: 'en_AE',
siteName: 'YS Lootah Robotics', siteName: 'YS Lootah Robotics',
images: [{ url: OG_IMAGE, width: 1200, height: 630, alt: siteName }],
}, },
twitter: { twitter: {
card: 'summary_large_image', card: 'summary_large_image',
title: 'YS Lootah Robotics UAE Destination for Unitree & Pudu Robotics', title: 'YS Lootah Robotics UAE Destination for Unitree & Pudu Robotics',
description: description:
'The UAEs dedicated destination for Unitree and Pudu Robotics sales, demo inquiries, and business deployment.', 'The UAEs dedicated destination for Unitree and Pudu Robotics sales, demo inquiries, and business deployment.',
images: [OG_IMAGE],
},
manifest: '/site.webmanifest',
robots: {
index: true,
follow: true,
googleBot: {
index: true,
follow: true,
'max-image-preview': 'large',
'max-snippet': -1,
'max-video-preview': -1,
},
}, },
}; };
@ -63,6 +80,7 @@ export default function RootLayout({
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@200;300;400;500;600;700&display=swap" rel="stylesheet" /> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@200;300;400;500;600;700&display=swap" rel="stylesheet" />
</head> </head>
<body suppressHydrationWarning> <body suppressHydrationWarning>
<JsonLd data={[organizationJsonLd, websiteJsonLd]} />
<ErrorBoundary> <ErrorBoundary>
<I18nProvider> <I18nProvider>
{children} {children}

View File

@ -35,6 +35,7 @@ export const metadata: Metadata = {
'robotics configuration UAE', 'robotics configuration UAE',
'robot sales and demo UAE', 'robot sales and demo UAE',
], ],
alternates: { canonical: '/' },
}; };
export default function HomePage() { export default function HomePage() {

16
src/app/robots.ts Normal file
View File

@ -0,0 +1,16 @@
import type { MetadataRoute } from 'next';
import { SITE_URL } from '@/lib/seo';
export default function robots(): MetadataRoute.Robots {
return {
rules: [
{
userAgent: '*',
allow: '/',
disallow: ['/admin', '/admin/', '/api/'],
},
],
sitemap: `${SITE_URL}/sitemap.xml`,
host: SITE_URL,
};
}

View File

@ -11,6 +11,8 @@ import { ProductSpecTable } from '@/components/robotics/ProductSpecTable';
import { ProductGallery } from '@/components/robotics/ProductGallery'; import { ProductGallery } from '@/components/robotics/ProductGallery';
import { MotionSection } from '@/components/ui/MotionSection'; import { MotionSection } from '@/components/ui/MotionSection';
import { ROBOTS, getRobotBySlug, BRANDS, CATEGORY_LABELS } from '@/data/robots'; import { ROBOTS, getRobotBySlug, BRANDS, CATEGORY_LABELS } from '@/data/robots';
import JsonLd from '@/components/JsonLd';
import { canonical, abs, productJsonLd, breadcrumbJsonLd } from '@/lib/seo';
type Params = { slug: string }; type Params = { slug: string };
@ -22,10 +24,17 @@ export async function generateMetadata({ params }: { params: Promise<Params> }):
const { slug } = await params; const { slug } = await params;
const robot = getRobotBySlug(slug); const robot = getRobotBySlug(slug);
if (!robot) return { title: 'Robot not found YS Lootah Robotics' }; if (!robot) return { title: 'Robot not found YS Lootah Robotics' };
const url = canonical(`/robots/${robot.slug}`);
const img = abs(robot.image);
const title = `${robot.brandLabel} ${robot.name} Available in Dubai | YS Lootah Robotics`;
const description = `${robot.shortDescription} Available through YS Lootah Robotics in Dubai request a price or book a live demo.`;
return { return {
title: `${robot.brandLabel} ${robot.name} Available in Dubai | YS Lootah Robotics`, title,
description: `${robot.shortDescription} Available through YS Lootah Robotics in Dubai request a price or book a live demo.`, description,
keywords: [robot.brandLabel, robot.name, 'Dubai', 'UAE', CATEGORY_LABELS[robot.category], 'robotics'], keywords: [robot.brandLabel, robot.name, 'Dubai', 'UAE', CATEGORY_LABELS[robot.category], 'robotics'],
alternates: { canonical: url },
openGraph: { type: 'website', url, title, description, images: [{ url: img, alt: `${robot.brandLabel} ${robot.name}` }] },
twitter: { card: 'summary_large_image', title, description, images: [img] },
}; };
} }
@ -37,8 +46,23 @@ export default async function RobotDetailPage({ params }: { params: Promise<Para
const brand = BRANDS[robot.brand]; const brand = BRANDS[robot.brand];
const related = ROBOTS.filter((r) => r.id !== robot.id && (r.brand === robot.brand || r.category === robot.category)).slice(0, 3); const related = ROBOTS.filter((r) => r.id !== robot.id && (r.brand === robot.brand || r.category === robot.category)).slice(0, 3);
const productLd = productJsonLd({
name: `${robot.brandLabel} ${robot.name}`,
description: robot.shortDescription,
image: robot.image,
brandName: brand.name,
path: `/robots/${robot.slug}`,
category: CATEGORY_LABELS[robot.category],
});
const crumbLd = breadcrumbJsonLd([
{ name: 'Home', path: '/' },
{ name: 'Robots', path: '/robots' },
{ name: robot.name, path: `/robots/${robot.slug}` },
]);
return ( return (
<> <>
<JsonLd data={[productLd, crumbLd]} />
<Navbar /> <Navbar />
<main style={{ paddingTop: 'clamp(6rem, 10vw, 8rem)', paddingBottom: 'clamp(4rem, 8vw, 6rem)' }}> <main style={{ paddingTop: 'clamp(6rem, 10vw, 8rem)', paddingBottom: 'clamp(4rem, 8vw, 6rem)' }}>

View File

@ -1,29 +1,48 @@
import { MetadataRoute } from 'next'; import { MetadataRoute } from 'next';
import { ROBOTS } from '@/data/robots'; import { ROBOTS } from '@/data/robots';
import { INDUSTRIES } from '@/data/industries';
const baseUrl = 'https://yslootahrobotics.com'; import { ACCESSORIES } from '@/data/accessories';
import { SITE_URL } from '@/lib/seo';
export default function sitemap(): MetadataRoute.Sitemap { export default function sitemap(): MetadataRoute.Sitemap {
const now = new Date(); const now = new Date();
const u = (path: string) => `${SITE_URL}${path}`;
const staticRoutes: MetadataRoute.Sitemap = [ const staticRoutes: MetadataRoute.Sitemap = [
{ url: `${baseUrl}/`, lastModified: now, changeFrequency: 'weekly', priority: 1 }, { url: u('/'), lastModified: now, changeFrequency: 'weekly', priority: 1 },
{ url: `${baseUrl}/robots/`, lastModified: now, changeFrequency: 'weekly', priority: 0.9 }, { url: u('/robots/'), lastModified: now, changeFrequency: 'weekly', priority: 0.9 },
{ url: `${baseUrl}/brands/`, lastModified: now, changeFrequency: 'weekly', priority: 0.8 }, { url: u('/configure/'), lastModified: now, changeFrequency: 'weekly', priority: 0.9 },
{ url: `${baseUrl}/industries/`, lastModified: now, changeFrequency: 'weekly', priority: 0.8 }, { url: u('/brands/'), lastModified: now, changeFrequency: 'weekly', priority: 0.8 },
{ url: `${baseUrl}/about/`, lastModified: now, changeFrequency: 'monthly', priority: 0.6 }, { url: u('/industries/'), lastModified: now, changeFrequency: 'weekly', priority: 0.8 },
{ url: `${baseUrl}/contact/`, lastModified: now, changeFrequency: 'monthly', priority: 0.7 }, { url: u('/accessories/'), lastModified: now, changeFrequency: 'weekly', priority: 0.7 },
{ url: `${baseUrl}/book-demo/`, lastModified: now, changeFrequency: 'monthly', priority: 0.8 }, { url: u('/book-demo/'), lastModified: now, changeFrequency: 'monthly', priority: 0.8 },
{ url: `${baseUrl}/configure/`, lastModified: now, changeFrequency: 'monthly', priority: 0.8 }, { url: u('/contact/'), lastModified: now, changeFrequency: 'monthly', priority: 0.7 },
{ url: `${baseUrl}/privacy-policy/`, lastModified: now, changeFrequency: 'yearly', priority: 0.3 }, { url: u('/about/'), lastModified: now, changeFrequency: 'monthly', priority: 0.6 },
{ url: `${baseUrl}/terms-of-service/`, lastModified: now, changeFrequency: 'yearly', priority: 0.3 }, { url: u('/bu-sunaidah/'), lastModified: now, changeFrequency: 'monthly', priority: 0.5 },
{ url: u('/privacy-policy/'), lastModified: now, changeFrequency: 'yearly', priority: 0.3 },
{ url: u('/terms-of-service/'), lastModified: now, changeFrequency: 'yearly', priority: 0.3 },
]; ];
const robotRoutes: MetadataRoute.Sitemap = ROBOTS.map((r) => ({ const robotRoutes: MetadataRoute.Sitemap = ROBOTS.map((r) => ({
url: `${baseUrl}/robots/${r.slug}/`, url: u(`/robots/${r.slug}/`),
lastModified: now, lastModified: now,
changeFrequency: 'monthly', changeFrequency: 'monthly',
priority: 0.7, priority: 0.7,
})); }));
return [...staticRoutes, ...robotRoutes]; const industryRoutes: MetadataRoute.Sitemap = INDUSTRIES.map((i) => ({
url: u(`/industries/${i.slug}/`),
lastModified: now,
changeFrequency: 'monthly',
priority: 0.6,
}));
const accessoryRoutes: MetadataRoute.Sitemap = ACCESSORIES.map((a) => ({
url: u(`/accessories/${a.slug}/`),
lastModified: now,
changeFrequency: 'monthly',
priority: 0.5,
}));
return [...staticRoutes, ...robotRoutes, ...industryRoutes, ...accessoryRoutes];
} }

10
src/components/JsonLd.tsx Normal file
View File

@ -0,0 +1,10 @@
// Renders a JSON-LD structured-data script tag.
export default function JsonLd({ data }: { data: object | object[] }) {
return (
<script
type="application/ld+json"
// JSON.stringify output is safe inside a script tag; escape '<' defensively.
dangerouslySetInnerHTML={{ __html: JSON.stringify(data).replace(/</g, '\\u003c') }}
/>
);
}

109
src/lib/seo.ts Normal file
View File

@ -0,0 +1,109 @@
// Central SEO config + structured-data builders for the shop.
export const SITE_URL = (process.env.NEXT_PUBLIC_SITE_URL || 'https://yslootahrobotics.com').replace(/\/$/, '');
// Canonical page URLs (trailingSlash: true in next.config).
export const canonical = (path = '/') => {
let p = path.startsWith('/') ? path : `/${path}`;
if (!p.endsWith('/')) p += '/';
return `${SITE_URL}${p}`;
};
// Absolute URL for assets (no trailing slash).
export const abs = (path = '') => `${SITE_URL}${path.startsWith('/') ? path : `/${path}`}`;
export const siteName = 'YS Lootah Robotics';
export const siteDescription =
'YS Lootah Robotics is the UAE sales destination for selected Unitree, AgiBot and Pudu robotics. Explore, configure and book a demo of humanoid, quadruped, delivery and cleaning robots across Dubai and the UAE.';
export const OG_IMAGE = abs('/og.jpg');
export const business = {
legalName: 'YS Lootah Robotics',
parent: 'Yousuf Saeed Lootah Investment Group',
phones: ['+971559482728', '+97143499319'],
email: 'info@yslootahrobotics.com',
whatsapp: 'https://wa.me/971559482728',
street: 'Office 408, City Bay Business Center',
city: 'Dubai',
country: 'AE',
};
export const organizationJsonLd = {
'@context': 'https://schema.org',
'@type': 'Organization',
'@id': `${SITE_URL}/#organization`,
name: business.legalName,
url: SITE_URL,
logo: abs('/apple-touch-icon.png'),
image: OG_IMAGE,
description: siteDescription,
parentOrganization: { '@type': 'Organization', name: business.parent },
email: business.email,
telephone: business.phones[0],
address: {
'@type': 'PostalAddress',
streetAddress: business.street,
addressLocality: business.city,
addressCountry: business.country,
},
areaServed: { '@type': 'Country', name: 'United Arab Emirates' },
contactPoint: [
{
'@type': 'ContactPoint',
telephone: business.phones[0],
email: business.email,
contactType: 'sales',
areaServed: 'AE',
availableLanguage: ['en', 'ar'],
},
],
};
export const websiteJsonLd = {
'@context': 'https://schema.org',
'@type': 'WebSite',
'@id': `${SITE_URL}/#website`,
url: SITE_URL,
name: siteName,
description: siteDescription,
publisher: { '@id': `${SITE_URL}/#organization` },
inLanguage: 'en',
};
export function breadcrumbJsonLd(items: { name: string; path: string }[]) {
return {
'@context': 'https://schema.org',
'@type': 'BreadcrumbList',
itemListElement: items.map((it, i) => ({
'@type': 'ListItem',
position: i + 1,
name: it.name,
item: canonical(it.path),
})),
};
}
export function productJsonLd(opts: {
name: string;
description: string;
image: string;
brandName: string;
path: string;
category?: string;
}) {
return {
'@context': 'https://schema.org',
'@type': 'Product',
name: opts.name,
description: opts.description,
image: opts.image.startsWith('http') ? opts.image : abs(opts.image),
category: opts.category,
url: canonical(opts.path),
brand: { '@type': 'Brand', name: opts.brandName },
offers: {
'@type': 'Offer',
availability: 'https://schema.org/InStock',
priceCurrency: 'AED',
seller: { '@id': `${SITE_URL}/#organization` },
url: canonical(opts.path),
},
};
}