2026-05-17 20:03:42 -05:00
import { Helmet } from 'react-helmet-async'
2026-05-12 01:04:17 -05:00
import { useState } from 'react'
import { useMutation } from '@tanstack/react-query'
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'
import { Select } from '@/components/ui/Select'
2026-05-17 18:03:55 -05:00
import { Link } from 'react-router-dom'
2026-05-12 01:04:17 -05:00
import { api } from '@/lib/api'
2026-05-17 16:10:10 -05:00
import { useDebounce } from '@/hooks/useDebounce'
2026-05-12 01:04:17 -05:00
const Support = ( ) => {
const [ formState , setFormState ] = useState ( {
name : '' ,
company : '' ,
email : '' ,
phone : '' ,
issue : '' ,
priority : 'medium' ,
} )
2026-05-13 18:10:04 -05:00
const [ errors , setErrors ] = useState ( {
name : '' ,
company : '' ,
email : '' ,
issue : '' ,
} )
2026-05-17 16:10:10 -05:00
// Debounce validation errors so they don't flash on every keystroke
const debouncedErrors = useDebounce ( errors , 300 )
2026-05-12 01:04:17 -05:00
const mutation = useMutation ( {
mutationFn : ( data ) => api . post ( '/support' , data ) ,
onSuccess : ( ) => {
toast . success ( 'Thanks! We\'ll get back to you soon.' )
setFormState ( {
name : '' ,
company : '' ,
email : '' ,
phone : '' ,
issue : '' ,
priority : 'medium' ,
} )
2026-05-13 18:10:04 -05:00
setErrors ( {
name : '' ,
company : '' ,
email : '' ,
issue : '' ,
} )
2026-05-12 01:04:17 -05:00
} ,
onError : ( error ) => {
toast . error ( error . message || 'Failed to submit form. Please try again.' )
} ,
} )
2026-05-13 18:10:04 -05:00
const validateForm = ( ) => {
const newErrors = {
name : '' ,
company : '' ,
email : '' ,
issue : '' ,
}
// Validate required fields
if ( ! formState . name . trim ( ) ) newErrors . name = 'Name is required'
if ( ! formState . company . trim ( ) ) newErrors . company = 'Company name is required'
if ( ! formState . issue . trim ( ) ) newErrors . issue = 'Please describe your issue'
// Validate email format
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
if ( ! formState . email . trim ( ) ) {
newErrors . email = 'Email is required'
} else if ( ! emailRegex . test ( formState . email ) ) {
newErrors . email = 'Please enter a valid email address'
}
// Validate issue minimum length (10 chars matches server-side Zod rule)
if ( formState . issue . trim ( ) . length < 10 ) {
newErrors . issue = 'Issue description must be at least 10 characters'
}
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-12 01:04:17 -05:00
mutation . mutate ( formState )
}
const handleChange = ( e ) => {
const { name , value } = e . target
setFormState ( prev => ( { ... prev , [ name ] : value } ) )
2026-05-13 18:10:04 -05:00
// Clear error for this field as user types
if ( errors [ name ] ) {
setErrors ( prev => ( { ... prev , [ name ] : '' } ) )
}
2026-05-12 01:04:17 -05:00
}
return (
2026-05-17 17:11:29 -05:00
< >
2026-05-17 20:03:42 -05:00
< Helmet >
< title > IT Support & Help Desk | Queue North Technologies < / title >
< meta name = "description" content = "Get IT support and help desk services from Queue North Technologies. 24/7 monitoring, rapid response SLAs, and dedicated support engineers for your business." / >
< meta property = "og:title" content = "IT Support & Help Desk | Queue North Technologies" / >
< meta property = "og:description" content = "24/7 IT support and help desk services. Rapid response SLAs, dedicated support engineers." / >
< meta property = "og:url" content = "https://queuenorth.com/support" / >
< meta property = "og:type" content = "website" / >
< meta property = "og:image" content = "https://queuenorth.com/logo.svg" / >
< meta property = "og:site_name" content = "Queue North Technologies" / >
< / Helmet >
2026-05-12 01:04:17 -05:00
{ /* Page Hero */ }
2026-05-17 17:11:29 -05:00
< section className = "bg-background py-16 md:py-24" >
< div className = "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8" >
< div >
< h1 className = "text-4xl md:text-5xl font-bold text-primary-navy mb-6" > Support < / h1 >
< p className = "text-xl text-soft-text max-w-3xl mb-8" >
Need help with your communications or infrastructure ? Submit a support request and we ' ll get back to you promptly .
2026-05-12 01:04:17 -05:00
< / p >
2026-05-17 17:11:29 -05:00
< div className = "flex flex-wrap gap-4 mb-8" >
< div className = "flex items-center gap-2 px-4 py-2 bg-primary-navy/10 rounded-lg text-primary-navy" >
< svg className = "h-5 w-5" fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" >
< path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = "2" d = "M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 5.13a1 1 0 01-1.31.42a2 2 0 00-2.63-.54l-2.238 5.03a2 2 0 01-2.71.319L2.663 12H4a2 2 0 012 2v2a2 2 0 01-2 2H2a2 2 0 01-2-2V5z" / >
< / svg >
2026-05-17 20:44:18 -05:00
< p className = "text-sm font-medium" > ( 321 ) 730 - 8020 < / p >
2026-05-17 14:35:29 -05:00
< / div >
2026-05-17 17:11:29 -05:00
< div className = "flex items-center gap-2 px-4 py-2 bg-primary-navy/10 rounded-lg text-primary-navy" >
< svg className = "h-5 w-5" fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" >
< path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = "2" d = "M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" / >
< / svg >
< p className = "text-sm font-medium" > info @ queuenorth . com < / p >
2026-05-12 01:04:17 -05:00
< / div >
< / div >
2026-05-17 17:11:29 -05:00
< div className = "mb-8" >
2026-05-17 18:03:55 -05:00
< Link to = "/contact" className = "inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-6 bg-primary-navy text-white hover:bg-primary-navy-dark transition-colors" >
2026-05-17 17:11:29 -05:00
Request Consultation
2026-05-17 18:03:55 -05:00
< / Link >
2026-05-17 17:11:29 -05:00
< / div >
2026-05-12 01:04:17 -05:00
< / div >
< / div >
2026-05-17 17:11:29 -05:00
< / section >
2026-05-12 01:04:17 -05:00
2026-05-17 17:11:29 -05:00
{ /* Support Form */ }
< section className = "bg-background py-16" >
< div className = "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8" >
< div className = "grid grid-cols-1 lg:grid-cols-2 gap-12" >
{ /* Left - Info */ }
2026-05-12 01:04:17 -05:00
< div >
2026-05-17 17:11:29 -05:00
< div className = "mb-8" >
< h2 className = "text-2xl font-bold text-primary-navy mb-4" > Support Services < / h2 >
< p className = "text-soft-text mb-6" >
We provide comprehensive support for all our services , including 24 / 7 monitoring , rapid response , and dedicated support engineers .
< / p >
< div className = "space-y-4" >
2026-05-17 20:55:03 -05:00
< div className = "bg-section-alt rounded-lg p-6" >
< h3 className = "font-semibold text-primary-navy mb-4" > Queue North Support Center < / h3 >
< p className = "text-text mb-6" >
Need to sign up for the Queue North Support Center ? Create an account to access our knowledge base and submit support tickets .
< / p >
< div className = "flex flex-wrap gap-4 mb-4" >
< a
href = "https://queuenorthtechnologiesllc.zohodesk.com/portal/en/signup"
target = "_blank"
rel = "noopener noreferrer"
className = "inline-flex items-center justify-center px-6 py-3 bg-primary-navy text-white font-medium rounded-md hover:bg-primary-navy-dark transition-colors"
>
Sign Up
< svg className = "ml-2 h-4 w-4" fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" >
< path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = "2" d = "M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" / >
< / svg >
< / a >
< a
href = "https://queuenorthtechnologiesllc.zohodesk.com/portal/en/signin"
target = "_blank"
rel = "noopener noreferrer"
className = "inline-flex items-center justify-center px-6 py-3 bg-secondary-bg text-primary-navy font-medium rounded-md hover:bg-secondary-bg/80 transition-colors"
>
Already a member ? Sign In
< / a >
< / div >
< p className = "text-sm text-soft-text" >
Or call our support team ( 321 ) 730 - 8020
< / p >
2026-05-17 17:11:29 -05:00
< / div >
< div >
< h3 className = "font-semibold text-text mb-2" > Phone < / h3 >
2026-05-17 20:44:18 -05:00
< p className = "text-soft-text" > ( 321 ) 730 - 8020 < / p >
2026-05-17 17:11:29 -05:00
< / div >
< div >
< h3 className = "font-semibold text-text mb-2" > Support Hours < / h3 >
< p className = "text-soft-text" > 24 / 7 Monitoring with rapid response SLAs < / p >
< / div >
< div >
< h3 className = "font-semibold text-text mb-2" > Priority Levels < / h3 >
< p className = "text-soft-text" >
Low : General inquiries ( response within 24 hours ) < br / >
Medium : Standard issues ( response within 4 hours ) < br / >
High : Critical issues ( response within 1 hour )
< / p >
< / div >
< / div >
< / div >
2026-05-12 01:04:17 -05:00
2026-05-17 17:11:29 -05:00
< div className = "bg-section-alt rounded-lg p-6" >
< h3 className = "font-semibold text-primary-navy mb-4" > What We Support < / h3 >
< ul className = "space-y-3" >
{ [
'8x8 Communications Platform' ,
'VoIP Phone Systems' ,
'Contact Center Solutions' ,
'Network Infrastructure' ,
'Cloud Migration Support' ,
] . map ( ( item , index ) => (
< li key = { index } className = "flex items-center gap-3 text-text" >
< svg className = "h-5 w-5 text-primary-navy" fill = "none" stroke = "currentColor" viewBox = "0 0 24 24" >
< path strokeLinecap = "round" strokeLinejoin = "round" strokeWidth = "2" d = "M5 13l4 4L19 7" / >
< / svg >
{ item }
< / li >
) ) }
< / ul >
< / div >
2026-05-12 01:04:17 -05:00
< / div >
2026-05-17 17:11:29 -05:00
{ /* Right - Form */ }
2026-05-12 01:04:17 -05:00
< div >
2026-05-17 17:11:29 -05:00
< form onSubmit = { handleSubmit } noValidate className = { ` space-y-6 ${ mutation . isPending ? 'opacity-70 pointer-events-none' : '' } ` } >
< div >
< label htmlFor = "name" className = "block text-sm font-medium text-text mb-2" >
Name < span className = "text-red-600" > * < / span >
< / label >
< Input
type = "text"
id = "name"
name = "name"
value = { formState . name }
onChange = { handleChange }
required
placeholder = "Your full name"
className = { errors . name ? 'border-red-500 focus-visible:ring-red-500' : '' }
/ >
{ debouncedErrors . name && (
< p className = "text-xs text-red-600 mt-1" > { debouncedErrors . name } < / p >
) }
< / div >
2026-05-12 01:04:17 -05:00
2026-05-17 17:11:29 -05:00
< div >
< label htmlFor = "company" className = "block text-sm font-medium text-text mb-2" >
Company Name < span className = "text-red-600" > * < / span >
< / label >
< Input
type = "text"
id = "company"
name = "company"
value = { formState . company }
onChange = { handleChange }
required
placeholder = "Your company name"
className = { errors . company ? 'border-red-500 focus-visible:ring-red-500' : '' }
/ >
{ debouncedErrors . company && (
< p className = "text-xs text-red-600 mt-1" > { debouncedErrors . company } < / p >
) }
< / div >
2026-05-12 01:04:17 -05:00
2026-05-17 17:11:29 -05:00
< div >
< label htmlFor = "email" className = "block text-sm font-medium text-text mb-2" >
Email < span className = "text-red-600" > * < / span >
< / label >
< Input
type = "email"
id = "email"
name = "email"
value = { formState . email }
onChange = { handleChange }
required
placeholder = "your.email@example.com"
className = { errors . email ? 'border-red-500 focus-visible:ring-red-500' : '' }
/ >
{ debouncedErrors . email && (
< p className = "text-xs text-red-600 mt-1" > { debouncedErrors . email } < / p >
) }
< / div >
2026-05-12 01:04:17 -05:00
2026-05-17 17:11:29 -05:00
< div >
< label htmlFor = "phone" className = "block text-sm font-medium text-text mb-2" >
Phone ( Optional )
< / label >
< Input
type = "tel"
id = "phone"
name = "phone"
value = { formState . phone }
onChange = { handleChange }
placeholder = "(555) 123-4567"
/ >
< / div >
< div >
< label htmlFor = "priority" className = "block text-sm font-medium text-text mb-2" >
Priority < span className = "text-red-600" > * < / span >
< / label >
< Select
id = "priority"
name = "priority"
value = { formState . priority }
onChange = { handleChange }
>
< option value = "low" > Low - General inquiries ( 24 hours ) < / option >
< option value = "medium" > Medium - Standard issues ( 4 hours ) < / option >
< option value = "high" > High - Critical issues ( 1 hour ) < / option >
< / Select >
< / div >
< div >
< label htmlFor = "issue" className = "block text-sm font-medium text-text mb-2" >
Describe Your Issue < span className = "text-red-600" > * < / span >
< / label >
< Textarea
id = "issue"
name = "issue"
value = { formState . issue }
onChange = { handleChange }
required
placeholder = "Please describe your issue in detail..."
2026-05-17 17:42:03 -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 ${ errors . issue ? 'border-red-500 focus-visible:ring-red-500' : '' } ` }
2026-05-17 17:11:29 -05:00
rows = { 5 }
/ >
{ debouncedErrors . issue && (
< p className = "text-xs text-red-600 mt-1" > { debouncedErrors . issue } < / p >
) }
< / div >
2026-05-12 01:04:17 -05:00
2026-05-17 17:11:29 -05:00
< Button
type = "submit"
className = "w-full"
disabled = { mutation . isPending }
>
{ mutation . isPending ? 'Submitting...' : 'Submit Request' }
< / Button >
< / form >
< / 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 Support