From 14cb8ce1fc46ac28b8c67fe1a65ea715fbbea8f2 Mon Sep 17 00:00:00 2001 From: DMleadgen Date: Thu, 16 Apr 2026 13:03:12 -0600 Subject: [PATCH] deploy: polish public marketing pages --- app/globals.css | 29 +- app/service-areas/page.tsx | 45 +-- app/services/repairs/page.tsx | 143 +++++----- .../machines-for-sale/page.tsx | 270 ++++++++++++++++-- components/about-page.tsx | 2 +- components/contact-page.tsx | 56 +++- components/dropdown-page-shell.tsx | 34 ++- components/features-section.tsx | 6 +- components/footer.tsx | 8 +- components/header.tsx | 172 ++++++++++- components/public-surface.tsx | 10 +- components/request-machine-section.tsx | 4 +- components/reviews-page.tsx | 41 ++- components/who-we-serve-page.tsx | 47 +++ 14 files changed, 714 insertions(+), 153 deletions(-) diff --git a/app/globals.css b/app/globals.css index 9bacf058..928e6161 100644 --- a/app/globals.css +++ b/app/globals.css @@ -183,7 +183,8 @@ transition: color 0.2s ease, text-decoration-color 0.2s ease, - opacity 0.2s ease; + opacity 0.2s ease, + transform 0.2s ease; } a:hover, @@ -202,7 +203,8 @@ transition: color 0.2s ease, text-decoration-color 0.2s ease, - opacity 0.2s ease; + opacity 0.2s ease, + transform 0.2s ease; } a[href]:hover, @@ -211,6 +213,29 @@ background-color: transparent; } + button a, + [role="button"] a, + .bg-primary a, + .text-primary-foreground a { + color: inherit; + } + + button a:hover, + button a:focus, + [role="button"] a:hover, + [role="button"] a:focus, + .bg-primary a:hover, + .bg-primary a:focus, + .text-primary-foreground a:hover, + .text-primary-foreground a:focus { + color: inherit; + } + + p, + li { + text-wrap: pretty; + } + a:focus-visible, button:focus-visible, [role="button"]:focus-visible, diff --git a/app/service-areas/page.tsx b/app/service-areas/page.tsx index fdff7e1a..06ad2e39 100644 --- a/app/service-areas/page.tsx +++ b/app/service-areas/page.tsx @@ -37,10 +37,10 @@ function LocationCard({ }) { return ( - +
-

+

{city}

{zipCode}

@@ -49,15 +49,15 @@ function LocationCard({
- +

Popular Areas

{neighborhoods.slice(0, 2).join(", ")}

- - +
+
) } @@ -222,7 +222,7 @@ export default function ServiceAreasPage() {
-
+
{[ { title: "Salt Lake County", @@ -243,19 +243,24 @@ export default function ServiceAreasPage() { items: utahCounty, }, ].map((section) => ( -
-
-

- Coverage Area -

-

- {section.title} -

-

- {section.description} -

+ +
+
+

+ Coverage Area +

+

+ {section.title} +

+

+ {section.description} +

+
+
+ {section.items.length} cities +
-
+
{section.items.map((location) => ( ))}
-
+
))}
-
+

Why businesses choose Rocky Mountain Vending diff --git a/app/services/repairs/page.tsx b/app/services/repairs/page.tsx index 7c4645ab..2ab62926 100644 --- a/app/services/repairs/page.tsx +++ b/app/services/repairs/page.tsx @@ -1,11 +1,9 @@ import { notFound } from "next/navigation" -import { loadImageMapping } from "@/lib/wordpress-content" import { generateRegistryMetadata, generateRegistryStructuredData, } from "@/lib/seo" import { getPageBySlug } from "@/lib/wordpress-data-loader" -import { cleanWordPressContent } from "@/lib/clean-wordPress-content" import { FAQSection } from "@/components/faq-section" import { ServiceAreasSection } from "@/components/service-areas-section" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" @@ -58,21 +56,13 @@ export default async function RepairsPage() { notFound() } - let imageMapping: any = {} - try { - imageMapping = loadImageMapping() - } catch (e) { - imageMapping = {} - } - // Extract FAQs from content const faqs: Array<{ question: string; answer: string }> = [] - let contentWithoutFAQs = page.content || "" - let contentWithoutVirtualServices = "" let virtualServicesContent = "" if (page.content) { const contentStr = String(page.content) + let strippedContent = contentStr // Extract FAQ items from accordion structure const questionMatches = contentStr.matchAll( @@ -112,40 +102,28 @@ export default async function RepairsPage() { if (faqs.length > 0) { const faqSectionRegex = /]*>.*?Answers\s+To\s+Common\s+Questions.*?<\/h2>[\s\S]*?(?=]*>.*?Virtual\s+Services|]*>.*?Service\s+Area|$)/i - contentWithoutFAQs = contentStr.replace(faqSectionRegex, "").trim() + strippedContent = contentStr.replace(faqSectionRegex, "").trim() } // Extract Virtual Services section const virtualServicesRegex = /]*>.*?Virtual\s+Services.*?<\/h2>([\s\S]*?)(?=]*>.*?Service\s+Area|$)/i - const virtualMatch = contentStr.match(virtualServicesRegex) + const virtualMatch = strippedContent.match(virtualServicesRegex) if (virtualMatch) { virtualServicesContent = virtualMatch[1] - // Remove Virtual Services from main content - contentWithoutVirtualServices = contentWithoutFAQs - .replace(virtualServicesRegex, "") - .trim() - } else { - contentWithoutVirtualServices = contentWithoutFAQs } } - const content = contentWithoutVirtualServices ? ( -
- {cleanWordPressContent(String(contentWithoutVirtualServices), { - imageMapping, - pageTitle: page.title, - })} -
- ) : ( -

No content available.

- ) - const structuredData = generateRegistryStructuredData("repairs", { datePublished: page.date, dateModified: page.modified || page.date, }) + const excerpt = String(page.excerpt || "") + .replace(/<[^>]+>/g, " ") + .replace(/\s+/g, " ") + .trim() + const surfaceCardClass = "rounded-[var(--public-surface-radius)] border border-border/70 bg-[linear-gradient(180deg,rgba(255,255,255,0.98),rgba(255,251,243,0.96))] shadow-[var(--public-surface-shadow)]" const insetCardClass = @@ -171,37 +149,14 @@ export default async function RepairsPage() { align="center" className="mb-8" eyebrow="Repair Services" - title={page.title || "Vending Machine Repairs and Service"} + title="Vending machine repairs and service for Utah businesses" description={ - "Rocky Mountain Vending delivers expert vending machine repair and maintenance services to keep your business thriving." + "Get help with payment issues, refrigeration problems, machine errors, and ongoing maintenance from a local vending service team." } >

- Rocky Mountain Vending delivers expert{" "} - - vending machine repair - {" "} - and maintenance services to keep your business thriving. From - resolving jammed coin slots and refrigeration issues to fixing - non-dispensing machines, our skilled technicians ensure reliable - performance. For all your{" "} - - vending machine parts - {" "} - needs and professional{" "} - - vending machine moving - {" "} - services, contact us today for fast, professional solutions! + {excerpt || + "Rocky Mountain Vending helps businesses across Davis, Salt Lake, and Utah counties keep machines running with practical repair, maintenance, and support guidance."}

{/* Images Carousel */} @@ -211,15 +166,71 @@ export default async function RepairsPage() {

- {contentWithoutVirtualServices ? ( -
-
- -
{content}
-
-
-
- ) : null} +
+
+ +

+ Repair Overview +

+

+ Clear next steps when a machine is down, rejecting payments, or + needs service. +

+

+ We help with common vending issues like bill acceptor problems, + refrigeration failures, card reader troubleshooting, controller + errors, and recurring maintenance needs. If the issue can be + handled virtually, we can talk through that too. +

+
+ + Request Service + + + Need Parts Instead? + +
+
+ +

+ Before You Reach Out +

+

+ The more detail you share, the faster we can point you in the + right direction. +

+
    +
  • + + + Include the machine model, brand, and whether the issue is + intermittent or constant. + +
  • +
  • + + + Tell us if the problem is payment-related, refrigeration, + dispensing, display errors, or a recent setup change. + +
  • +
  • + + + Photos or short videos can make remote triage much easier + before an on-site visit is scheduled. + +
  • +
+
+
+
{/* Services Section */}
diff --git a/app/vending-machines/machines-for-sale/page.tsx b/app/vending-machines/machines-for-sale/page.tsx index e1bf8e47..f7c8d617 100644 --- a/app/vending-machines/machines-for-sale/page.tsx +++ b/app/vending-machines/machines-for-sale/page.tsx @@ -1,16 +1,74 @@ import { notFound } from "next/navigation" -import { loadImageMapping } from "@/lib/wordpress-content" -import type { ImageMapping } from "@/lib/wordpress-content" +import type { Metadata } from "next" +import Image from "next/image" +import Link from "next/link" +import { + CheckCircle2, + CreditCard, + Refrigerator, + ShoppingCart, +} from "lucide-react" import { generateSEOMetadata, generateStructuredData } from "@/lib/seo" import { getPageBySlug } from "@/lib/wordpress-data-loader" -import { cleanWordPressContent } from "@/lib/clean-wordPress-content" -import type { Metadata } from "next" -import { PublicPageHeader, PublicSurface } from "@/components/public-surface" +import { + PublicInset, + PublicPageHeader, + PublicSectionHeader, + PublicSurface, +} from "@/components/public-surface" import { GetFreeMachineCta } from "@/components/get-free-machine-cta" import { Breadcrumbs } from "@/components/breadcrumbs" +import { Button } from "@/components/ui/button" const WORDPRESS_SLUG = "vending-machines-for-sale-in-utah" +const machineHighlights = [ + "Snack, drink, combo, and card-reader-ready equipment options", + "New and used machine guidance based on traffic, budget, and product mix", + "Local help with payment hardware, installation planning, and next-step questions", +] + +const machineOptions = [ + { + title: "Snack and drink machines", + description: + "Traditional snack, beverage, and combo machines for breakrooms, customer spaces, and mixed-traffic locations.", + icon: ShoppingCart, + }, + { + title: "Cashless payment hardware", + description: + "Card reader and mobile-payment options that help modernize older machines or support new installs.", + icon: CreditCard, + }, + { + title: "Refrigerated equipment", + description: + "Cold drink and refrigerated machine options for workplaces that need dependable temperature-controlled service.", + icon: Refrigerator, + }, +] + +const buyingSteps = [ + { + title: "Tell us about the location", + body: "We learn about traffic, available space, product needs, and whether you are comparing free placement with a direct purchase.", + }, + { + title: "Compare the right machine options", + body: "We help narrow down machine styles, payment hardware, and new-versus-used tradeoffs so you are looking at realistic fits.", + }, + { + title: "Plan install and support", + body: "Once you know what you want, we can talk through delivery, setup, payment configuration, and any follow-up service needs.", + }, +] + +function normalizeWpImageUrl(url?: string) { + if (!url) return null + return url.replace("https:///", "https://rockymountainvending.com/") +} + export async function generateMetadata(): Promise { const page = getPageBySlug(WORDPRESS_SLUG) @@ -39,13 +97,6 @@ export default async function MachinesForSalePage() { notFound() } - let imageMapping: ImageMapping = {} - try { - imageMapping = loadImageMapping() - } catch { - imageMapping = {} - } - const structuredData = (() => { try { return generateStructuredData({ @@ -70,6 +121,16 @@ export default async function MachinesForSalePage() { } })() + const heroImage = + normalizeWpImageUrl(page.images?.[0]?.url) ?? + "https://rockymountainvending.com/wp-content/uploads/2024/01/EH0A1551-HDR.webp" + const comboImage = + normalizeWpImageUrl(page.images?.[1]?.url) ?? + "https://rockymountainvending.com/wp-content/uploads/2022/06/Seage-HY900-Combo.webp" + const paymentImage = + normalizeWpImageUrl(page.images?.[2]?.url) ?? + "https://rockymountainvending.com/wp-content/uploads/2024/01/Parlevel-Pay-Plus.jpg" + return ( <>