2026-05-18 13:55:06 -05:00
import SEO from '@/components/SEO'
2026-05-27 20:57:55 -05:00
import { useState , useRef , useEffect } from 'react'
2026-05-13 22:07:35 -05:00
import { toast } from 'sonner'
2026-05-12 01:04:17 -05:00
import { Button } from '@/components/ui/Button'
import { Input } from '@/components/ui/Input'
import { Textarea } from '@/components/ui/Textarea'
2026-05-25 20:20:15 -05:00
import RecaptchaPlaceholder from '@/components/RecaptchaPlaceholder'
2026-05-26 12:22:11 -05:00
import { ArrowRight } from 'lucide-react'
2026-05-12 01:04:17 -05:00
const Contact = ( ) => {
2026-05-27 20:57:55 -05:00
const formRef = useRef ( null )
2026-05-12 01:04:17 -05:00
const [ formState , setFormState ] = useState ( {
2026-05-27 20:57:55 -05:00
'Last Name' : '' ,
Company : '' ,
Email : '' ,
Phone : '' ,
'Zip Code' : '' ,
Description : '' ,
2026-05-12 01:04:17 -05:00
} )
2026-05-13 18:10:04 -05:00
const [ errors , setErrors ] = useState ( {
2026-05-27 20:57:55 -05:00
'Last Name' : '' ,
Company : '' ,
Email : '' ,
'Zip Code' : '' ,
Description : '' ,
2026-05-25 20:20:15 -05:00
recaptcha _token : '' ,
2026-05-13 18:10:04 -05:00
} )
2026-05-27 20:57:55 -05:00
const [ debouncedErrors , setDebouncedErrors ] = useState ( errors )
2026-05-17 22:33:11 -05:00
const [ isSubmitting , setIsSubmitting ] = useState ( false )
2026-05-12 01:04:17 -05:00
2026-05-27 20:57:55 -05:00
useEffect ( ( ) => {
const t = setTimeout ( ( ) => setDebouncedErrors ( errors ) , 300 )
return ( ) => clearTimeout ( t )
} , [ errors ] )
useEffect ( ( ) => {
const iframe = document . getElementById ( 'zoho_webform_iframe' )
if ( ! iframe ) return
const handleLoad = ( ) => {
if ( ! isSubmitting ) return
setIsSubmitting ( false )
toast . success ( "Thanks! We'll be in touch shortly." )
setFormState ( { 'Last Name' : '' , Company : '' , Email : '' , Phone : '' , 'Zip Code' : '' , Description : '' } )
setErrors ( { 'Last Name' : '' , Company : '' , Email : '' , 'Zip Code' : '' , Description : '' , recaptcha _token : '' } )
}
iframe . addEventListener ( 'load' , handleLoad )
return ( ) => iframe . removeEventListener ( 'load' , handleLoad )
} , [ isSubmitting ] )
useEffect ( ( ) => {
const script = document . createElement ( 'script' )
script . id = 'wf_anal'
script . src = 'https://crm.zohopublic.com/crm/WebFormAnalyticsServeServlet?rid=e44e9662530fc5bd9cdd3c43501fc243f89ba03759e7946c4b5e5016795b606b59b54d0e73c68671b2140fac5c8e788agid3b907524e85f9cba94899d77d7200771ee5d0ea567c43ec341d7b2ce40324d40gid26922a9cd1e8191a5f58ecb2524e0d22b8dd027eb943658ee681ab6890436af2gidefa1b1002d15951a0a2ac36cb33cdb4b5c6aeb110e6f4ac68b764345b9429653&tw=e048253ca680b107993ed5922e00cc1ebab3de97e797fce56fc6ad6af0dfc0bc'
document . body . appendChild ( script )
return ( ) => { document . getElementById ( 'wf_anal' ) ? . remove ( ) }
} , [ ] )
2026-05-13 18:10:04 -05:00
const validateForm = ( ) => {
2026-05-27 20:57:55 -05:00
const newErrors = { 'Last Name' : '' , Company : '' , Email : '' , 'Zip Code' : '' , Description : '' , recaptcha _token : '' }
if ( ! formState . Company . trim ( ) ) newErrors . Company = 'Company name is required'
if ( ! formState [ 'Last Name' ] . trim ( ) ) newErrors [ 'Last Name' ] = 'Name is required'
if ( ! formState [ 'Zip Code' ] . trim ( ) ) newErrors [ 'Zip Code' ] = 'ZIP code is required'
if ( ! formState . Description . trim ( ) ) newErrors . Description = 'Message is required'
2026-05-13 18:10:04 -05:00
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
2026-05-27 20:57:55 -05:00
if ( ! formState . Email . trim ( ) ) {
newErrors . Email = 'Email is required'
} else if ( ! emailRegex . test ( formState . Email ) ) {
newErrors . Email = 'Please enter a valid email address'
2026-05-13 18:10:04 -05:00
}
const hasErrors = Object . values ( newErrors ) . some ( error => error !== '' )
setErrors ( newErrors )
if ( hasErrors ) {
toast . error ( 'Please fix the errors in the form' )
return false
}
return true
}
2026-05-12 01:04:17 -05:00
const handleSubmit = ( e ) => {
e . preventDefault ( )
2026-05-13 18:10:04 -05:00
if ( ! validateForm ( ) ) return
2026-05-17 22:33:11 -05:00
setIsSubmitting ( true )
2026-05-27 20:57:55 -05:00
formRef . current . submit ( )
2026-05-12 01:04:17 -05:00
}
const handleChange = ( e ) => {
const { name , value } = e . target
setFormState ( prev => ( { ... prev , [ name ] : value } ) )
2026-05-18 12:11:56 -05:00
if ( errors [ name ] ) setErrors ( prev => ( { ... prev , [ name ] : '' } ) )
2026-05-12 01:04:17 -05:00
}
2026-05-18 12:11:56 -05:00
const contactDetails = [
{
label : 'Phone' ,
icon : (
< svg className = "h-5 w-5 text-primary-cyan" fill = "none" viewBox = "0 0 24 24" stroke = "currentColor" strokeWidth = { 1.5 } >
< path strokeLinecap = "round" strokeLinejoin = "round" d = "M2.25 6.75c0 8.284 6.716 15 15 15h2.25a2.25 2.25 0 002.25-2.25v-1.372c0-.516-.351-.966-.852-1.091l-4.423-1.106c-.44-.11-.902.055-1.173.417l-.97 1.293c-.282.376-.769.542-1.21.38a12.035 12.035 0 01-7.143-7.143c-.162-.441.004-.928.38-1.21l1.293-.97c.363-.271.527-.734.417-1.173L6.963 3.102a1.125 1.125 0 00-1.091-.852H4.5A2.25 2.25 0 002.25 4.5v2.25z" / >
< / svg >
) ,
content : (
< div >
< a href = "tel:+13217308020" className = "block text-white hover:text-primary-cyan transition-colors" > ( 321 ) 730 - 8020 < / a >
< a href = "tel:+18886562850" className = "block text-white/70 text-sm hover:text-primary-cyan transition-colors mt-0.5" > ( 888 ) 656 - 2850 Toll - Free < / a >
< / div >
) ,
} ,
{
label : 'Office' ,
icon : (
< svg className = "h-5 w-5 text-primary-cyan" fill = "none" viewBox = "0 0 24 24" stroke = "currentColor" strokeWidth = { 1.5 } >
< path strokeLinecap = "round" strokeLinejoin = "round" d = "M15 10.5a3 3 0 11-6 0 3 3 0 016 0z" / >
< path strokeLinecap = "round" strokeLinejoin = "round" d = "M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1115 0z" / >
< / svg >
) ,
content : (
< a
href = "https://maps.google.com/?q=7901+4th+St+N+St+Petersburg+FL+33702"
target = "_blank"
rel = "noopener noreferrer"
className = "text-white hover:text-primary-cyan transition-colors leading-relaxed"
>
< span className = "block" > 7901 4 th St N < / span >
< span className = "block" > St . Petersburg , FL 33702 < / span >
< / a >
) ,
} ,
{
label : 'Hours' ,
icon : (
< svg className = "h-5 w-5 text-primary-cyan" fill = "none" viewBox = "0 0 24 24" stroke = "currentColor" strokeWidth = { 1.5 } >
< path strokeLinecap = "round" strokeLinejoin = "round" d = "M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z" / >
< / svg >
) ,
content : < p className = "text-white/80 text-sm" > Mon – Fri : 8 : 00 AM – 6 : 00 PM CT < / p > ,
} ,
]
const trustPoints = [
2026-05-25 17:56:32 -05:00
< div key = "8x8" > < span className = "font-numeric" > 8 x8 < / span > Certified Partner with proven expertise < / div > ,
2026-05-27 14:10:28 -05:00
'Cisco Certified Partner' ,
2026-05-25 17:56:32 -05:00
< div key = "veteran" > < span className = "font-numeric" > 25 + < / span > years of experience < / div > ,
2026-05-18 12:11:56 -05:00
'SMB to Enterprise solutions' ,
'No vendor bias — we recommend what fits' ,
]
2026-05-12 01:04:17 -05:00
return (
2026-05-17 17:11:29 -05:00
< >
2026-05-18 13:55:06 -05:00
< SEO
title = "Contact Queue North | Schedule a Free Consultation"
description = "Contact Queue North Technologies to schedule a free consultation. Call (321) 730-8020 or toll-free (888) 656-2850 for business phone, UCaaS, IT support, and networking solutions."
url = "https://queuenorth.com/contact"
/ >
2026-05-18 09:35:29 -05:00
2026-05-18 12:11:56 -05:00
{ /* Hero */ }
2026-05-26 12:22:11 -05:00
< section className = "relative isolate overflow-hidden bg-primary-navy py-16 lg:py-24" >
< div className = "absolute inset-0 -z-10" >
< img
src = "/assets/hero-tech.webp"
alt = "Queue North communications infrastructure consultation"
className = "h-full w-full object-cover object-center"
/ >
< div className = "absolute inset-0 bg-primary-navy/82" / >
< div className = "absolute inset-0 bg-gradient-to-r from-primary-navy via-primary-navy/92 to-primary-navy/45" / >
< / div >
2026-05-18 12:11:56 -05:00
< div className = "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8" >
2026-05-26 12:22:11 -05:00
< div className = "inline-flex items-center gap-2 rounded-md border border-white/20 bg-white/10 px-3 py-2 text-xs font-semibold uppercase tracking-wide text-primary-cyan mb-6" >
2026-05-27 14:27:30 -05:00
Contact
2026-05-26 12:22:11 -05:00
< / div >
2026-05-18 12:11:56 -05:00
< h1 className = "text-4xl md:text-5xl font-bold text-white mb-4" > Let ' s Talk < / h1 >
< p className = "text-xl text-white/70 max-w-2xl" >
Tell us about your business and we ' ll cut through the noise to find what actually works for you .
< / p >
2026-05-26 12:22:11 -05:00
< a
href = "#contact-form"
className = "mt-8 inline-flex h-11 items-center justify-center gap-2 rounded-md bg-white px-5 text-sm font-semibold text-primary-navy hover:bg-section-alt transition-colors"
>
Send a Message
< ArrowRight className = "h-4 w-4" aria - hidden = "true" / >
< / a >
2026-05-12 01:04:17 -05:00
< / div >
2026-05-17 17:11:29 -05:00
< / section >
2026-05-12 01:04:17 -05:00
2026-05-18 12:11:56 -05:00
{ /* Contact Body */ }
2026-05-26 12:22:11 -05:00
< section id = "contact-form" className = "bg-background py-16 lg:py-24" >
2026-05-18 13:12:18 -05:00
< div className = "max-w-7xl mx-auto px-0 sm:px-6 lg:px-8" >
< div className = "grid grid-cols-1 lg:grid-cols-5 rounded-none sm:rounded-md overflow-hidden shadow-none sm:shadow-xl border-y sm:border border-border" >
2026-05-18 12:11:56 -05:00
2026-05-18 13:12:18 -05:00
{ /* Left: Info panel — order 2 on mobile so form appears first */ }
< div className = "lg:col-span-2 order-2 lg:order-1 bg-primary-navy text-white p-8 lg:p-10 flex flex-col gap-10" >
2026-05-18 12:11:56 -05:00
< div className = "space-y-7" >
{ contactDetails . map ( ( item ) => (
< div key = { item . label } className = "flex items-start gap-4" >
2026-05-18 13:12:18 -05:00
< div className = "w-10 h-10 bg-white/10 rounded-md flex items-center justify-center flex-shrink-0" >
2026-05-18 12:11:56 -05:00
{ item . icon }
< / div >
< div >
< p className = "text-white/50 text-xs uppercase tracking-wider mb-1" > { item . label } < / p >
{ item . content }
< / div >
2026-05-17 17:11:29 -05:00
< / div >
2026-05-18 12:11:56 -05:00
) ) }
2026-05-17 17:11:29 -05:00
< / div >
2026-05-12 01:04:17 -05:00
2026-05-18 12:11:56 -05:00
< div className = "border-t border-white/10" / >
< div >
2026-05-27 14:10:28 -05:00
< p className = "text-white/50 text-xs uppercase tracking-wider mb-5" > Why Queue North Technologies < / p >
2026-05-18 12:11:56 -05:00
< ul className = "space-y-4" >
{ trustPoints . map ( ( point , i ) => (
< li key = { i } className = "flex items-start gap-3 text-white/80 text-sm leading-relaxed" >
< svg className = "h-4 w-4 text-primary-cyan flex-shrink-0 mt-0.5" fill = "none" viewBox = "0 0 24 24" stroke = "currentColor" strokeWidth = { 2.5 } >
< path strokeLinecap = "round" strokeLinejoin = "round" d = "M4.5 12.75l6 6 9-13.5" / >
2026-05-17 17:11:29 -05:00
< / svg >
2026-05-18 12:11:56 -05:00
{ point }
2026-05-17 17:11:29 -05:00
< / li >
) ) }
< / ul >
< / div >
2026-05-12 01:04:17 -05:00
< / div >
2026-05-18 13:12:18 -05:00
{ /* Right: Form panel — order 1 on mobile so it appears first */ }
< div className = "lg:col-span-3 order-1 lg:order-2 bg-white p-6 sm:p-8 lg:p-10" >
2026-05-18 12:11:56 -05:00
< h2 className = "text-2xl font-bold text-primary-navy mb-1" > Send Us a Message < / h2 >
< p className = "text-soft-text text-sm mb-8" > We typically respond within one business day . < / p >
2026-05-27 20:57:55 -05:00
< form
ref = { formRef }
id = "contact-form"
name = "WebToLeads7130861000000581796"
method = "POST"
action = "https://crm.zoho.com/crm/WebToLeadForm"
target = "zoho_webform_iframe"
acceptCharset = "UTF-8"
onSubmit = { handleSubmit }
noValidate
className = { ` space-y-5 ${ isSubmitting ? 'opacity-70 pointer-events-none' : '' } ` }
>
{ /* Zoho required hidden fields */ }
< input type = "hidden" name = "xnQsjsdp" value = "b78607b2ef073f134a736184c22aa442ba026b6b00cfdbcb8078d8dee0bb1bbd" / >
< input type = "hidden" name = "zc_gad" id = "zc_gad" value = "" / >
< input type = "hidden" name = "xmIwtLD" value = "e1201f09c921b74ca7844fca8689433ad14277423595fe88de0e4cd6c58e43e743fb001043cb5229e129ff4ab8b2beea" / >
< input type = "hidden" name = "actionType" value = "TGVhZHM=" / >
< input type = "hidden" name = "returnURL" value = "null" / >
{ /* Honeypot */ }
< input type = "text" name = "aG9uZXlwb3Q" defaultValue = "" tabIndex = { - 1 } autoComplete = "off" aria - hidden = "true" style = { { display : 'none' } } readOnly / >
2026-05-18 12:11:56 -05:00
{ /* Company */ }
2026-05-17 17:11:29 -05:00
< div >
2026-05-27 20:57:55 -05:00
< label htmlFor = "Company" className = "block text-sm font-medium text-text mb-1.5" >
2026-05-18 12:11:56 -05:00
Company Name < span className = "text-red-500" > * < / span >
2026-05-17 17:11:29 -05:00
< / label >
< Input
type = "text"
2026-05-27 20:57:55 -05:00
id = "Company"
name = "Company"
value = { formState . Company }
2026-05-17 17:11:29 -05:00
onChange = { handleChange }
required
placeholder = "Your company name"
2026-05-27 20:57:55 -05:00
className = { debouncedErrors . Company ? 'border-red-500 focus-visible:ring-red-500' : '' }
2026-05-17 17:11:29 -05:00
/ >
2026-05-27 20:57:55 -05:00
{ debouncedErrors . Company && < p className = "text-xs text-red-500 mt-1" > { debouncedErrors . Company } < / p > }
2026-05-17 17:11:29 -05:00
< / div >
2026-05-12 01:04:17 -05:00
2026-05-18 12:11:56 -05:00
{ /* Name + Email */ }
< div className = "grid grid-cols-1 sm:grid-cols-2 gap-4" >
< div >
2026-05-27 20:57:55 -05:00
< label htmlFor = "Last_Name" className = "block text-sm font-medium text-text mb-1.5" >
2026-05-18 12:11:56 -05:00
Name < span className = "text-red-500" > * < / span >
< / label >
< Input
type = "text"
2026-05-27 20:57:55 -05:00
id = "Last_Name"
name = "Last Name"
value = { formState [ 'Last Name' ] }
2026-05-18 12:11:56 -05:00
onChange = { handleChange }
required
placeholder = "Your full name"
2026-05-27 20:57:55 -05:00
className = { debouncedErrors [ 'Last Name' ] ? 'border-red-500 focus-visible:ring-red-500' : '' }
2026-05-18 12:11:56 -05:00
/ >
2026-05-27 20:57:55 -05:00
{ debouncedErrors [ 'Last Name' ] && < p className = "text-xs text-red-500 mt-1" > { debouncedErrors [ 'Last Name' ] } < / p > }
2026-05-18 12:11:56 -05:00
< / div >
< div >
2026-05-27 20:57:55 -05:00
< label htmlFor = "Email" className = "block text-sm font-medium text-text mb-1.5" >
2026-05-18 12:11:56 -05:00
Email < span className = "text-red-500" > * < / span >
< / label >
< Input
type = "email"
2026-05-27 20:57:55 -05:00
id = "Email"
name = "Email"
value = { formState . Email }
2026-05-18 12:11:56 -05:00
onChange = { handleChange }
required
placeholder = "you@company.com"
2026-05-27 20:57:55 -05:00
className = { debouncedErrors . Email ? 'border-red-500 focus-visible:ring-red-500' : '' }
2026-05-18 12:11:56 -05:00
/ >
2026-05-27 20:57:55 -05:00
{ debouncedErrors . Email && < p className = "text-xs text-red-500 mt-1" > { debouncedErrors . Email } < / p > }
2026-05-18 12:11:56 -05:00
< / div >
2026-05-17 17:11:29 -05:00
< / div >
2026-05-25 18:17:16 -05:00
{ /* Phone + ZIP */ }
2026-05-18 12:11:56 -05:00
< div className = "grid grid-cols-1 sm:grid-cols-2 gap-4" >
< div >
2026-05-27 20:57:55 -05:00
< label htmlFor = "Phone" className = "block text-sm font-medium text-text mb-1.5" >
2026-05-18 12:11:56 -05:00
Phone < span className = "text-soft-text font-normal" > ( optional ) < / span >
< / label >
< Input
type = "tel"
2026-05-27 20:57:55 -05:00
id = "Phone"
name = "Phone"
value = { formState . Phone }
2026-05-18 12:11:56 -05:00
onChange = { handleChange }
placeholder = "(555) 123-4567"
/ >
< / div >
< div >
2026-05-27 20:57:55 -05:00
< label htmlFor = "Zip_Code" className = "block text-sm font-medium text-text mb-1.5" >
2026-05-25 18:17:16 -05:00
ZIP Code < span className = "text-red-500" > * < / span >
2026-05-18 12:11:56 -05:00
< / label >
2026-05-25 18:17:16 -05:00
< Input
type = "text"
2026-05-27 20:57:55 -05:00
id = "Zip_Code"
name = "Zip Code"
value = { formState [ 'Zip Code' ] }
2026-05-18 12:11:56 -05:00
onChange = { handleChange }
2026-05-25 18:17:16 -05:00
required
autoComplete = "postal-code"
inputMode = "numeric"
placeholder = "33702"
2026-05-27 20:57:55 -05:00
className = { debouncedErrors [ 'Zip Code' ] ? 'border-red-500 focus-visible:ring-red-500' : '' }
2026-05-25 18:17:16 -05:00
/ >
2026-05-27 20:57:55 -05:00
{ debouncedErrors [ 'Zip Code' ] && < p className = "text-xs text-red-500 mt-1" > { debouncedErrors [ 'Zip Code' ] } < / p > }
2026-05-18 12:11:56 -05:00
< / div >
2026-05-17 17:11:29 -05:00
< / div >
2026-05-18 12:11:56 -05:00
{ /* Message */ }
2026-05-17 17:11:29 -05:00
< div >
2026-05-27 20:57:55 -05:00
< label htmlFor = "Description" className = "block text-sm font-medium text-text mb-1.5" >
2026-05-18 12:11:56 -05:00
Message < span className = "text-red-500" > * < / span >
2026-05-17 17:11:29 -05:00
< / label >
< Textarea
2026-05-27 20:57:55 -05:00
id = "Description"
name = "Description"
value = { formState . Description }
2026-05-17 17:11:29 -05:00
onChange = { handleChange }
required
placeholder = "Tell us about your needs..."
2026-05-27 20:57:55 -05:00
className = { ` w-full rounded-md border bg-background px-3 py-2 text-sm ring-offset-[#F8FAFC] placeholder:text-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-navy focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 ${ debouncedErrors . Description ? 'border-red-500 focus-visible:ring-red-500' : '' } ` }
2026-05-17 17:11:29 -05:00
rows = { 5 }
/ >
2026-05-27 20:57:55 -05:00
{ debouncedErrors . Description && < p className = "text-xs text-red-500 mt-1" > { debouncedErrors . Description } < / p > }
2026-05-17 21:51:53 -05:00
< / div >
2026-05-25 20:20:15 -05:00
< RecaptchaPlaceholder error = { debouncedErrors . recaptcha _token } / >
2026-05-18 12:11:56 -05:00
< Button type = "submit" className = "w-full h-11" disabled = { isSubmitting } >
2026-05-17 22:35:55 -05:00
{ isSubmitting ? (
< >
< svg className = "animate-spin -ml-1 mr-2 h-4 w-4" xmlns = "http://www.w3.org/2000/svg" fill = "none" viewBox = "0 0 24 24" >
< circle className = "opacity-25" cx = "12" cy = "12" r = "10" stroke = "currentColor" strokeWidth = "4" / >
< path className = "opacity-75" fill = "currentColor" d = "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" / >
< / svg >
2026-05-18 12:11:56 -05:00
Sending ...
2026-05-17 22:35:55 -05:00
< / >
) : (
2026-05-18 12:11:56 -05:00
'Send Message'
2026-05-17 22:35:55 -05:00
) }
2026-05-17 17:11:29 -05:00
< / Button >
< / form >
2026-05-27 20:57:55 -05:00
< iframe
name = "zoho_webform_iframe"
id = "zoho_webform_iframe"
title = "Zoho form submission"
style = { { display : 'none' } }
/ >
2026-05-17 17:11:29 -05:00
< / div >
< / div >
2026-05-12 01:04:17 -05:00
< / div >
2026-05-17 17:11:29 -05:00
< / section >
< / >
2026-05-12 01:04:17 -05:00
)
}
export default Contact