From 7061fcff3e8d06d26c7c49309ee276379653c9ea Mon Sep 17 00:00:00 2001 From: selvi Date: Wed, 11 Mar 2026 10:22:41 +0530 Subject: [PATCH] career form updated --- src/app/careers/page.tsx | 4 +- src/components/careers/CareersForm.tsx | 338 +++++++++++++++++++++++++ 2 files changed, 341 insertions(+), 1 deletion(-) create mode 100644 src/components/careers/CareersForm.tsx diff --git a/src/app/careers/page.tsx b/src/app/careers/page.tsx index 4d4ff5c..57e28a4 100644 --- a/src/app/careers/page.tsx +++ b/src/app/careers/page.tsx @@ -8,6 +8,7 @@ import AboutService from "@/components/services-digital-solutions/AboutService"; import AboutThree from "@/components/home/AboutThree"; import ProjectsSection from "@/components/home/ProjectsSection"; import MetatronInitializer from "@/components/MetatronInitializer"; +import CareersForm from "@/components/careers/CareersForm"; export const metadata: Metadata = { title: "Careers - Metatroncube Software Solutions | Innovative & User-Centric Tech Services in Waterloo", @@ -19,7 +20,7 @@ export const metadata: Metadata = { export default function CareersPage() { return ( - <> + <>
@@ -32,6 +33,7 @@ export default function CareersPage() { +
diff --git a/src/components/careers/CareersForm.tsx b/src/components/careers/CareersForm.tsx new file mode 100644 index 0000000..01da3a7 --- /dev/null +++ b/src/components/careers/CareersForm.tsx @@ -0,0 +1,338 @@ +"use client"; + +import React, { useState, useEffect, useRef } from "react"; +import ReCAPTCHA from "react-google-recaptcha"; +import axios from "axios"; + +const CareersForm = () => { + const sectionRef = useRef(null); + const [formData, setFormData] = useState({ + name: "", + email: "", + phone: "", + position: "", + message: "", + }); + + const [resumeFile, setResumeFile] = useState(null); + const [resumeBase64, setResumeBase64] = useState(null); + + const [formErrors, setFormErrors] = useState({}); + const [captchaToken, setCaptchaToken] = useState(null); + const [alert, setAlert] = useState({ show: false, type: "", message: "" }); + const [isSubmitting, setIsSubmitting] = useState(false); + + const handleChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; + setFormData((prev) => ({ ...prev, [name]: value })); + }; + + const handleFileChange = (e: React.ChangeEvent) => { + if (e.target.files && e.target.files.length > 0) { + const file = e.target.files[0]; + + // Check file size (e.g., max 5MB) + if (file.size > 5 * 1024 * 1024) { + setFormErrors((prev: any) => ({ ...prev, resume: "File size should be less than 5MB." })); + setResumeFile(null); + setResumeBase64(null); + return; + } + + // Check file type + const allowedTypes = ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document']; + if (!allowedTypes.includes(file.type)) { + setFormErrors((prev: any) => ({ ...prev, resume: "Only PDF and Word documents are allowed." })); + setResumeFile(null); + setResumeBase64(null); + return; + } + + setResumeFile(file); + setFormErrors((prev: any) => ({ ...prev, resume: undefined })); + + // Convert to Base64 for email attachment + const reader = new FileReader(); + reader.readAsDataURL(file); + reader.onload = () => { + setResumeBase64(reader.result as string); + }; + reader.onerror = (error) => { + console.error("Error reading file:", error); + setFormErrors((prev: any) => ({ ...prev, resume: "Error reading file." })); + }; + } + }; + + const handleCaptchaChange = (token: string | null) => { + setCaptchaToken(token); + }; + + useEffect(() => { + const observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + const elements = entry.target.querySelectorAll(".wow"); + elements.forEach((el) => { + const htmlEl = el as HTMLElement; + const delay = htmlEl.dataset.wowDelay || "0ms"; + setTimeout(() => { + htmlEl.classList.add("animated"); + + if (htmlEl.classList.contains("fadeInLeft")) { + htmlEl.classList.add("fadeInLeft"); + } else if (htmlEl.classList.contains("fadeInRight")) { + htmlEl.classList.add("fadeInRight"); + } else { + htmlEl.classList.add("fadeInUp"); + } + + htmlEl.style.visibility = "visible"; + }, parseInt(delay)); + }); + observer.unobserve(entry.target); + } + }); + }, + { threshold: 0.1 } + ); + + if (sectionRef.current) { + observer.observe(sectionRef.current); + } + + return () => observer.disconnect(); + }, []); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + const errors: any = {}; + if (!formData.name.trim()) errors.name = "Name is required."; + if (!formData.email.trim()) errors.email = "Email is required."; + if (!formData.phone.trim()) errors.phone = "Phone is required."; + if (!formData.position.trim()) errors.position = "Please select a position."; + // if (!captchaToken) errors.captcha = "Please verify the CAPTCHA."; + if (!resumeFile) errors.resume = "Please upload your resume."; + + setFormErrors(errors); + if (Object.keys(errors).length > 0) return; + + setIsSubmitting(true); + + const emailData: any = { + ...formData, + message: `Position: ${formData.position}

Phone: ${formData.phone}

Message: ${formData.message || "No additional message."}`, + to: "info@metatroncubesolutions.com", + senderName: "Metatroncube Careers Application", + recaptchaToken: captchaToken, + }; + + // If the backend endpoint supports attachments via Nodemailer's "attachments" array matching to data URI paths + if (resumeBase64 && resumeFile) { + emailData.attachments = [ + { + filename: resumeFile.name, + path: resumeBase64 + } + ]; + } + + try { + const res = await axios.post("https://mailserver.metatronnest.com/send", emailData, { + headers: { "Content-Type": "application/json" }, + }); + + setAlert({ + show: true, + type: "success", + message: res?.data?.message || "Application submitted successfully!", + }); + + setFormData({ + name: "", + email: "", + phone: "", + position: "", + message: "", + }); + setResumeFile(null); + setResumeBase64(null); + // Reset file input value manually + const fileInput = document.getElementById("resume-upload") as HTMLInputElement; + if (fileInput) fileInput.value = ""; + + setCaptchaToken(null); + setFormErrors({}); + } catch (error) { + console.error("❌ Error sending email:", error); + setAlert({ + show: true, + type: "danger", + message: "Failed to submit application. Please try again later.", + }); + } finally { + setIsSubmitting(false); + } + }; + + useEffect(() => { + if (alert.show) { + const timer = setTimeout(() => { + setAlert((prev) => ({ ...prev, show: false })); + }, 5000); + return () => clearTimeout(timer); + } + }, [alert.show]); + + return ( +
+ +
+
+
+
+
+ Career Growth +
+
+
+
+
+
+ + Join Our Team + +
+

+ Apply for your dream position today. +

+
+ + {alert.show && ( +
+ {alert.message} +
+ )} + +
+
+
+ + + {formErrors.name && {formErrors.name}} +
+
+ + + {formErrors.email && {formErrors.email}} +
+
+ + + {formErrors.phone && {formErrors.phone}} +
+
+ + + {formErrors.position && {formErrors.position}} +
+ +
+ + + {formErrors.resume && {formErrors.resume}} + {resumeFile && !formErrors.resume && File selected: {resumeFile.name}} +
+ +
+ + +
+ + {/*
+ + {formErrors.captcha && {formErrors.captcha}} +
*/} + +
+ +
+
+
+
+
+
+
+
+
+ ); +}; + +export default CareersForm;