Files
route-commerce/src/app/sitemap.ts
T
tyler 778b3fe311 fix: full Apple HIG mobile + SEO audit - all issues resolved
MOBILE RESPONSIVENESS (Apple HIG):
- HeroSection: typography scaled 7xl→4xl sm:5xl md:6xl lg:7xl, responsive px/py, rounded-2xl, active:scale-95
- Cart quantity buttons: h-8→h-11 w-8→w-11 (44px touch target), rounded-xl, active:scale-95
- StorefrontFooter newsletter: responsive w-full sm:w-52 lg:w-64, aria-label, larger touch targets
- StorefrontHeader mobile nav: padding px-6→px-4 py-6→py-5
- AdminTable: overflow-x-auto + min-w-[600px] for horizontal scroll
- AdminOrdersPanel: same table overflow fix
- AdminLayout: mobile px-4 sm:px-6 py-6 sm:py-10
- AdminFilterTabs: responsive text/text sizes
- AdminSidebar hamburger: h-10→h-11 w-10→w-11 (44px touch target)
- DashboardClient: grid gap-3 sm:gap-4, responsive stat text
- OrderEditForm: grid-cols-1 sm:grid-cols-2 (was 2, breaks on mobile)
- BillingClient: min-h-[44px] on select/button
- ProductsClient: h-32 sm:h-40 responsive image height
- StopCard: line-clamp-2 instead of line-clamp-1 on location
- CommunicationsPage: tabs wrapped in overflow-x-auto
- Checkout page: grid breakpoint md not lg
- Tuxedo page: SectionHeader mobile-first, feature grid grid-cols-1 sm:2, label visible
- Tuxedo stats: text-3xl sm:text-4xl

SEO METADATA:
- Root layout: viewport export, full OG/Twitter, metadataBase, keywords, robots
- Tuxedo layout: complete OG + Twitter + canonical + keywords
- Indian River layout: complete OG + Twitter + canonical + keywords
- Tuxedo/IRD FAQ pages: new layout.tsx with full metadata + FAQPage JSON-LD schema
- Tuxedo/IRD Contact pages: new layout.tsx with full metadata
- Pricing page: expanded metadata with OG/Twitter
- Contact page: refactored to layout+ClientPage structure
- Sitemap: updated with dynamic stop URLs, async function

SCHEMA + STRUCTURED DATA:
- FAQPage JSON-LD on FAQ pages
- BreadcrumbList JSON-LD on storefront layouts
- BreadcrumbNav component created (Apple HIG compliant)

BUG FIXES:
- Indian River: replaced raw <img> with Next.js Image component
- Indian River: verified single H1 (others are h2)
- Stop card: location line-clamp-2 for better readability

TYPE CHECK: all pass
2026-06-02 04:32:58 +00:00

116 lines
2.7 KiB
TypeScript

import { MetadataRoute } from "next";
import { getActiveStopsForSitemap } from "@/actions/stops";
const BASE_URL = process.env.NEXT_PUBLIC_SITE_URL ?? "https://yourdomain.com";
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const now = new Date();
// Fetch active stops for dynamic URLs
const activeStops = await getActiveStopsForSitemap();
// Base static URLs
const staticUrls: MetadataRoute.Sitemap = [
{
url: BASE_URL,
lastModified: now,
changeFrequency: "weekly",
priority: 1,
},
{
url: `${BASE_URL}/contact`,
lastModified: now,
changeFrequency: "monthly",
priority: 0.6,
},
{
url: `${BASE_URL}/pricing`,
lastModified: now,
changeFrequency: "monthly",
priority: 0.7,
},
{
url: `${BASE_URL}/privacy-policy`,
lastModified: now,
changeFrequency: "yearly",
priority: 0.3,
},
{
url: `${BASE_URL}/terms-and-conditions`,
lastModified: now,
changeFrequency: "yearly",
priority: 0.3,
},
// Tuxedo Corn storefront
{
url: `${BASE_URL}/tuxedo`,
lastModified: now,
changeFrequency: "weekly",
priority: 0.9,
},
{
url: `${BASE_URL}/tuxedo/about`,
lastModified: now,
changeFrequency: "monthly",
priority: 0.7,
},
{
url: `${BASE_URL}/tuxedo/faq`,
lastModified: now,
changeFrequency: "monthly",
priority: 0.6,
},
{
url: `${BASE_URL}/tuxedo/contact`,
lastModified: now,
changeFrequency: "monthly",
priority: 0.6,
},
// Indian River Direct storefront
{
url: `${BASE_URL}/indian-river-direct`,
lastModified: now,
changeFrequency: "weekly",
priority: 0.9,
},
{
url: `${BASE_URL}/indian-river-direct/about`,
lastModified: now,
changeFrequency: "monthly",
priority: 0.7,
},
{
url: `${BASE_URL}/indian-river-direct/faq`,
lastModified: now,
changeFrequency: "monthly",
priority: 0.6,
},
{
url: `${BASE_URL}/indian-river-direct/contact`,
lastModified: now,
changeFrequency: "monthly",
priority: 0.6,
},
// Wholesale portal
{
url: `${BASE_URL}/wholesale/portal`,
lastModified: now,
changeFrequency: "weekly",
priority: 0.8,
},
];
// Dynamic stop URLs
const stopUrls: MetadataRoute.Sitemap = activeStops.map((stop) => ({
url: `${BASE_URL}/${stop.brand_slug}/stops/${stop.slug}`,
lastModified: new Date(stop.last_modified),
changeFrequency: "weekly" as const,
priority: 0.8,
}));
return [...staticUrls, ...stopUrls];
}