c71354d2ae
Deploy to route.crispygoat.com / deploy (push) Successful in 4m25s
Reskin /tuxedo/contact, /tuxedo/faq, /tuxedo/stops, and /tuxedo/stops/[id] so they match the homepage's editorial magazine language (Fraunces display serif, stone-50/stone-950 alternation, amber rims, hairline rules, tabular numerals). - About page rewritten as inlined editorial sections sharing one WP_IMAGES library with the homepage; the four lazy-loaded Tuxedo about components are removed (Indian River has its own variants). - Contact page now uses a dark hero with heroField half-bleed, a hairline-ruled info strip (address/phone/email), a two-column form section with bottom-border editorial inputs, and a closing Fraunces italic quote. /tuxedo/contact was 404ing in prod because page.tsx was missing — created and removed the duplicated render from layout.tsx. - FAQ page uses a cream hero with Fraunces headline, an editorial bottom-border search, hairline-ruled accordion rows (no card wrappers), and a dark closing CTA with Contact pill. - Stops list uses a dark hero with heroField bleed and a cream hairline-ruled sidebar list: tabular date numerals on the left, city/location middle, mono time + cutoff right, chevron for upcoming rows. - Stop detail uses a dark hero pairing the city/state headline with a giant tabular day stamp, a cream hairline-ruled 4-col info strip (Date/Time/Location/Order by), and the editorial products header. Removed the dual-brand isBlue branching now that Indian River has its own page. - Extracted TUXEDO_IMAGES to src/components/storefront/tuxedo-images.ts so the homepage, about, contact, and stops list share one brand-specific asset library. - /tuxedo/faq/layout.tsx no longer double-renders the page component (was rendering <FAQClientPage /> alongside children). Verified: tsc --noEmit clean, eslint clean (only pre-existing homepage warnings), npm run build produces all Tuxedo routes including the now-existing /tuxedo/contact.
149 lines
4.5 KiB
TypeScript
149 lines
4.5 KiB
TypeScript
import type { Metadata } from "next";
|
|
import { serializeJsonForScript } from "@/lib/safe-json";
|
|
|
|
const BASE_URL = process.env.NEXT_PUBLIC_SITE_URL ?? "https://routecommerce.com";
|
|
|
|
export const metadata: Metadata = {
|
|
title: "FAQ — Frequently Asked Questions",
|
|
description: "Find answers to common questions about ordering corn, pickup stops, shipping, and wholesale accounts for Tuxedo Corn.",
|
|
keywords: ["Tuxedo Corn FAQ", "Olathe Sweet corn questions", "corn pickup FAQ", "wholesale corn accounts", "corn shipping"],
|
|
openGraph: {
|
|
title: "FAQ — Tuxedo Corn Frequently Asked Questions",
|
|
description: "Find answers to common questions about ordering corn, pickup stops, shipping, and wholesale accounts.",
|
|
url: `${BASE_URL}/tuxedo/faq`,
|
|
siteName: "Tuxedo Corn",
|
|
locale: "en_US",
|
|
type: "website",
|
|
images: [
|
|
{
|
|
url: "/og-tuxedo.jpg",
|
|
width: 1200,
|
|
height: 630,
|
|
alt: "Tuxedo Corn FAQ",
|
|
},
|
|
],
|
|
},
|
|
twitter: {
|
|
card: "summary_large_image",
|
|
title: "FAQ — Tuxedo Corn",
|
|
description: "Find answers to common questions about ordering corn, pickup stops, shipping, and wholesale accounts.",
|
|
site: "@TuxedoCorn",
|
|
images: ["/og-tuxedo.jpg"],
|
|
},
|
|
alternates: {
|
|
canonical: `${BASE_URL}/tuxedo/faq`,
|
|
},
|
|
robots: {
|
|
index: true,
|
|
follow: true,
|
|
},
|
|
};
|
|
|
|
const faqJsonLd = {
|
|
"@context": "https://schema.org",
|
|
"@type": "FAQPage",
|
|
"mainEntity": [
|
|
{
|
|
"@type": "Question",
|
|
"name": "How do I place a preorder for corn?",
|
|
"acceptedAnswer": {
|
|
"@type": "Answer",
|
|
"text": "Find a stop near you on our homepage and click 'Shop This Stop' to add corn to your cart and select your pickup location. Preordering helps us bring the right amount of corn to each stop."
|
|
}
|
|
},
|
|
{
|
|
"@type": "Question",
|
|
"name": "Can I order without selecting a pickup stop?",
|
|
"acceptedAnswer": {
|
|
"@type": "Answer",
|
|
"text": "Yes. Choose 'Shipping' at checkout and we will mail cooler boxes directly to your home. Shipping is available for orders of 4 or more dozen and is fulfilled after the season ends."
|
|
}
|
|
},
|
|
{
|
|
"@type": "Question",
|
|
"name": "Do you ship corn?",
|
|
"acceptedAnswer": {
|
|
"@type": "Answer",
|
|
"text": "Yes. We ship Olathe Sweet Sweet Corn nationwide. Orders ship as cooler boxes after our field season ends in late summer. A minimum of 4 dozen is required for shipping."
|
|
}
|
|
},
|
|
{
|
|
"@type": "Question",
|
|
"name": "How do I set up a wholesale account?",
|
|
"acceptedAnswer": {
|
|
"@type": "Answer",
|
|
"text": "Visit /wholesale/register to apply. We review applications and respond within 1-2 business days. Wholesale accounts are available to retailers, restaurants, and farm stands."
|
|
}
|
|
},
|
|
{
|
|
"@type": "Question",
|
|
"name": "What makes Olathe Sweet different?",
|
|
"acceptedAnswer": {
|
|
"@type": "Answer",
|
|
"text": "Olathe Sweet Sweet Corn is grown in the Uncompahgre Valley of Colorado, where high-altitude days and cool nights produce exceptionally sweet, tender corn. We have been growing this variety for over 40 years."
|
|
}
|
|
}
|
|
]
|
|
};
|
|
|
|
const breadcrumbJsonLd = {
|
|
"@context": "https://schema.org",
|
|
"@type": "BreadcrumbList",
|
|
"itemListElement": [
|
|
{
|
|
"@type": "ListItem",
|
|
"position": 1,
|
|
"name": "Home",
|
|
"item": BASE_URL
|
|
},
|
|
{
|
|
"@type": "ListItem",
|
|
"position": 2,
|
|
"name": "Tuxedo Corn",
|
|
"item": `${BASE_URL}/tuxedo`
|
|
},
|
|
{
|
|
"@type": "ListItem",
|
|
"position": 3,
|
|
"name": "FAQ",
|
|
"item": `${BASE_URL}/tuxedo/faq`
|
|
}
|
|
]
|
|
};
|
|
|
|
const organizationJsonLd = {
|
|
"@context": "https://schema.org",
|
|
"@type": "Organization",
|
|
name: "Tuxedo Corn",
|
|
url: `${BASE_URL}/tuxedo`,
|
|
logo: {
|
|
"@type": "ImageObject",
|
|
url: `${BASE_URL}/logo-tuxedo.png`
|
|
},
|
|
contactPoint: {
|
|
"@type": "ContactPoint",
|
|
telephone: "+1-970-323-6874",
|
|
contactType: "customer service",
|
|
availableLanguage: "English"
|
|
}
|
|
};
|
|
|
|
export default function TuxedoFAQLayout({ children }: { children: React.ReactNode }) {
|
|
return (
|
|
<>
|
|
<script
|
|
type="application/ld+json"
|
|
dangerouslySetInnerHTML={{ __html: serializeJsonForScript(faqJsonLd) }}
|
|
/>
|
|
<script
|
|
type="application/ld+json"
|
|
dangerouslySetInnerHTML={{ __html: serializeJsonForScript(breadcrumbJsonLd) }}
|
|
/>
|
|
<script
|
|
type="application/ld+json"
|
|
dangerouslySetInnerHTML={{ __html: serializeJsonForScript(organizationJsonLd) }}
|
|
/>
|
|
{children}
|
|
</>
|
|
);
|
|
} |