Queue-North-Website/src/pages/Support.jsx

372 lines
16 KiB
React
Raw Normal View History

import { Helmet } from 'react-helmet-async'
2026-05-12 01:04:17 -05:00
import { useState } from 'react'
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'
import { Link } from 'react-router-dom'
import { submitSupport } from '@/lib/api'
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',
company_website: '', // Honeypot field - hidden from humans, bots will fill it
2026-05-12 01:04:17 -05:00
})
const [errors, setErrors] = useState({
name: '',
company: '',
email: '',
issue: '',
})
// Debounce validation errors so they don't flash on every keystroke
const debouncedErrors = useDebounce(errors, 300)
const [isSubmitting, setIsSubmitting] = useState(false)
2026-05-12 01:04:17 -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()
if (!validateForm()) return
handleSubmitForm()
}
const handleSubmitForm = async () => {
setIsSubmitting(true)
try {
await submitSupport(formState)
toast.success("Thanks! We\'ll get back to you soon.")
setFormState({
name: '',
company: '',
email: '',
phone: '',
issue: '',
priority: 'medium',
})
setErrors({
name: '',
company: '',
email: '',
issue: '',
})
} catch (error) {
toast.error(error.message || 'Failed to submit form. Please try again.')
} finally {
setIsSubmitting(false)
}
2026-05-12 01:04:17 -05:00
}
const handleChange = (e) => {
const { name, value } = e.target
setFormState(prev => ({ ...prev, [name]: value }))
// Clear error for this field as user types
if (errors[name]) {
setErrors(prev => ({ ...prev, [name]: '' }))
}
2026-05-12 01:04:17 -05:00
}
return (
<>
<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/assets/og-image.png" />
<meta property="og:site_name" content="Queue North Technologies" />
</Helmet>
2026-05-12 01:04:17 -05:00
{/* Page Hero */}
<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>
<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>
<p className="text-sm font-medium"><a href="tel:+13217308020" className="text-primary-cyan hover:text-cyan-700 underline">(321) 730-8020</a></p>
</div>
<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"><a href="mailto:info@queuenorth.com" className="text-primary-cyan hover:text-cyan-700 underline">info@queuenorth.com</a></p>
2026-05-12 01:04:17 -05:00
</div>
</div>
<div className="mb-8">
<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">
Request Consultation
</Link>
</div>
2026-05-12 01:04:17 -05:00
</div>
</div>
</section>
2026-05-12 01:04:17 -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>
<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">
<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-white text-primary-navy font-medium rounded-md hover:bg-gray-50 transition-colors"
>
Already a member? Sign In
</a>
</div>
<p className="text-sm text-soft-text">
Or call our support team <a href="tel:+13217308020" className="text-primary-cyan hover:text-cyan-700 underline">(321) 730-8020</a>
</p>
</div>
<div>
<h3 className="font-semibold text-text mb-2">Phone</h3>
<p className="text-soft-text"><a href="tel:+13217308020" className="text-primary-cyan hover:text-cyan-700 underline">(321) 730-8020</a></p>
</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
<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>
{/* Right - Form */}
2026-05-12 01:04:17 -05:00
<div>
<form onSubmit={handleSubmit} noValidate className={`space-y-6 ${isSubmitting ? '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
<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
<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
<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..."
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' : ''}`}
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
<div className="absolute opacity-0 h-0 overflow-hidden" aria-hidden="true">
<input
type="text"
name="company_website"
tabIndex="-1"
autoComplete="off"
value={formState.company_website}
onChange={handleChange}
/>
</div>
<Button
type="submit"
className="w-full"
disabled={isSubmitting}
>
{isSubmitting ? 'Submitting...' : 'Submit Request'}
</Button>
</form>
</div>
</div>
2026-05-12 01:04:17 -05:00
</div>
</section>
</>
2026-05-12 01:04:17 -05:00
)
}
export default Support