656 lines
28 KiB
TypeScript
656 lines
28 KiB
TypeScript
"use client";
|
|
import { usePathname } from "next/navigation";
|
|
import { useState, useEffect, useRef } from "react";
|
|
import Image from "next/legacy/image";
|
|
import { contactApiService, ContactFormData } from "@/lib/api/contactService";
|
|
|
|
const ContactSection = () => {
|
|
const pathname = usePathname();
|
|
const isServiceSingle = pathname === "/service-single" || pathname === "/about-us";
|
|
|
|
const [formData, setFormData] = useState({
|
|
firstName: '',
|
|
lastName: '',
|
|
email: '',
|
|
phone: '',
|
|
company: '',
|
|
jobTitle: '',
|
|
industry: '',
|
|
companySize: '',
|
|
budget: '',
|
|
timeline: '',
|
|
projectType: '',
|
|
message: '',
|
|
newsletter: false,
|
|
privacy: false
|
|
});
|
|
|
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
const [submitStatus, setSubmitStatus] = useState<{
|
|
type: 'success' | 'error' | null;
|
|
message: string;
|
|
}>({ type: null, message: '' });
|
|
|
|
// Math Captcha state
|
|
const [captcha, setCaptcha] = useState({ num1: 0, num2: 0, operator: '+', answer: 0 });
|
|
const [captchaAnswer, setCaptchaAnswer] = useState('');
|
|
const [captchaError, setCaptchaError] = useState('');
|
|
|
|
// Refs for scrolling to status messages
|
|
const statusRef = useRef<HTMLDivElement>(null);
|
|
const formRef = useRef<HTMLFormElement>(null);
|
|
|
|
// Generate math captcha
|
|
const generateCaptcha = () => {
|
|
const operators = ['+', '-'];
|
|
const operator = operators[Math.floor(Math.random() * operators.length)];
|
|
let num1 = Math.floor(Math.random() * 10) + 1; // 1-10
|
|
let num2 = Math.floor(Math.random() * 10) + 1; // 1-10
|
|
|
|
// Ensure subtraction doesn't result in negative numbers
|
|
if (operator === '-' && num1 < num2) {
|
|
[num1, num2] = [num2, num1];
|
|
}
|
|
|
|
const answer = operator === '+' ? num1 + num2 : num1 - num2;
|
|
|
|
setCaptcha({ num1, num2, operator, answer });
|
|
setCaptchaAnswer('');
|
|
setCaptchaError('');
|
|
};
|
|
|
|
// Generate captcha on component mount
|
|
useEffect(() => {
|
|
generateCaptcha();
|
|
}, []);
|
|
|
|
// Scroll to status message when it appears
|
|
useEffect(() => {
|
|
if (submitStatus.type && statusRef.current) {
|
|
setTimeout(() => {
|
|
statusRef.current?.scrollIntoView({
|
|
behavior: 'smooth',
|
|
block: 'center'
|
|
});
|
|
}, 100);
|
|
}
|
|
}, [submitStatus]);
|
|
|
|
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
|
|
const { name, value, type } = e.target;
|
|
if (type === 'checkbox') {
|
|
const checked = (e.target as HTMLInputElement).checked;
|
|
setFormData(prev => ({ ...prev, [name]: checked }));
|
|
} else {
|
|
setFormData(prev => ({ ...prev, [name]: value }));
|
|
}
|
|
};
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
setIsSubmitting(true);
|
|
setSubmitStatus({ type: null, message: '' });
|
|
setCaptchaError('');
|
|
|
|
// Validate captcha
|
|
const userAnswer = parseInt(captchaAnswer.trim());
|
|
if (isNaN(userAnswer) || userAnswer !== captcha.answer) {
|
|
setCaptchaError('Incorrect answer. Please try again.');
|
|
setIsSubmitting(false);
|
|
generateCaptcha(); // Generate new captcha on error
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Transform form data to match API requirements
|
|
const apiData: ContactFormData = {
|
|
first_name: formData.firstName,
|
|
last_name: formData.lastName,
|
|
email: formData.email,
|
|
phone: formData.phone || undefined,
|
|
company: formData.company,
|
|
job_title: formData.jobTitle,
|
|
industry: formData.industry || undefined,
|
|
company_size: formData.companySize || undefined,
|
|
project_type: formData.projectType || undefined,
|
|
timeline: formData.timeline || undefined,
|
|
budget: formData.budget || undefined,
|
|
message: formData.message,
|
|
newsletter_subscription: formData.newsletter,
|
|
privacy_consent: formData.privacy
|
|
};
|
|
|
|
// Submit to Django API
|
|
const response = await contactApiService.submitContactForm(apiData);
|
|
|
|
setSubmitStatus({
|
|
type: 'success',
|
|
message: response.message || 'Thank you! We\'ll contact you within 24 hours.'
|
|
});
|
|
|
|
// Reset form on success
|
|
setFormData({
|
|
firstName: '',
|
|
lastName: '',
|
|
email: '',
|
|
phone: '',
|
|
company: '',
|
|
jobTitle: '',
|
|
industry: '',
|
|
companySize: '',
|
|
budget: '',
|
|
timeline: '',
|
|
projectType: '',
|
|
message: '',
|
|
newsletter: false,
|
|
privacy: false
|
|
});
|
|
|
|
// Reset captcha
|
|
setCaptchaAnswer('');
|
|
generateCaptcha();
|
|
|
|
} catch (error) {
|
|
setSubmitStatus({
|
|
type: 'error',
|
|
message: error instanceof Error ? error.message : 'Failed to submit form. Please try again.'
|
|
});
|
|
} finally {
|
|
setIsSubmitting(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<section
|
|
className={
|
|
"tp-contact pb-60 fade-wrapper luxury-contact" +
|
|
(isServiceSingle ? " pt-60" : " fix-top")
|
|
}
|
|
>
|
|
<div className="container">
|
|
{/* Contact Information Cards */}
|
|
<div className="row mb-30">
|
|
<div className="col-12 col-md-4">
|
|
<div className="contact-info-card">
|
|
<div className="contact-info-icon">
|
|
<i className="fa-solid fa-phone"></i>
|
|
</div>
|
|
<h4>Phone Support</h4>
|
|
<p>Main Contact & Emergency</p>
|
|
<a href="tel:+359896138030">+359 896 13 80 30</a>
|
|
<a href="tel:+359897338147">Emergency: +359 897 338 147</a>
|
|
<span className="contact-hours">Available 24/7</span>
|
|
</div>
|
|
</div>
|
|
<div className="col-12 col-md-4">
|
|
<div className="contact-info-card">
|
|
<div className="contact-info-icon">
|
|
<i className="fa-solid fa-envelope"></i>
|
|
</div>
|
|
<h4>Email Support</h4>
|
|
<p>Software Solutions</p>
|
|
<a href="mailto:info@gnxsoft.com">info@gnxsoft.com</a>
|
|
<a href="mailto:contact@gnxsoft.com">contact@gnxsoft.com</a>
|
|
<span className="contact-hours">24/7 Response Time</span>
|
|
</div>
|
|
</div>
|
|
<div className="col-12 col-md-4">
|
|
<div className="contact-info-card">
|
|
<div className="contact-info-icon">
|
|
<i className="fa-solid fa-map-marker-alt"></i>
|
|
</div>
|
|
<h4>Office Locations</h4>
|
|
<p>Our Location</p>
|
|
<address>
|
|
GNX Soft Ltd.<br />
|
|
Tsar Simeon I, 56<br />
|
|
Burgas, Burgas 8000, Bulgaria
|
|
</address>
|
|
<span className="contact-hours">Schedule a Visit</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="row vertical-column-gap-md justify-content-between mt-30">
|
|
<div className="col-12 col-lg-7">
|
|
<div className="tp-contact__content">
|
|
<div className="form-header-compact mb-20">
|
|
<h2 className="luxury-title">Request Enterprise Consultation</h2>
|
|
<p className="luxury-subtitle">Fill out the form below to get started</p>
|
|
</div>
|
|
<div className="contact-form">
|
|
<form ref={formRef} onSubmit={handleSubmit} className="enterprise-form luxury-form">
|
|
<div className="form-section compact-section">
|
|
<div className="row">
|
|
<div className="col-12 col-md-6">
|
|
<div className="input-single compact-input">
|
|
<label htmlFor="firstName">First Name *</label>
|
|
<input
|
|
type="text"
|
|
name="firstName"
|
|
id="firstName"
|
|
value={formData.firstName}
|
|
onChange={handleInputChange}
|
|
placeholder="John"
|
|
required
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div className="col-12 col-md-6">
|
|
<div className="input-single compact-input">
|
|
<label htmlFor="lastName">Last Name *</label>
|
|
<input
|
|
type="text"
|
|
name="lastName"
|
|
id="lastName"
|
|
value={formData.lastName}
|
|
onChange={handleInputChange}
|
|
placeholder="Smith"
|
|
required
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="row">
|
|
<div className="col-12 col-md-6">
|
|
<div className="input-single compact-input">
|
|
<label htmlFor="email">Business Email *</label>
|
|
<input
|
|
type="email"
|
|
name="email"
|
|
id="email"
|
|
value={formData.email}
|
|
onChange={handleInputChange}
|
|
placeholder="john.smith@company.com"
|
|
required
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div className="col-12 col-md-6">
|
|
<div className="input-single compact-input">
|
|
<label htmlFor="phone">Phone Number</label>
|
|
<input
|
|
type="tel"
|
|
name="phone"
|
|
id="phone"
|
|
value={formData.phone}
|
|
onChange={handleInputChange}
|
|
placeholder="+1 (555) 123-4567"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="row">
|
|
<div className="col-12 col-md-6">
|
|
<div className="input-single compact-input">
|
|
<label htmlFor="company">Company Name *</label>
|
|
<input
|
|
type="text"
|
|
name="company"
|
|
id="company"
|
|
value={formData.company}
|
|
onChange={handleInputChange}
|
|
placeholder="Acme Corporation"
|
|
required
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div className="col-12 col-md-6">
|
|
<div className="input-single compact-input">
|
|
<label htmlFor="jobTitle">Job Title *</label>
|
|
<input
|
|
type="text"
|
|
name="jobTitle"
|
|
id="jobTitle"
|
|
value={formData.jobTitle}
|
|
onChange={handleInputChange}
|
|
placeholder="CTO, IT Director, etc."
|
|
required
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="row">
|
|
<div className="col-12 col-md-6">
|
|
<div className="input-single compact-input">
|
|
<label htmlFor="industry">Industry</label>
|
|
<select
|
|
name="industry"
|
|
id="industry"
|
|
value={formData.industry}
|
|
onChange={handleInputChange}
|
|
>
|
|
<option value="">Select Industry</option>
|
|
<option value="technology">Technology</option>
|
|
<option value="finance">Finance</option>
|
|
<option value="healthcare">Healthcare</option>
|
|
<option value="manufacturing">Manufacturing</option>
|
|
<option value="retail">Retail</option>
|
|
<option value="education">Education</option>
|
|
<option value="government">Government</option>
|
|
<option value="other">Other</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div className="col-12 col-md-6">
|
|
<div className="input-single compact-input">
|
|
<label htmlFor="companySize">Company Size</label>
|
|
<select
|
|
name="companySize"
|
|
id="companySize"
|
|
value={formData.companySize}
|
|
onChange={handleInputChange}
|
|
>
|
|
<option value="">Select Company Size</option>
|
|
<option value="1-10">1-10 employees</option>
|
|
<option value="11-50">11-50 employees</option>
|
|
<option value="51-200">51-200 employees</option>
|
|
<option value="201-1000">201-1000 employees</option>
|
|
<option value="1000+">1000+ employees</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="row">
|
|
<div className="col-12 col-md-4">
|
|
<div className="input-single compact-input">
|
|
<label htmlFor="projectType">Project Type</label>
|
|
<select
|
|
name="projectType"
|
|
id="projectType"
|
|
value={formData.projectType}
|
|
onChange={handleInputChange}
|
|
>
|
|
<option value="">Select Type</option>
|
|
<option value="software-development">Software Development</option>
|
|
<option value="cloud-migration">Cloud Migration</option>
|
|
<option value="digital-transformation">Digital Transformation</option>
|
|
<option value="data-analytics">Data Analytics</option>
|
|
<option value="security-compliance">Security & Compliance</option>
|
|
<option value="integration">System Integration</option>
|
|
<option value="consulting">Consulting Services</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div className="col-12 col-md-4">
|
|
<div className="input-single compact-input">
|
|
<label htmlFor="timeline">Timeline</label>
|
|
<select
|
|
name="timeline"
|
|
id="timeline"
|
|
value={formData.timeline}
|
|
onChange={handleInputChange}
|
|
>
|
|
<option value="">Select Timeline</option>
|
|
<option value="immediate">Immediate (0-3 months)</option>
|
|
<option value="short">Short-term (3-6 months)</option>
|
|
<option value="medium">Medium-term (6-12 months)</option>
|
|
<option value="long">Long-term (12+ months)</option>
|
|
<option value="planning">Still planning</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div className="col-12 col-md-4">
|
|
<div className="input-single compact-input">
|
|
<label htmlFor="budget">Budget Range</label>
|
|
<select
|
|
name="budget"
|
|
id="budget"
|
|
value={formData.budget}
|
|
onChange={handleInputChange}
|
|
>
|
|
<option value="">Select Budget</option>
|
|
<option value="under-50k">Under €50,000</option>
|
|
<option value="50k-100k">€50,000 - €100,000</option>
|
|
<option value="100k-250k">€100,000 - €250,000</option>
|
|
<option value="250k-500k">€250,000 - €500,000</option>
|
|
<option value="500k-1m">€500,000 - €1,000,000</option>
|
|
<option value="over-1m">Over €1,000,000</option>
|
|
<option value="discuss">Prefer to discuss</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="input-single compact-input">
|
|
<label htmlFor="message">Project Description *</label>
|
|
<textarea
|
|
name="message"
|
|
id="message"
|
|
value={formData.message}
|
|
onChange={handleInputChange}
|
|
placeholder="Please describe your project requirements, current challenges, and expected outcomes..."
|
|
rows={4}
|
|
required
|
|
/>
|
|
</div>
|
|
<div className="checkbox-group compact-checkbox">
|
|
<div className="checkbox-single">
|
|
<input
|
|
type="checkbox"
|
|
name="newsletter"
|
|
id="newsletter"
|
|
checked={formData.newsletter}
|
|
onChange={handleInputChange}
|
|
/>
|
|
<label htmlFor="newsletter">
|
|
Subscribe to newsletter
|
|
</label>
|
|
</div>
|
|
<div className="checkbox-single">
|
|
<input
|
|
type="checkbox"
|
|
name="privacy"
|
|
id="privacy"
|
|
checked={formData.privacy}
|
|
onChange={handleInputChange}
|
|
required
|
|
/>
|
|
<label htmlFor="privacy">
|
|
I agree to the <a href="/policy?type=privacy">Privacy Policy</a> *
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Math Captcha */}
|
|
<div className="input-single compact-input captcha-container">
|
|
<label htmlFor="captcha">
|
|
Security Verification *
|
|
<span className="captcha-hint">(Please solve the math problem)</span>
|
|
</label>
|
|
<div className="captcha-wrapper">
|
|
<div className="captcha-question">
|
|
<span className="captcha-numbers">
|
|
{captcha.num1} {captcha.operator} {captcha.num2} = ?
|
|
</span>
|
|
<button
|
|
type="button"
|
|
className="captcha-refresh"
|
|
onClick={generateCaptcha}
|
|
title="Generate new question"
|
|
aria-label="Refresh captcha"
|
|
>
|
|
<i className="fa-solid fa-rotate"></i>
|
|
</button>
|
|
</div>
|
|
<input
|
|
type="number"
|
|
name="captcha"
|
|
id="captcha"
|
|
value={captchaAnswer}
|
|
onChange={(e) => {
|
|
setCaptchaAnswer(e.target.value);
|
|
setCaptchaError('');
|
|
}}
|
|
placeholder="Enter answer"
|
|
required
|
|
className={captchaError ? 'error' : ''}
|
|
/>
|
|
{captchaError && (
|
|
<span className="captcha-error">
|
|
<i className="fa-solid fa-exclamation-circle"></i>
|
|
{captchaError}
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Status Message */}
|
|
{submitStatus.type && (
|
|
<div
|
|
ref={statusRef}
|
|
className={`form-status mt-20 ${submitStatus.type === 'success' ? 'success' : 'error'}`}
|
|
>
|
|
<div className="status-content">
|
|
<i className={`fa-solid ${submitStatus.type === 'success' ? 'fa-check-circle' : 'fa-exclamation-circle'}`}></i>
|
|
<span>{submitStatus.message}</span>
|
|
{submitStatus.type === 'error' && (
|
|
<div className="error-details mt-10">
|
|
<small>
|
|
If the problem persists, please contact us directly at{' '}
|
|
<a href="mailto:info@gnxsoft.com" className="text-primary">info@gnxsoft.com</a>
|
|
</small>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
<div className="form-actions mt-30">
|
|
<button
|
|
type="submit"
|
|
className="btn btn-primary enterprise-btn"
|
|
disabled={isSubmitting}
|
|
>
|
|
{isSubmitting ? (
|
|
<>
|
|
<span>Sending...</span>
|
|
<i className="fa-solid fa-spinner fa-spin"></i>
|
|
</>
|
|
) : (
|
|
<>
|
|
<span>Request Software Consultation</span>
|
|
<i className="fa-solid fa-arrow-right"></i>
|
|
</>
|
|
)}
|
|
</button>
|
|
<p className="form-disclaimer">
|
|
<i className="fa-solid fa-lock"></i>
|
|
Your information is secure and will only be used to provide you with relevant software solutions.
|
|
</p>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="col-12 col-lg-5">
|
|
<div className="tp-contact__sidebar luxury-sidebar">
|
|
<div className="contact-sidebar-card compact-sidebar-card">
|
|
<div className="sidebar-header">
|
|
<h3>Enterprise Solutions</h3>
|
|
<div className="enterprise-badge">
|
|
<i className="fa-solid fa-award"></i>
|
|
<span>Premium</span>
|
|
</div>
|
|
</div>
|
|
<div className="sidebar-features compact-features">
|
|
<div className="feature-item compact-feature">
|
|
<div className="feature-icon">
|
|
<i className="fa-solid fa-shield-halved"></i>
|
|
</div>
|
|
<div className="feature-content">
|
|
<h4>Incident Management</h4>
|
|
<p>Real-time monitoring and automated response capabilities.</p>
|
|
</div>
|
|
</div>
|
|
<div className="feature-item compact-feature">
|
|
<div className="feature-icon">
|
|
<i className="fa-solid fa-users"></i>
|
|
</div>
|
|
<div className="feature-content">
|
|
<h4>Custom Development</h4>
|
|
<p>Tailored solutions with dedicated development teams.</p>
|
|
</div>
|
|
</div>
|
|
<div className="feature-item compact-feature">
|
|
<div className="feature-icon">
|
|
<i className="fa-solid fa-chart-line"></i>
|
|
</div>
|
|
<div className="feature-content">
|
|
<h4>System Integrations</h4>
|
|
<p>Seamless API integration for unified workflows.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="contact-sidebar-card compact-sidebar-card">
|
|
<h3>Quick Process</h3>
|
|
<div className="next-steps compact-steps">
|
|
<div className="step-item compact-step">
|
|
<div className="step-number">1</div>
|
|
<div className="step-content">
|
|
<h4>Consultation</h4>
|
|
<p>24-hour response time</p>
|
|
</div>
|
|
</div>
|
|
<div className="step-item compact-step">
|
|
<div className="step-number">2</div>
|
|
<div className="step-content">
|
|
<h4>Custom Demo</h4>
|
|
<p>Personalized demonstration</p>
|
|
</div>
|
|
</div>
|
|
<div className="step-item compact-step">
|
|
<div className="step-number">3</div>
|
|
<div className="step-content">
|
|
<h4>Proposal</h4>
|
|
<p>Detailed pricing & timeline</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="contact-sidebar-card compact-sidebar-card">
|
|
<h3>Location</h3>
|
|
<div className="company-map compact-map">
|
|
<div className="map-container">
|
|
<iframe
|
|
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d2933.123456789!2d27.4758968970689!3d42.496781103070504!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x40a6b8c9d1234567%3A0x1234567890abcdef!2sBurgas%2C%20Bulgaria!5e0!3m2!1sen!2sbg!4v1234567890123!5m2!1sen!2sbg"
|
|
width="100%"
|
|
height="150"
|
|
style={{ border: 0, borderRadius: '8px' }}
|
|
allowFullScreen
|
|
loading="lazy"
|
|
referrerPolicy="no-referrer-when-downgrade"
|
|
title="Company Location"
|
|
/>
|
|
</div>
|
|
<div className="map-info compact-map-info">
|
|
<div className="location-details">
|
|
<div className="location-icon">
|
|
<i className="fa-solid fa-map-marker-alt"></i>
|
|
</div>
|
|
<div className="location-text">
|
|
<h4>GNX Soft Ltd.</h4>
|
|
<p>Tsar Simeon I, 56, Burgas 8000</p>
|
|
</div>
|
|
</div>
|
|
<a href="https://maps.google.com/?q=Tsar+Simeon+I+56+Burgas+Bulgaria" target="_blank" rel="noopener noreferrer" className="map-link">
|
|
<i className="fa-solid fa-directions"></i>
|
|
Directions
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export default ContactSection;
|