diff --git a/components/AboutYourProject.tsx b/components/AboutYourProject.tsx new file mode 100644 index 0000000..4b387b5 --- /dev/null +++ b/components/AboutYourProject.tsx @@ -0,0 +1,737 @@ +import React, { useState, useRef } from "react"; +import { motion } from "framer-motion"; +import { Button } from "../components/ui/button"; +import { ShimmerButton } from "../components/ui/shimmer-button"; +import { Card, CardContent } from "../components/ui/card"; +import { Input } from "../components/ui/input"; +import { Textarea } from "../components/ui/textarea"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "../components/ui/select"; +import { Checkbox } from "../components/ui/checkbox"; +import { RadioGroup, RadioGroupItem } from "../components/ui/radio-group"; +import { Label } from "../components/ui/label"; +import { MathVerificationPopup } from "../components/MathVerificationPopup"; +import { useStoreContactUsMutation } from "@/src/services/storeContactUs"; +import { useNavigate } from "react-router-dom"; +import * as Yup from "yup"; +import { + Rocket, + Upload, + FileText, + CheckCircle, + ArrowRight, + Shield, +} from "lucide-react"; + +interface AboutYourProjectProps { + // You can add any props you might need to pass from the parent component +} + +// Country codes constant +const COUNTRY_CODES: Record = { + us: "+1", + uk: "+44", + ca: "+1", + au: "+61", + in: "+91", + de: "+49", + fr: "+33", + other: "+", +}; + +export const AboutYourProject: React.FC = () => { + const [isHumanVerified, setIsHumanVerified] = useState(false); + const [showVerificationPopup, setShowVerificationPopup] = useState(false); + const [formData, setFormData] = useState({ + name: "", + email: "", + country: "", + phone: "", + services: "", + budget: "", + projectDescription: "", + developmentStage: "", + timeline: "", + ndaRequired: false, + agreeTerms: false, + }); + const [errors, setErrors] = useState>({}); + const [touched, setTouched] = useState>({}); + const [attachedFiles, setAttachedFiles] = useState([]); + const [submitContactForm, { isLoading }] = useStoreContactUsMutation(); + const navigate = useNavigate(); + + // Updated Validation Schema (removed recaptcha) + const validationSchema = Yup.object().shape({ + name: Yup.string() + .required("Name is required") + .min(2, "Name must be at least 2 characters") + .max(50, "Name must not exceed 50 characters"), + email: Yup.string() + .required("Email is required") + .email("Invalid email address"), + country: Yup.string().required("Country is required"), + phone: Yup.string() + .required("Phone number is required") + .matches(/^[\d\s+\-().]{10,}$/, "Please enter a valid phone number"), + services: Yup.string().required("Service selection is required"), + budget: Yup.string().required("Budget range is required"), + projectDescription: Yup.string() + .required("Project description is required") + .min(50, "Description should be at least 50 characters") + .max(2000, "Description must not exceed 2000 characters"), + developmentStage: Yup.string().required("Development stage is required"), + timeline: Yup.string().required("Timeline is required"), + ndaRequired: Yup.boolean(), + agreeTerms: Yup.boolean() + .oneOf([true], "You must agree to the terms and conditions") + .required("You must agree to the terms and conditions"), + }).test( + 'human-verification', + 'Human verification is required', + function(value) { + return isHumanVerified; + } + ); + + const handleBlur = (field: string) => { + setTouched({ ...touched, [field]: true }); + validateField(field); + }; + + const validateField = async (field: string, dataToValidate = formData) => { + try { + await validationSchema.validateAt(field, dataToValidate); + setErrors((prev) => ({ ...prev, [field]: "" })); + } catch (err) { + if (err instanceof Yup.ValidationError) { + setErrors((prev) => ({ ...prev, [field]: err.message })); + } + } + }; + + const validateForm = async () => { + try { + await validationSchema.validate(formData, { abortEarly: false }); + setErrors({}); + return true; + } catch (err) { + if (err instanceof Yup.ValidationError) { + const newErrors: Record = {}; + err.inner.forEach((error) => { + if (error.path) { + newErrors[error.path] = error.message; + } + }); + setErrors(newErrors); + + // Scroll to first error + const firstError = err.inner[0]; + if (firstError?.path) { + const element = document.getElementById(firstError.path); + if (element) { + element.scrollIntoView({ behavior: "smooth", block: "center" }); + } + } + } + return false; + } + }; + + const handleFileUpload = (event: React.ChangeEvent) => { + const files = event.target.files; + if (files) { + const validFiles = Array.from(files).filter( + (file) => file.size <= 10 * 1024 * 1024 + ); + setAttachedFiles((prev) => [...prev, ...validFiles]); + } + }; + + const removeFile = (index: number) => { + setAttachedFiles((prev) => prev.filter((_, i) => i !== index)); + }; + + const handleHumanVerification = () => { + setIsHumanVerified(true); + setShowVerificationPopup(false); + }; + + const handleVerificationCheckbox = (checked: boolean) => { + if (checked) { + setShowVerificationPopup(true); + } else { + setIsHumanVerified(false); + } + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + if (!isHumanVerified) { + setErrors(prev => ({ + ...prev, + humanVerification: "Please complete the human verification" + })); + setShowVerificationPopup(true); + return; + } + + const isValid = await validateForm(); + if (!isValid) return; + + try { + const formDataToSend = new FormData(); + formDataToSend.append("t_id", "xyz123"); + formDataToSend.append("name", formData.name); + formDataToSend.append("email", formData.email); + formDataToSend.append("country", formData.country); + formDataToSend.append("phone_number", formData.phone); + formDataToSend.append("service", formData.services); + formDataToSend.append("budget", formData.budget); + formDataToSend.append("message", formData.projectDescription); + formDataToSend.append("development_stage", formData.developmentStage); + formDataToSend.append("startTime", formData.timeline); + formDataToSend.append("nda_signing", formData.ndaRequired ? "1" : "0"); + formDataToSend.append("from_page", "contact-us"); + + if (attachedFiles.length > 0) { + attachedFiles.forEach((file) => { + formDataToSend.append("contact_us_attachment", file); + }); + } + + formDataToSend.append("ip", "192.168.1.10"); + formDataToSend.append("user_agent", navigator.userAgent); + + await submitContactForm(formDataToSend).unwrap(); + + // Reset form + setFormData({ + name: "", + email: "", + country: "", + phone: "", + services: "", + budget: "", + projectDescription: "", + developmentStage: "", + timeline: "", + ndaRequired: false, + agreeTerms: false, + }); + setAttachedFiles([]); + setIsHumanVerified(false); + + navigate("/thank-you"); + } catch (error) { + console.error("Form submission error:", error); + alert("Failed to submit the form. Please try again."); + } + }; + + // Helper components for form fields (keep the same as before) + const renderInputField = ( + field: keyof typeof formData, + label: string, + placeholder: string, + type = "text" + ) => ( +
+ + setFormData({ ...formData, [field]: e.target.value })} + onBlur={() => handleBlur(field)} + /> + {errors[field] && ( +

{errors[field]}

+ )} +
+ ); + + const renderSelectField = ( + field: keyof typeof formData, + label: string, + placeholder: string, + options: { value: string; label: string }[], + onValueChange?: (value: string) => void + ) => ( +
+ + + {errors[field] && ( +

{errors[field]}

+ )} +
+ ); + + const renderTextarea = ( + field: keyof typeof formData, + label: string, + placeholder: string, + rows = 6 + ) => ( +
+
+ + + {formData[field]?.toString().length || 0}/2000 + +
+