feat(seo): fix titles, canonicals, share tags and soft 404s
CI/CD / test-and-build (pull_request) Canceled after 0s
CI/CD / deploy (pull_request) Canceled after 0s

- Titles carried the brand twice (page title plus layout template) and Pudu
  items read "Pudu PUDU". Pages now go through pageMetadata() and displayName().
- /configure, /privacy-policy and /terms-of-service reused the homepage title.
- Eight pages had no canonical and inherited the homepage og:url.
- Unknown /robots, /accessories and /industries slugs returned 200; they are
  now a real 404 (dynamicParams = false) with a not-found page.
- Events, Smart Buildings and Government have no page content yet and rendered
  not-found: moved to UPCOMING_INDUSTRIES until it is written.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Najjar\NajjarV02
2026-10-13 16:57:18 +04:00
co-authored by Claude Opus 5.5
parent 6f6536db7c
commit 8382501598
19 changed files with 261 additions and 73 deletions
+5 -3
View File
@@ -7,12 +7,14 @@ import { WhyUs } from '@/components/robotics/WhyUs';
import { FloatingTechPanel } from '@/components/robotics/FloatingTechPanel';
import { CompanyStory } from '@/components/robotics/CompanyStory';
import { FounderSection } from '@/components/robotics/FounderSection';
import { pageMetadata } from '@/lib/seo';
export const metadata: Metadata = {
title: 'About YS Lootah Robotics UAE Destination for Unitree & Pudu Robotics',
export const metadata: Metadata = pageMetadata({
title: 'About Us · UAE Destination for Unitree & Pudu Robotics',
description:
'YS Lootah Robotics is part of the Yousuf Saeed Lootah Investment Group a trusted UAE robotics partner delivering AI, automation, and intelligent robotics across Dubai and the UAE.',
};
path: '/about/',
});
export default function AboutPage() {
return (
+10 -11
View File
@@ -10,9 +10,14 @@ import { FooterAndContact } from '@/components/FooterAndContact';
import { MotionSection } from '@/components/ui/MotionSection';
import { CTAButton } from '@/components/ui/CTAButton';
import { ACCESSORIES, GROUP_LABELS, getAccessoryBySlug, type Accessory } from '@/data/accessories';
import { displayName, pageMetadata } from '@/lib/seo';
type Params = { slug: string };
// Only the slugs below exist. Anything else is a real 404 from the router, not a
// page that renders "not found" in the browser with status 200.
export const dynamicParams = false;
export function generateStaticParams() {
return ACCESSORIES.map((a) => ({ slug: a.slug }));
}
@@ -20,18 +25,12 @@ export function generateStaticParams() {
export async function generateMetadata({ params }: { params: Promise<Params> }): Promise<Metadata> {
const { slug } = await params;
const a = getAccessoryBySlug(slug);
if (!a) return { title: 'Accessory not found · YS Lootah Robotics' };
return {
title: `${a.brandLabel} ${a.name} · Available in UAE | YS Lootah Robotics`,
if (!a) return { title: 'Accessory not found' };
return pageMetadata({
title: `${displayName(a.brandLabel, a.name)} · Available in UAE`,
description: `${a.tagline} ${a.description} Available through YS Lootah Robotics in Dubai.`,
alternates: { canonical: `/accessories/${a.slug}/` },
openGraph: {
title: `${a.brandLabel} ${a.name}`,
description: a.tagline,
url: `/accessories/${a.slug}/`,
type: 'website',
},
};
path: `/accessories/${a.slug}`,
});
}
export default async function AccessoryDetailPage({ params }: { params: Promise<Params> }) {
+5 -4
View File
@@ -5,13 +5,14 @@ import { Navbar } from '@/components/Navbar';
import { FooterAndContact } from '@/components/FooterAndContact';
import { AccessoriesShowcase } from '@/components/robotics/AccessoriesShowcase';
import { ACCESSORIES } from '@/data/accessories';
import { pageMetadata } from '@/lib/seo';
export const metadata: Metadata = {
title: 'Robotics Accessories · Dexterous Hands, Arms, Sensors, IoT | YS Lootah Robotics',
export const metadata: Metadata = pageMetadata({
title: 'Robotics Accessories · Dexterous Hands, Arms, Sensors, IoT',
description:
'Browse Unitree dexterous hands, robotic arms, perception sensors, and Pudu dispatch, docking, modular attachments, and building IoT modules — available in the UAE through YS Lootah Robotics.',
alternates: { canonical: '/accessories/' },
};
path: '/accessories/',
});
export default function AccessoriesIndexPage() {
return (
+5 -3
View File
@@ -2,12 +2,14 @@ import type { Metadata } from 'next';
import { Navbar } from '@/components/Navbar';
import { FooterAndContact } from '@/components/FooterAndContact';
import { InquiryForm } from '@/components/robotics/InquiryForm';
import { pageMetadata } from '@/lib/seo';
export const metadata: Metadata = {
title: 'Book a Robotics Demo in Dubai YS Lootah Robotics',
export const metadata: Metadata = pageMetadata({
title: 'Book a Robotics Demo in Dubai',
description:
'Book a live robot demo at our Dubai showroom or at your venue. See selected Unitree and Pudu Robotics solutions in action, available in the UAE through YS Lootah Robotics.',
};
path: '/book-demo/',
});
export default function BookDemoPage() {
return (
+5 -3
View File
@@ -5,12 +5,14 @@ import { BRANDS, ROBOTS, type RobotBrand } from '@/data/robots';
import { RobotProductCard } from '@/components/robotics/RobotProductCard';
import { MotionSection } from '@/components/ui/MotionSection';
import { DemoCTA } from '@/components/robotics/DemoCTA';
import { pageMetadata } from '@/lib/seo';
export const metadata: Metadata = {
title: 'Robotics Brands Unitree & Pudu | UAE Access via YS Lootah Robotics',
export const metadata: Metadata = pageMetadata({
title: 'Robotics Brands in the UAE · Unitree & Pudu',
description:
'Selected Unitree and Pudu Robotics solutions available in the UAE through YS Lootah Robotics. Humanoid, quadruped, service, delivery, and cleaning robots.',
};
path: '/brands/',
});
const ORDER: RobotBrand[] = ['unitree', 'pudu'];
+5 -10
View File
@@ -23,22 +23,17 @@ import {
} from '@/data/bu-sunaidah';
import { BU_SUNAIDAH_PRESS } from '@/data/bu-sunaidah-press';
import { getLatestBuSunaidahPosts } from '@/lib/bu-sunaidah-fetch';
import { pageMetadata } from '@/lib/seo';
// Re-fetch latest Instagram posts every 30 minutes on the server.
export const revalidate = 1800;
export const metadata: Metadata = {
title: 'Bu Sunaidah · Emirati Robotics Persona | YS Lootah Robotics',
export const metadata: Metadata = pageMetadata({
title: 'Bu Sunaidah · Emirati Robotics Persona',
description:
'Meet Bu Sunaidah — the Emirati culture-inspired robotics character from YS Lootah Robotics. Media activations, exhibitions, and community storytelling across the UAE.',
alternates: { canonical: '/bu-sunaidah/' },
openGraph: {
title: 'Bu Sunaidah · Emirati Robotics Persona',
description: 'Emirati culture-inspired robotics character — UAE media, exhibitions, community.',
url: '/bu-sunaidah/',
type: 'website',
},
};
path: '/bu-sunaidah/',
});
const PILLARS = [
{
+14
View File
@@ -0,0 +1,14 @@
import type { Metadata } from 'next';
import { pageMetadata } from '@/lib/seo';
// The page itself is a client component and cannot export metadata.
export const metadata: Metadata = pageMetadata({
title: 'Unitree G1 3D Configurator',
description:
'Build your Unitree G1 in 3D. Choose persona, attire, colours and accessories, preview it live, then request a UAE quotation from YS Lootah Robotics.',
path: '/configure/',
});
export default function ConfigureLayout({ children }: { children: React.ReactNode }) {
return children;
}
+5 -3
View File
@@ -2,12 +2,14 @@ import type { Metadata } from 'next';
import { Navbar } from '@/components/Navbar';
import { FooterAndContact } from '@/components/FooterAndContact';
import { InquiryForm } from '@/components/robotics/InquiryForm';
import { pageMetadata } from '@/lib/seo';
export const metadata: Metadata = {
title: 'Contact YS Lootah Robotics Dubai Robotics Sales & Demo Inquiries',
export const metadata: Metadata = pageMetadata({
title: 'Contact · Dubai Robotics Sales & Demo Inquiries',
description:
'Dubai robotics sales, support, and demo inquiries. Contact YS Lootah Robotics, the UAE destination for selected Unitree and Pudu Robotics solutions, by phone, email, or WhatsApp.',
};
path: '/contact/',
});
export default function ContactPage() {
return (
+10 -6
View File
@@ -26,6 +26,7 @@ import { MotionSection } from '@/components/ui/MotionSection';
import { CTAButton } from '@/components/ui/CTAButton';
import { INDUSTRIES, getIndustryBySlug } from '@/data/industries';
import { INDUSTRY_PAGES, type IndustrySolution } from '@/data/industry-pages';
import { pageMetadata } from '@/lib/seo';
type Params = { slug: string };
@@ -44,6 +45,10 @@ const ICONS: Record<IndustrySolution['iconKey'], React.ComponentType<{ size?: nu
tray: UtensilsCrossed,
};
// Only the slugs below exist. Anything else is a real 404 from the router, not a
// page that renders "not found" in the browser with status 200.
export const dynamicParams = false;
export function generateStaticParams() {
return INDUSTRIES.map((i) => ({ slug: i.slug }));
}
@@ -54,14 +59,13 @@ export async function generateMetadata({ params }: { params: Promise<Params> }):
if (!industry) return {};
const content = INDUSTRY_PAGES[industry.id];
if (!content) return {};
const url = `/industries/${industry.slug}/`;
return {
return pageMetadata({
title: content.seoTitle,
description: content.seoDescription,
alternates: { canonical: url },
openGraph: { title: content.seoTitle, description: content.seoDescription, url, type: 'website' },
twitter: { card: 'summary_large_image', title: content.seoTitle, description: content.seoDescription },
};
path: `/industries/${industry.slug}`,
image: industry.image,
imageAlt: industry.imageAlt,
});
}
const TRUST_DEFAULT = [
+6 -4
View File
@@ -9,12 +9,14 @@ import { FooterAndContact } from '@/components/FooterAndContact';
import { IndustryUseCases } from '@/components/robotics/IndustryUseCases';
import { DemoCTA } from '@/components/robotics/DemoCTA';
import { MotionSection } from '@/components/ui/MotionSection';
import { pageMetadata } from '@/lib/seo';
export const metadata: Metadata = {
title: 'Robotics for UAE Industries YS Lootah Robotics Dubai',
export const metadata: Metadata = pageMetadata({
title: 'Robotics for UAE Industries',
description:
'Robotics solutions for hospitality, restaurants, hotels, healthcare, education, security, warehouses, smart buildings, and government across the UAE.',
};
'Robotics solutions for hospitality, restaurants, hotels, healthcare, education, security, and warehouses across the UAE.',
path: '/industries/',
});
const HERO_COLLAGE: { src: string; alt: string }[] = [
{ src: '/industries/cards/hospitality.webp', alt: 'Service robot in a luxury hospitality lobby' },
+39
View File
@@ -0,0 +1,39 @@
import type { Metadata } from 'next';
import Link from 'next/link';
import { Navbar } from '@/components/Navbar';
import { FooterAndContact } from '@/components/FooterAndContact';
// Next adds the noindex tag to not-found responses itself.
export const metadata: Metadata = {
title: 'Page not found',
};
export default function NotFound() {
return (
<>
<Navbar />
<main style={{ paddingTop: 'clamp(8rem, 14vw, 11rem)', paddingBottom: 'clamp(5rem, 10vw, 8rem)' }}>
<div
className="container-wide"
style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '1.5rem', textAlign: 'center' }}
>
<span className="eyebrow">Error 404</span>
<h1 style={{ fontSize: 'clamp(2.2rem, 6vw, 4rem)', fontWeight: 600, letterSpacing: '-0.03em', lineHeight: 1.05, color: '#f5f5f5', maxWidth: '16ch' }}>
This page could not be found.
</h1>
<p style={{ color: '#a8a8a8', maxWidth: 520, lineHeight: 1.7 }}>
The address may have changed or the product may no longer be listed. Browse the catalog or
get in touch and we will point you to the right robot.
</p>
<div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'center', gap: '0.75rem 1.5rem', marginTop: '0.5rem' }}>
<Link href="/robots/" style={{ color: '#f5f5f5' }}>Robots</Link>
<Link href="/accessories/" style={{ color: '#f5f5f5' }}>Accessories</Link>
<Link href="/industries/" style={{ color: '#f5f5f5' }}>Industries</Link>
<Link href="/contact/" style={{ color: '#f5f5f5' }}>Contact</Link>
</div>
</div>
</main>
<FooterAndContact />
</>
);
}
+9
View File
@@ -1,6 +1,15 @@
import type { Metadata } from 'next';
import React from 'react';
import { Navbar } from '@/components/Navbar';
import { FooterAndContact } from '@/components/FooterAndContact';
import { pageMetadata } from '@/lib/seo';
export const metadata: Metadata = pageMetadata({
title: 'Privacy Policy',
description:
'How YS Lootah Robotics collects, uses and protects personal information on this website and in enquiries, demo bookings and orders.',
path: '/privacy-policy/',
});
export default function PrivacyPolicyPage() {
return (
+17 -14
View File
@@ -12,10 +12,14 @@ import { ProductGallery } from '@/components/robotics/ProductGallery';
import { MotionSection } from '@/components/ui/MotionSection';
import { ROBOTS, getRobotBySlug, BRANDS, CATEGORY_LABELS } from '@/data/robots';
import JsonLd from '@/components/JsonLd';
import { canonical, abs, productJsonLd, breadcrumbJsonLd } from '@/lib/seo';
import { displayName, pageMetadata, productJsonLd, breadcrumbJsonLd } from '@/lib/seo';
type Params = { slug: string };
// Only the slugs below exist. Anything else is a real 404 from the router, not a
// page that renders "not found" in the browser with status 200.
export const dynamicParams = false;
export function generateStaticParams() {
return ROBOTS.map((r) => ({ slug: r.slug }));
}
@@ -23,18 +27,17 @@ export function generateStaticParams() {
export async function generateMetadata({ params }: { params: Promise<Params> }): Promise<Metadata> {
const { slug } = await params;
const robot = getRobotBySlug(slug);
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.`;
if (!robot) return { title: 'Robot not found' };
const name = displayName(robot.brandLabel, robot.name);
return {
title,
description,
...pageMetadata({
title: `${name} Available in Dubai`,
description: `${robot.shortDescription} Available through YS Lootah Robotics in Dubai request a price or book a live demo.`,
path: `/robots/${robot.slug}`,
image: robot.image,
imageAlt: name,
}),
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] },
};
}
@@ -47,7 +50,7 @@ export default async function RobotDetailPage({ params }: { params: Promise<Para
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}`,
name: displayName(robot.brandLabel, robot.name),
description: robot.shortDescription,
image: robot.image,
brandName: brand.name,
@@ -85,7 +88,7 @@ export default async function RobotDetailPage({ params }: { params: Promise<Para
>
<div style={{ position: 'relative' }}>
<ProductGallery
images={[{ src: robot.image, alt: `${robot.brandLabel} ${robot.name}` }]}
images={[{ src: robot.image, alt: displayName(robot.brandLabel, robot.name) }]}
accent={robot.accent}
/>
{robot.imageType === 'placeholder' && (
@@ -184,7 +187,7 @@ export default async function RobotDetailPage({ params }: { params: Promise<Para
Tell us about your venue, timeline, and use case. We will respond with availability, configuration options, and pricing for the UAE.
</p>
</div>
<InquiryForm defaultRobot={`${robot.brandLabel} ${robot.name}`} defaultCategory={robot.category} intent="quotation" />
<InquiryForm defaultRobot={displayName(robot.brandLabel, robot.name)} defaultCategory={robot.category} intent="quotation" />
</div>
<div className="card" style={{ padding: 'clamp(1.5rem, 4vw, 2.25rem)', display: 'flex', flexDirection: 'column', gap: '1rem' }}>
+5 -4
View File
@@ -9,13 +9,14 @@ import { RoboticsSplineShowcase } from '@/components/sections/robotics-spline-sh
import { CTAButton } from '@/components/ui/CTAButton';
import { ROBOTS } from '@/data/robots';
import { ACCESSORIES } from '@/data/accessories';
import { pageMetadata } from '@/lib/seo';
export const metadata: Metadata = {
title: 'Robots Catalog · Unitree & Pudu in UAE | YS Lootah Robotics',
export const metadata: Metadata = pageMetadata({
title: 'Robots Catalog · Unitree & Pudu in UAE',
description:
'Explore selected humanoid, quadruped, industrial, service, delivery, hospitality, and cleaning robots — plus dexterous hands, arms, sensors, and accessories — from Unitree and Pudu available in the UAE through YS Lootah Robotics.',
alternates: { canonical: '/robots/' },
};
path: '/robots/',
});
const unitreeCount = ROBOTS.filter((r) => r.brand === 'unitree').length;
const puduCount = ROBOTS.filter((r) => r.brand === 'pudu').length;
+9
View File
@@ -1,6 +1,15 @@
import type { Metadata } from 'next';
import React from 'react';
import { Navbar } from '@/components/Navbar';
import { FooterAndContact } from '@/components/FooterAndContact';
import { pageMetadata } from '@/lib/seo';
export const metadata: Metadata = pageMetadata({
title: 'Terms of Service',
description:
'The terms that apply to using the YS Lootah Robotics website, requesting quotations, booking demos and ordering robots in the UAE.',
path: '/terms-of-service/',
});
export default function TermsOfServicePage() {
return (