fix: align public customer-facing styles
This commit is contained in:
parent
726c4877c6
commit
e2124cfa66
12 changed files with 160 additions and 181 deletions
|
|
@ -197,7 +197,7 @@ function renderLocationPage(locationData: any, locationSlug: string) {
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="grid gap-6 md:grid-cols-2">
|
<div className="grid gap-6 md:grid-cols-2">
|
||||||
<Card className="rounded-[1.75rem] border-border/70 bg-[linear-gradient(180deg,rgba(255,255,255,0.96),rgba(247,244,236,0.9))] shadow-[0_18px_45px_rgba(0,0,0,0.08)] transition-all hover:-translate-y-0.5 hover:border-primary/35">
|
<Card className="rounded-[1.75rem] border border-border/70 bg-background shadow-[0_18px_45px_rgba(15,23,42,0.08)] transition-all hover:-translate-y-0.5 hover:border-primary/35">
|
||||||
<CardContent className="p-6">
|
<CardContent className="p-6">
|
||||||
<h3 className="text-xl font-semibold mb-3">Vending Machine Sales</h3>
|
<h3 className="text-xl font-semibold mb-3">Vending Machine Sales</h3>
|
||||||
<p className="text-muted-foreground">
|
<p className="text-muted-foreground">
|
||||||
|
|
@ -207,7 +207,7 @@ function renderLocationPage(locationData: any, locationSlug: string) {
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card className="rounded-[1.75rem] border-border/70 bg-[linear-gradient(180deg,rgba(255,255,255,0.96),rgba(247,244,236,0.9))] shadow-[0_18px_45px_rgba(0,0,0,0.08)] transition-all hover:-translate-y-0.5 hover:border-primary/35">
|
<Card className="rounded-[1.75rem] border border-border/70 bg-background shadow-[0_18px_45px_rgba(15,23,42,0.08)] transition-all hover:-translate-y-0.5 hover:border-primary/35">
|
||||||
<CardContent className="p-6">
|
<CardContent className="p-6">
|
||||||
<h3 className="text-xl font-semibold mb-3">Vending Machine Repair</h3>
|
<h3 className="text-xl font-semibold mb-3">Vending Machine Repair</h3>
|
||||||
<p className="text-muted-foreground">
|
<p className="text-muted-foreground">
|
||||||
|
|
@ -217,7 +217,7 @@ function renderLocationPage(locationData: any, locationSlug: string) {
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card className="rounded-[1.75rem] border-border/70 bg-[linear-gradient(180deg,rgba(255,255,255,0.96),rgba(247,244,236,0.9))] shadow-[0_18px_45px_rgba(0,0,0,0.08)] transition-all hover:-translate-y-0.5 hover:border-primary/35">
|
<Card className="rounded-[1.75rem] border border-border/70 bg-background shadow-[0_18px_45px_rgba(15,23,42,0.08)] transition-all hover:-translate-y-0.5 hover:border-primary/35">
|
||||||
<CardContent className="p-6">
|
<CardContent className="p-6">
|
||||||
<h3 className="text-xl font-semibold mb-3">Healthy Snack and Beverage Options</h3>
|
<h3 className="text-xl font-semibold mb-3">Healthy Snack and Beverage Options</h3>
|
||||||
<p className="text-muted-foreground">
|
<p className="text-muted-foreground">
|
||||||
|
|
@ -227,7 +227,7 @@ function renderLocationPage(locationData: any, locationSlug: string) {
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card className="rounded-[1.75rem] border-border/70 bg-[linear-gradient(180deg,rgba(255,255,255,0.96),rgba(247,244,236,0.9))] shadow-[0_18px_45px_rgba(0,0,0,0.08)] transition-all hover:-translate-y-0.5 hover:border-primary/35">
|
<Card className="rounded-[1.75rem] border border-border/70 bg-background shadow-[0_18px_45px_rgba(15,23,42,0.08)] transition-all hover:-translate-y-0.5 hover:border-primary/35">
|
||||||
<CardContent className="p-6">
|
<CardContent className="p-6">
|
||||||
<h3 className="text-xl font-semibold mb-3">Maintenance Services</h3>
|
<h3 className="text-xl font-semibold mb-3">Maintenance Services</h3>
|
||||||
<p className="text-muted-foreground">
|
<p className="text-muted-foreground">
|
||||||
|
|
@ -304,7 +304,7 @@ function renderLocationPage(locationData: any, locationSlug: string) {
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="grid gap-4 md:grid-cols-3 mb-8">
|
<div className="grid gap-4 md:grid-cols-3 mb-8">
|
||||||
<Card className="rounded-[1.75rem] border-border/70 bg-[linear-gradient(180deg,rgba(255,255,255,0.96),rgba(247,244,236,0.9))] shadow-[0_18px_45px_rgba(0,0,0,0.08)] transition-all hover:-translate-y-0.5 hover:border-primary/35">
|
<Card className="rounded-[1.75rem] border border-border/70 bg-background shadow-[0_18px_45px_rgba(15,23,42,0.08)] transition-all hover:-translate-y-0.5 hover:border-primary/35">
|
||||||
<CardContent className="p-6 flex items-start gap-4">
|
<CardContent className="p-6 flex items-start gap-4">
|
||||||
<Phone className="h-6 w-6 text-secondary flex-shrink-0 mt-1" />
|
<Phone className="h-6 w-6 text-secondary flex-shrink-0 mt-1" />
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -316,7 +316,7 @@ function renderLocationPage(locationData: any, locationSlug: string) {
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card className="rounded-[1.75rem] border-border/70 bg-[linear-gradient(180deg,rgba(255,255,255,0.96),rgba(247,244,236,0.9))] shadow-[0_18px_45px_rgba(0,0,0,0.08)] transition-all hover:-translate-y-0.5 hover:border-primary/35">
|
<Card className="rounded-[1.75rem] border border-border/70 bg-background shadow-[0_18px_45px_rgba(15,23,42,0.08)] transition-all hover:-translate-y-0.5 hover:border-primary/35">
|
||||||
<CardContent className="p-6 flex items-start gap-4">
|
<CardContent className="p-6 flex items-start gap-4">
|
||||||
<Mail className="h-6 w-6 text-secondary flex-shrink-0 mt-1" />
|
<Mail className="h-6 w-6 text-secondary flex-shrink-0 mt-1" />
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -328,7 +328,7 @@ function renderLocationPage(locationData: any, locationSlug: string) {
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card className="rounded-[1.75rem] border-border/70 bg-[linear-gradient(180deg,rgba(255,255,255,0.96),rgba(247,244,236,0.9))] shadow-[0_18px_45px_rgba(0,0,0,0.08)] transition-all hover:-translate-y-0.5 hover:border-primary/35">
|
<Card className="rounded-[1.75rem] border border-border/70 bg-background shadow-[0_18px_45px_rgba(15,23,42,0.08)] transition-all hover:-translate-y-0.5 hover:border-primary/35">
|
||||||
<CardContent className="p-6 flex items-start gap-4">
|
<CardContent className="p-6 flex items-start gap-4">
|
||||||
<Globe className="h-6 w-6 text-secondary flex-shrink-0 mt-1" />
|
<Globe className="h-6 w-6 text-secondary flex-shrink-0 mt-1" />
|
||||||
<div>
|
<div>
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,12 @@ import { ServiceAreasSection } from '@/components/service-areas-section';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { CheckCircle2, Wrench, Clock, Phone, Shield, MapPin } from 'lucide-react';
|
import { CheckCircle2, Wrench, Clock, Phone, Shield, MapPin } from 'lucide-react';
|
||||||
import { RepairsImageCarousel } from '@/components/repairs-image-carousel';
|
import { RepairsImageCarousel } from '@/components/repairs-image-carousel';
|
||||||
|
import { ContactForm } from '@/components/forms/contact-form';
|
||||||
|
import { PublicInset, PublicSurface } from '@/components/public-surface';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import Script from 'next/script';
|
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { ArrowRight } from 'lucide-react';
|
import { ArrowRight } from 'lucide-react';
|
||||||
|
import { businessConfig } from '@/lib/seo-config';
|
||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
|
|
||||||
const WORDPRESS_SLUG = 'vending-machine-repairs';
|
const WORDPRESS_SLUG = 'vending-machine-repairs';
|
||||||
|
|
@ -392,32 +394,28 @@ export default async function RepairsPage() {
|
||||||
Request Service
|
Request Service
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-muted-foreground">
|
<p className="text-muted-foreground">
|
||||||
Fill out the form below to request vending machine repair or maintenance services
|
Use the native Rocky intake below for repairs, diagnostics, or virtual support.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Card className="border-border/50">
|
<PublicSurface className="space-y-6 p-5 md:p-7">
|
||||||
<CardContent className="p-0">
|
<PublicInset className="flex flex-col gap-3 p-5 text-sm text-muted-foreground md:flex-row md:items-center md:justify-between">
|
||||||
<div className="w-full h-[600px] overflow-y-auto">
|
<div>
|
||||||
<iframe
|
<p className="font-semibold text-foreground">Before you submit</p>
|
||||||
src="https://link.sluice-box.io/widget/form/EJSeCs27dWUrveXwAHJE"
|
<p className="mt-1 leading-relaxed">
|
||||||
style={{ width: '100%', minHeight: '1613px', border: 'none', borderRadius: '4px' }}
|
Include the machine model, what it is doing, and whether you need on-site help or virtual support.
|
||||||
id="inline-EJSeCs27dWUrveXwAHJE"
|
</p>
|
||||||
data-layout="{'id':'INLINE'}"
|
|
||||||
data-trigger-type="alwaysShow"
|
|
||||||
data-trigger-value=""
|
|
||||||
data-activation-type="alwaysActivated"
|
|
||||||
data-activation-value=""
|
|
||||||
data-deactivation-type="neverDeactivate"
|
|
||||||
data-deactivation-value=""
|
|
||||||
data-form-name="Repairs"
|
|
||||||
data-height="1613"
|
|
||||||
data-layout-iframe-id="inline-EJSeCs27dWUrveXwAHJE"
|
|
||||||
data-form-id="EJSeCs27dWUrveXwAHJE"
|
|
||||||
title="Repairs"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
<div className="flex flex-col gap-2 text-left md:text-right">
|
||||||
</Card>
|
<a href={businessConfig.publicCallUrl} className="font-medium text-foreground hover:text-primary">
|
||||||
|
Call {businessConfig.publicCallNumber}
|
||||||
|
</a>
|
||||||
|
<a href={businessConfig.publicSmsUrl} className="font-medium text-foreground hover:text-primary">
|
||||||
|
Text photos or videos
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</PublicInset>
|
||||||
|
<ContactForm defaultIntent="Repairs" />
|
||||||
|
</PublicSurface>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -670,9 +668,6 @@ export default async function RepairsPage() {
|
||||||
|
|
||||||
{/* Service Areas Section */}
|
{/* Service Areas Section */}
|
||||||
<ServiceAreasSection />
|
<ServiceAreasSection />
|
||||||
|
|
||||||
{/* Form Script */}
|
|
||||||
<Script src="https://link.sluice-box.io/js/form_embed.js" strategy="afterInteractive" />
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { isAdminUiEnabled } from "@/lib/server/admin-auth";
|
import { isAdminUiEnabled } from "@/lib/server/admin-auth";
|
||||||
|
import { PublicPageHeader, PublicSurface } from "@/components/public-surface";
|
||||||
|
|
||||||
export default function SignInPage() {
|
export default function SignInPage() {
|
||||||
if (!isAdminUiEnabled()) {
|
if (!isAdminUiEnabled()) {
|
||||||
|
|
@ -7,13 +8,23 @@ export default function SignInPage() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen items-center justify-center bg-muted/30 px-6">
|
<div className="px-4 py-8 md:py-12">
|
||||||
<div className="max-w-md rounded-2xl border border-border bg-background p-8 text-center shadow-sm">
|
<div className="mx-auto flex min-h-[calc(100dvh-7rem)] max-w-3xl items-start justify-center md:items-center">
|
||||||
<h1 className="text-2xl font-semibold">Admin Sign-In</h1>
|
<div className="w-full max-w-xl space-y-8">
|
||||||
<p className="mt-3 text-sm text-muted-foreground">
|
<PublicPageHeader
|
||||||
Admin sign-in is not configured in this deployment. Enable the admin
|
align="center"
|
||||||
UI and wire an auth provider before using this area.
|
eyebrow="Customer Flow"
|
||||||
</p>
|
title="Admin Sign-In"
|
||||||
|
description="This route keeps the same Rocky shell as the rest of the site. Auth still needs to be wired before this area becomes usable."
|
||||||
|
/>
|
||||||
|
|
||||||
|
<PublicSurface className="p-6 text-center md:p-8">
|
||||||
|
<h2 className="text-2xl font-semibold">Admin sign-in is not configured</h2>
|
||||||
|
<p className="mt-3 text-sm text-muted-foreground">
|
||||||
|
Enable the admin UI and connect an auth provider before using this area.
|
||||||
|
</p>
|
||||||
|
</PublicSurface>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ import { Card, CardContent } from "@/components/ui/card";
|
||||||
import { ReviewsSection } from "@/components/reviews-section";
|
import { ReviewsSection } from "@/components/reviews-section";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { RequestMachineForm } from "@/components/forms/request-machine-form";
|
||||||
|
import { PublicInset, PublicPageHeader, PublicSurface } from "@/components/public-surface";
|
||||||
|
|
||||||
interface LocationPageProps {
|
interface LocationPageProps {
|
||||||
params: Promise<{ location: string }>;
|
params: Promise<{ location: string }>;
|
||||||
|
|
@ -168,28 +170,23 @@ export default async function LocationPage({ params }: LocationPageProps) {
|
||||||
|
|
||||||
<article className="container mx-auto px-4 py-8 md:py-12">
|
<article className="container mx-auto px-4 py-8 md:py-12">
|
||||||
{/* Hero Section */}
|
{/* Hero Section */}
|
||||||
<header className="mb-12 md:mb-16 text-center">
|
<PublicPageHeader
|
||||||
<h1 className="text-4xl md:text-5xl font-bold tracking-tight text-balance mb-4">
|
align="center"
|
||||||
Vending Machine Supplier in {locationData.city}, {locationData.state}
|
className="mb-12 md:mb-16"
|
||||||
</h1>
|
eyebrow="Local Service Area"
|
||||||
<p className="text-lg md:text-xl text-muted-foreground max-w-3xl mx-auto leading-relaxed text-pretty">
|
title={`Vending Machine Supplier in ${locationData.city}, ${locationData.state}`}
|
||||||
Need a vending machine supplier in {locationData.city}, {locationData.state}? Rocky Mountain Vending has
|
description={`Need a vending machine supplier in ${locationData.city}, ${locationData.state}? Rocky Mountain Vending has been helping local businesses and schools since 2019 with quality vending solutions. We bring healthy snacks, cold drinks, and dependable service right to your door—no hassle, no fuss.`}
|
||||||
been helping local businesses and schools since 2019 with quality vending solutions. We bring healthy
|
/>
|
||||||
snacks, cold drinks, and dependable service right to your door—no hassle, no fuss.
|
|
||||||
</p>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
{/* Local Anecdote */}
|
{/* Local Anecdote */}
|
||||||
<div className="mb-12 max-w-4xl mx-auto">
|
<div className="mb-12 max-w-4xl mx-auto">
|
||||||
<Card className="border-secondary/20 bg-secondary/5">
|
<PublicSurface className="p-6 md:p-8">
|
||||||
<CardContent className="p-6 md:p-8">
|
|
||||||
<p className="text-base md:text-lg leading-relaxed">
|
<p className="text-base md:text-lg leading-relaxed">
|
||||||
A while back, we worked with a {locationData.anecdote.customer} near {locationData.anecdote.location}.
|
A while back, we worked with a {locationData.anecdote.customer} near {locationData.anecdote.location}.
|
||||||
We set them up with a {locationData.anecdote.solution}. Now {locationData.anecdote.outcome}. That's
|
We set them up with a {locationData.anecdote.solution}. Now {locationData.anecdote.outcome}. That's
|
||||||
what we do best—make vending simple.
|
what we do best—make vending simple.
|
||||||
</p>
|
</p>
|
||||||
</CardContent>
|
</PublicSurface>
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Services Section */}
|
{/* Services Section */}
|
||||||
|
|
@ -350,33 +347,19 @@ export default async function LocationPage({ params }: LocationPageProps) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* CRM Form */}
|
{/* CRM Form */}
|
||||||
<Card className="border-secondary/20">
|
<PublicSurface className="p-6 md:p-8">
|
||||||
<CardContent className="p-8">
|
|
||||||
<div className="text-center mb-6">
|
<div className="text-center mb-6">
|
||||||
<h3 className="text-2xl font-bold mb-2">Get Your Free Vending Machine</h3>
|
<h3 className="text-2xl font-bold mb-2">Get Your Free Vending Machine</h3>
|
||||||
<p className="text-muted-foreground">
|
<p className="text-muted-foreground">
|
||||||
Fill out the form below and we'll contact you within 24 hours to discuss your needs.
|
Tell us about your location and we'll follow up within one business day to talk through the best machine mix.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<iframe
|
<PublicInset className="mb-5 p-5 text-sm leading-relaxed text-muted-foreground">
|
||||||
src="https://link.sluice-box.io/widget/form/T76mIdPvC5iBwAI2wFPg"
|
Free placement is for qualifying business locations. Share your foot traffic, preferred machine types,
|
||||||
style={{ width: "100%", height: "650px", border: "none", borderRadius: "4px" }}
|
and any site constraints so our team can recommend the right setup.
|
||||||
id="inline-T76mIdPvC5iBwAI2wFPg"
|
</PublicInset>
|
||||||
data-layout="{'id':'INLINE'}"
|
<RequestMachineForm />
|
||||||
data-trigger-type="alwaysShow"
|
</PublicSurface>
|
||||||
data-trigger-value=""
|
|
||||||
data-activation-type="alwaysActivated"
|
|
||||||
data-activation-value=""
|
|
||||||
data-deactivation-type="neverDeactivate"
|
|
||||||
data-deactivation-value=""
|
|
||||||
data-form-name="Request Machine Short"
|
|
||||||
data-height="638"
|
|
||||||
data-layout-iframe-id="inline-T76mIdPvC5iBwAI2wFPg"
|
|
||||||
data-form-id="T76mIdPvC5iBwAI2wFPg"
|
|
||||||
title="Request Free Vending Machine Form"
|
|
||||||
/>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Payment Options */}
|
{/* Payment Options */}
|
||||||
|
|
|
||||||
|
|
@ -1,82 +1,65 @@
|
||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { Card, CardContent } from "@/components/ui/card"
|
|
||||||
import { Phone, MapPin } from "lucide-react"
|
import { Phone, MapPin } from "lucide-react"
|
||||||
import { ContactForm } from "@/components/forms/contact-form"
|
import { ContactForm } from "@/components/forms/contact-form"
|
||||||
|
import { PublicInset, PublicPageHeader, PublicSurface } from "@/components/public-surface"
|
||||||
|
import { businessConfig } from "@/lib/seo-config"
|
||||||
|
|
||||||
export function ContactSection() {
|
export function ContactSection() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="contact" className="py-20 md:py-28 bg-muted/30">
|
<section id="contact" className="py-20 md:py-28 bg-muted/30">
|
||||||
<div className="container mx-auto px-4">
|
<div className="container mx-auto px-4">
|
||||||
|
<PublicPageHeader
|
||||||
|
className="mb-10 max-w-3xl"
|
||||||
|
eyebrow="Detailed Questions"
|
||||||
|
title="Need something more specific? We can sort that out here."
|
||||||
|
description="If you already handled the quick placement request, use this longer form for service details, sales questions, or anything that needs more context."
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="grid gap-12 lg:grid-cols-2 items-start">
|
<div className="grid gap-12 lg:grid-cols-2 items-start">
|
||||||
{/* Left Content */}
|
<PublicSurface className="space-y-6">
|
||||||
<div>
|
|
||||||
<h2 className="text-3xl font-bold tracking-tight md:text-4xl lg:text-5xl mb-4 text-balance">
|
|
||||||
Have Detailed Questions? Let's Talk!
|
|
||||||
</h2>
|
|
||||||
<p className="text-lg text-muted-foreground mb-8 text-pretty leading-relaxed">
|
|
||||||
Already submitted the quick form above? Great! We'll contact you soon. If you have specific requirements or detailed questions, feel free to share more below—we're here to help.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<Card className="border-border/50 transition-colors">
|
<PublicInset className="flex items-start gap-4 p-5">
|
||||||
<CardContent className="p-6">
|
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-primary/10 text-primary flex-shrink-0">
|
||||||
<div className="flex items-start gap-4">
|
<Phone className="h-6 w-6" />
|
||||||
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-secondary/10 flex-shrink-0">
|
</div>
|
||||||
<Phone className="h-6 w-6 text-secondary" />
|
<div>
|
||||||
</div>
|
<div className="font-semibold mb-1 text-foreground">Call or Text Us</div>
|
||||||
<div>
|
<a href={businessConfig.publicCallUrl} className="text-muted-foreground hover:text-foreground transition-colors">
|
||||||
<div className="font-semibold mb-1">Call or Text Us</div>
|
{businessConfig.publicCallNumber}
|
||||||
<a
|
</a>
|
||||||
href="tel:+14352339668"
|
<p className="text-xs text-muted-foreground mt-1">Mon-Fri: 8:00 AM - 5:00 PM</p>
|
||||||
className="text-muted-foreground hover:text-foreground transition-colors"
|
<a href={businessConfig.publicSmsUrl} className="text-xs hover:underline mt-2 block">
|
||||||
>
|
Or send a text message
|
||||||
(435) 233-9668
|
</a>
|
||||||
</a>
|
</div>
|
||||||
<p className="text-xs text-muted-foreground mt-1">Mon-Fri: 8:00 AM - 5:00 PM</p>
|
</PublicInset>
|
||||||
<a
|
|
||||||
href="sms:+14352339668"
|
|
||||||
className="text-xs hover:underline mt-2 block"
|
|
||||||
>
|
|
||||||
Or send a text message
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card className="border-border/50 transition-colors">
|
<PublicInset className="flex items-start gap-4 p-5">
|
||||||
<CardContent className="p-6">
|
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-primary/10 text-primary flex-shrink-0">
|
||||||
<div className="flex items-start gap-4">
|
<MapPin className="h-6 w-6" />
|
||||||
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-secondary/10 flex-shrink-0">
|
</div>
|
||||||
<MapPin className="h-6 w-6 text-secondary" />
|
<div>
|
||||||
</div>
|
<div className="font-semibold mb-1 text-foreground">Service Areas</div>
|
||||||
<div>
|
<p className="text-muted-foreground">Davis, Salt Lake & Utah Counties</p>
|
||||||
<div className="font-semibold mb-1">Service Areas</div>
|
<p className="text-xs text-muted-foreground mt-1">Serving 20+ cities across Utah</p>
|
||||||
<p className="text-muted-foreground">Davis, Salt Lake & Utah Counties</p>
|
</div>
|
||||||
<p className="text-xs text-muted-foreground mt-1">Serving 20+ cities across Utah</p>
|
</PublicInset>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</PublicSurface>
|
||||||
|
|
||||||
{/* Right Form - Long Form */}
|
<PublicSurface className="p-5 md:p-7">
|
||||||
<Card className="border-border/50">
|
<div className="mb-4">
|
||||||
<CardContent className="pt-6">
|
<h3 className="text-xl font-semibold mb-2">Contact Form</h3>
|
||||||
<div className="mb-4">
|
<p className="text-sm text-muted-foreground">
|
||||||
<h3 className="text-xl font-semibold mb-2">Contact Form</h3>
|
Tell us more about your needs and we'll get back to you within 24 hours.
|
||||||
<p className="text-sm text-muted-foreground">
|
</p>
|
||||||
Tell us more about your needs and we'll get back to you within 24 hours.
|
</div>
|
||||||
</p>
|
<div className="min-h-[738px]">
|
||||||
</div>
|
<ContactForm onSubmit={(data) => console.log('Contact form submitted:', data)} />
|
||||||
<div className="min-h-[738px]">
|
</div>
|
||||||
<ContactForm onSubmit={(data) => console.log('Contact form submitted:', data)} />
|
</PublicSurface>
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,10 @@ interface ContactFormData {
|
||||||
interface ContactFormProps {
|
interface ContactFormProps {
|
||||||
onSubmit?: (data: ContactFormData) => void
|
onSubmit?: (data: ContactFormData) => void
|
||||||
className?: string
|
className?: string
|
||||||
|
defaultIntent?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ContactForm({ onSubmit, className }: ContactFormProps) {
|
export function ContactForm({ onSubmit, className, defaultIntent = "" }: ContactFormProps) {
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||||
const [isSubmitted, setIsSubmitted] = useState(false)
|
const [isSubmitted, setIsSubmitted] = useState(false)
|
||||||
const [submitError, setSubmitError] = useState<string | null>(null)
|
const [submitError, setSubmitError] = useState<string | null>(null)
|
||||||
|
|
@ -58,7 +59,7 @@ export function ContactForm({ onSubmit, className }: ContactFormProps) {
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
source: "website",
|
source: "website",
|
||||||
page: "",
|
page: "",
|
||||||
intent: "",
|
intent: defaultIntent,
|
||||||
serviceTextConsent: false,
|
serviceTextConsent: false,
|
||||||
marketingTextConsent: false,
|
marketingTextConsent: false,
|
||||||
consentVersion: SMS_CONSENT_VERSION,
|
consentVersion: SMS_CONSENT_VERSION,
|
||||||
|
|
@ -76,13 +77,16 @@ export function ContactForm({ onSubmit, className }: ContactFormProps) {
|
||||||
setValue("page", pathname)
|
setValue("page", pathname)
|
||||||
setValue("consentSourcePage", pathname)
|
setValue("consentSourcePage", pathname)
|
||||||
setValue("consentVersion", SMS_CONSENT_VERSION)
|
setValue("consentVersion", SMS_CONSENT_VERSION)
|
||||||
|
if (defaultIntent) {
|
||||||
|
setValue("intent", defaultIntent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [setValue])
|
}, [defaultIntent, setValue])
|
||||||
|
|
||||||
const buildResetValues = () => ({
|
const buildResetValues = () => ({
|
||||||
source: "website",
|
source: "website",
|
||||||
page: typeof window !== "undefined" ? window.location.pathname : "",
|
page: typeof window !== "undefined" ? window.location.pathname : "",
|
||||||
intent: "",
|
intent: defaultIntent,
|
||||||
serviceTextConsent: false,
|
serviceTextConsent: false,
|
||||||
marketingTextConsent: false,
|
marketingTextConsent: false,
|
||||||
consentVersion: SMS_CONSENT_VERSION,
|
consentVersion: SMS_CONSENT_VERSION,
|
||||||
|
|
|
||||||
|
|
@ -206,9 +206,9 @@ function ManualCard({
|
||||||
const thumbnailUrl = getThumbnailUrl(manual)
|
const thumbnailUrl = getThumbnailUrl(manual)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="overflow-hidden rounded-[1.75rem] border-border/70 bg-[linear-gradient(180deg,rgba(255,255,255,0.96),rgba(247,244,236,0.9))] shadow-[0_18px_45px_rgba(0,0,0,0.08)] transition-all hover:-translate-y-0.5 hover:shadow-[0_24px_60px_rgba(0,0,0,0.12)]">
|
<Card className="overflow-hidden rounded-[1.75rem] border border-border/70 bg-background shadow-[0_18px_45px_rgba(15,23,42,0.08)] transition-all hover:-translate-y-0.5 hover:shadow-[0_24px_60px_rgba(15,23,42,0.12)]">
|
||||||
{thumbnailUrl && (
|
{thumbnailUrl && (
|
||||||
<div className="relative h-48 min-h-[192px] w-full overflow-hidden bg-[radial-gradient(circle_at_top_left,rgba(196,154,52,0.16),transparent_55%),linear-gradient(180deg,rgba(247,244,236,0.72),rgba(255,255,255,0.96))]">
|
<div className="relative h-48 min-h-[192px] w-full overflow-hidden bg-[radial-gradient(circle_at_top_left,rgba(196,154,52,0.12),transparent_52%),linear-gradient(180deg,rgba(255,255,255,0.98),rgba(255,255,255,0.98))]">
|
||||||
<Image
|
<Image
|
||||||
src={thumbnailUrl}
|
src={thumbnailUrl}
|
||||||
alt={manual.filename.replace(/\.pdf$/i, '')}
|
alt={manual.filename.replace(/\.pdf$/i, '')}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import {
|
||||||
MapPin,
|
MapPin,
|
||||||
Calendar
|
Calendar
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
|
import { PublicPageHeader, PublicSurface } from '@/components/public-surface'
|
||||||
|
|
||||||
interface Order {
|
interface Order {
|
||||||
id: string
|
id: string
|
||||||
|
|
@ -209,17 +210,17 @@ export function OrderTracking() {
|
||||||
}, [order])
|
}, [order])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="max-w-4xl mx-auto px-4 py-8">
|
<div className="max-w-4xl mx-auto px-4 py-10 md:py-14">
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div className="text-center">
|
<PublicPageHeader
|
||||||
<h1 className="text-3xl font-bold tracking-tight">Track Your Order</h1>
|
align="center"
|
||||||
<p className="text-muted-foreground mt-2">
|
eyebrow="Customer Orders"
|
||||||
Enter your order ID to track shipment status and delivery information
|
title="Track Your Order"
|
||||||
</p>
|
description="Enter your order ID to check shipment status, delivery updates, and the current stage of your order."
|
||||||
</div>
|
/>
|
||||||
|
|
||||||
{/* Search Form */}
|
{/* Search Form */}
|
||||||
<Card>
|
<PublicSurface as="section" className="p-5 md:p-7">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>Find Your Order</CardTitle>
|
<CardTitle>Find Your Order</CardTitle>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
|
|
@ -258,10 +259,10 @@ export function OrderTracking() {
|
||||||
<AlertDescription>
|
<AlertDescription>
|
||||||
Order found successfully! Scroll down for tracking details.
|
Order found successfully! Scroll down for tracking details.
|
||||||
</AlertDescription>
|
</AlertDescription>
|
||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</PublicSurface>
|
||||||
|
|
||||||
{/* Order Details */}
|
{/* Order Details */}
|
||||||
{order && (
|
{order && (
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ export function PublicSurface({
|
||||||
return (
|
return (
|
||||||
<Component
|
<Component
|
||||||
className={cn(
|
className={cn(
|
||||||
"rounded-[2rem] border border-border/70 bg-[linear-gradient(180deg,rgba(255,255,255,0.96),rgba(247,244,236,0.92))] p-5 shadow-[0_24px_60px_rgba(0,0,0,0.08)] md:p-7",
|
"rounded-[2rem] border border-border/70 bg-background p-5 shadow-[0_24px_60px_rgba(15,23,42,0.08)] md:p-7",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
@ -72,7 +72,7 @@ export function PublicInset({
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"rounded-[1.5rem] border border-border/60 bg-background/85 p-4 shadow-sm",
|
"rounded-[1.5rem] border border-border/60 bg-background p-4 shadow-sm",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { useEffect } from "react"
|
||||||
import { Star } from "lucide-react"
|
import { Star } from "lucide-react"
|
||||||
import { Badge } from "@/components/ui/badge"
|
import { Badge } from "@/components/ui/badge"
|
||||||
import { ReviewSchema } from "@/components/review-schema"
|
import { ReviewSchema } from "@/components/review-schema"
|
||||||
|
import { PublicPageHeader, PublicSurface } from "@/components/public-surface"
|
||||||
|
|
||||||
export function ReviewsSection() {
|
export function ReviewsSection() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -31,28 +32,29 @@ export function ReviewsSection() {
|
||||||
<ReviewSchema aggregateRating={aggregateRating} />
|
<ReviewSchema aggregateRating={aggregateRating} />
|
||||||
<section className="py-16 md:py-24 bg-card/30">
|
<section className="py-16 md:py-24 bg-card/30">
|
||||||
<div className="container mx-auto px-4">
|
<div className="container mx-auto px-4">
|
||||||
<div className="text-center mb-12">
|
<PublicPageHeader
|
||||||
<div className="inline-flex items-center justify-center gap-2 mb-4">
|
align="center"
|
||||||
<Star className="h-6 w-6 text-secondary fill-secondary" />
|
eyebrow="Google Reviews"
|
||||||
<h2 className="text-3xl md:text-4xl font-bold tracking-tight text-balance">What Our Customers Say</h2>
|
title="What Our Customers Say"
|
||||||
|
description="See what Utah businesses have to say about Rocky Mountain Vending, all in the same clean review shell used across the rest of the site."
|
||||||
|
>
|
||||||
|
<div className="mt-4 flex justify-center">
|
||||||
|
<Badge variant="secondary" className="rounded-full px-4 py-1.5 text-sm">
|
||||||
|
<Star className="mr-2 h-4 w-4 fill-current text-primary" />
|
||||||
|
{aggregateRating.ratingValue} / {aggregateRating.bestRating} ({aggregateRating.reviewCount}+ Reviews)
|
||||||
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
<Badge variant="secondary" className="mb-4">
|
</PublicPageHeader>
|
||||||
{aggregateRating.ratingValue} / {aggregateRating.bestRating} ({aggregateRating.reviewCount}+ Reviews)
|
|
||||||
</Badge>
|
|
||||||
<p className="text-lg text-muted-foreground max-w-2xl mx-auto text-pretty leading-relaxed">
|
|
||||||
Don't just take our word for it—see what Utah businesses have to say about our service
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="max-w-5xl mx-auto">
|
<PublicSurface className="mx-auto mt-10 max-w-5xl overflow-hidden p-5 md:p-7">
|
||||||
<iframe
|
<iframe
|
||||||
className="lc_reviews_widget w-full min-w-full min-h-[400px]"
|
className="lc_reviews_widget min-h-[400px] w-full rounded-[1.5rem] border border-border/60 bg-background"
|
||||||
src="https://reputationhub.site/reputation/widgets/review_widget/YAoWLgNSid8oG44j9BjG"
|
src="https://reputationhub.site/reputation/widgets/review_widget/YAoWLgNSid8oG44j9BjG"
|
||||||
frameBorder="0"
|
frameBorder="0"
|
||||||
scrolling="no"
|
scrolling="no"
|
||||||
title="Customer Reviews"
|
title="Customer Reviews"
|
||||||
/>
|
/>
|
||||||
</div>
|
</PublicSurface>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ function Card({ className, ...props }: React.ComponentProps<'div'>) {
|
||||||
<div
|
<div
|
||||||
data-slot="card"
|
data-slot="card"
|
||||||
className={cn(
|
className={cn(
|
||||||
'bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm',
|
'bg-card text-card-foreground flex flex-col gap-6 rounded-[1.75rem] border border-border/70 py-6 shadow-[0_18px_45px_rgba(15,23,42,0.08)]',
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
||||||
|
|
@ -41,17 +41,17 @@ export function VendingMachinesPage() {
|
||||||
</div>
|
</div>
|
||||||
</PublicSurface>
|
</PublicSurface>
|
||||||
|
|
||||||
<PublicSurface className="bg-[linear-gradient(145deg,rgba(78,137,66,0.98),rgba(31,74,32,0.98))] text-white">
|
<PublicSurface>
|
||||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-white/75">Get Started</p>
|
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-primary/80">Get Started</p>
|
||||||
<h2 className="mt-3 text-3xl font-semibold tracking-tight text-balance">Ready to talk through the right machine setup?</h2>
|
<h2 className="mt-3 text-3xl font-semibold tracking-tight text-balance">Ready to talk through the right machine setup?</h2>
|
||||||
<p className="mt-3 text-base leading-relaxed text-white/82">
|
<p className="mt-3 text-base leading-relaxed text-muted-foreground">
|
||||||
We can help with free placement, sales questions, feature comparisons, and planning the best layout for your location.
|
We can help with free placement, sales questions, feature comparisons, and planning the best layout for your location.
|
||||||
</p>
|
</p>
|
||||||
<div className="mt-6 flex flex-col gap-3 sm:flex-row">
|
<div className="mt-6 flex flex-col gap-3 sm:flex-row">
|
||||||
<Button asChild size="lg" className="h-11 rounded-full bg-white px-6 text-primary hover:bg-white/92">
|
<Button asChild size="lg" className="h-11 rounded-full px-6">
|
||||||
<Link href="/contact-us#contact-form">Contact Us</Link>
|
<Link href="/contact-us#contact-form">Contact Us</Link>
|
||||||
</Button>
|
</Button>
|
||||||
<Button asChild size="lg" variant="outline" className="h-11 rounded-full border-white/50 bg-transparent px-6 text-white hover:bg-white/10">
|
<Button asChild size="lg" variant="outline" className="h-11 rounded-full px-6">
|
||||||
<Link href="/#request-machine">Get Free Machine</Link>
|
<Link href="/#request-machine">Get Free Machine</Link>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue