diff --git a/package.json b/package.json index 87671d1..6fe1d63 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,9 @@ "preview": "vite preview" }, "dependencies": { + "@react-pdf/renderer": "^4.3.0", + "@tailwindcss/vite": "^4.1.11", "framer": "^2.4.1", "lottie-react": "^2.4.1", diff --git a/src/components/ContactPage.jsx b/src/components/ContactPage.jsx index 584fdda..b2a046b 100644 --- a/src/components/ContactPage.jsx +++ b/src/components/ContactPage.jsx @@ -1,105 +1,397 @@ import React, { useState, useEffect } from 'react'; -import { Link } from 'react-router-dom'; // Placeholder, navigation is handled by props -import { Send, User, Mail } from 'lucide-react'; -import Footer from './Footer'; - -const ContactPage = ({ darkMode, navigateTo }) => { - // State to control the animations - const [isVisible, setIsVisible] = useState(false); - - // useEffect to trigger the animation on component mount - useEffect(() => { - const timer = setTimeout(() => setIsVisible(true), 100); - return () => clearTimeout(timer); - }, []); - - return ( - // Main container with dynamic background -
-
-
- - {/* Animated content card */} -
- +// Import specific icons from lucide-react. This is the correct way to import them. +import { Send, User, Mail, MessageSquare, BookOpen, CheckCircle, XCircle, MapPin, Phone, Linkedin, Twitter, Github, Sun, Moon } from 'lucide-react'; + +// Footer Component: Displays copyright information. +const Footer = () => { + return ( +
+

© 2024 Your Company. All rights reserved.

+
+ ); +}; + +// App Component: Main entry point, manages dark mode and renders ContactPage. +export default function App() { + // State to manage the dark mode toggle + const [darkMode, setDarkMode] = useState(false); + + // Placeholder function for navigation. Can be expanded with React Router if needed. + const navigateTo = (path) => { + console.log(`Navigating to ${path}`); + }; + + return ( + // Render the ContactPage, passing dark mode state and setter, and navigate function + + ); +} + +// ContactPage Component: Handles the contact form, UI, and email sending logic. +const ContactPage = ({ darkMode, navigateTo, setDarkMode }) => { + // State for animating the main content card visibility + const [isVisible, setIsVisible] = useState(false); + // State to store form input data + const [formData, setFormData] = useState({ + name: '', + email: '', + subject: '', + message: '', + }); + // State to store form validation errors + const [errors, setErrors] = useState({}); + // State to track if the form is currently being submitted + const [isSubmitting, setIsSubmitting] = useState(false); + // State to store the result of the form submission (success or error) + const [submissionStatus, setSubmissionStatus] = useState(null); + // State to control the visibility of the toast notification + const [showToast, setShowToast] = useState(false); + + // Effect to trigger the initial fade-in animation for the content card + useEffect(() => { + const timer = setTimeout(() => setIsVisible(true), 100); + return () => clearTimeout(timer); // Cleanup timer on unmount + }, []); + + // Effect to dynamically load the emailjs library as a global script. + // This resolves "Dynamic require" errors often seen in certain environments. + useEffect(() => { + const script = document.createElement('script'); + script.src = 'https://cdn.jsdelivr.net/npm/@emailjs/browser@3/dist/email.min.js'; + script.async = true; // Load script asynchronously to not block parsing + + script.onload = () => { + console.log('emailjs script loaded successfully.'); + // EmailJS library initializes itself when loaded, no explicit init needed here + // if you're passing the public key directly in the send method. + }; + + script.onerror = (error) => { + console.error('Failed to load emailjs script:', error); + }; + + document.body.appendChild(script); // Append the script to the document body + + // Cleanup function: remove the script from the DOM when the component unmounts + return () => { + // Check if the script exists before trying to remove it + if (document.body.contains(script)) { + document.body.removeChild(script); + } + }; + }, []); // Empty dependency array ensures this effect runs only once on mount + + // Effect to manage the auto-hiding of the toast notification + useEffect(() => { + if (showToast) { + const toastTimer = setTimeout(() => { + setShowToast(false); + setSubmissionStatus(null); // Reset submission status after toast hides + }, 5000); // Toast will be visible for 5 seconds + return () => clearTimeout(toastTimer); // Cleanup timer on unmount or when toast hides + } + }, [showToast]); // Re-run this effect whenever showToast changes + + // Handles changes in form input fields (for controlled components) + const handleChange = (e) => { + const { name, value } = e.target; + setFormData((prevData) => ({ + ...prevData, + [name]: value, // Update the specific field + })); + + // Clear any previous validation error for the current field as the user types + setErrors((prevErrors) => ({ + ...prevErrors, + [name]: null, + })); + }; + + // Validates the form fields before submission + const validateForm = () => { + let newErrors = {}; + // Check if name is empty + if (!formData.name.trim()) { + newErrors.name = 'Name is required'; + } + // Check if email is empty or invalid format + if (!formData.email.trim()) { + newErrors.email = 'Email is required'; + } else if (!/\S+@\S+\.\S+/.test(formData.email)) { + newErrors.email = 'Email address is invalid'; + } + // Check if message is empty + if (!formData.message.trim()) { + newErrors.message = 'Message is required'; + } + + setErrors(newErrors); // Update the errors state + // Return true if there are no errors, false otherwise + return Object.keys(newErrors).length === 0; + }; + + // Handles the form submission event + const handleSubmit = (e) => { + e.preventDefault(); // Prevent default browser form submission behavior (page reload) + setSubmissionStatus(null); // Clear previous submission status + setShowToast(false); // Hide any currently visible toast + + // Run form validation + if (!validateForm()) { + return; // If validation fails, stop the submission process + } + + setIsSubmitting(true); // Set submitting state to true to show loading indicator + + // EmailJS service credentials (replace with your actual credentials if different) + const serviceID = 'service_sc9xokh'; + const templateID = 'template_22c3a2a'; + const publicKey = 'bcz_108RDETTP_XP4'; // Your EmailJS Public Key + + // Check if window.emailjs is available (meaning the script has loaded) + if (window.emailjs) { + // Send the email using EmailJS + window.emailjs.send(serviceID, templateID, formData, publicKey) + .then((response) => { + console.log('Email sent successfully!', response.status, response.text); + setSubmissionStatus('success'); // Set success status + setFormData({ name: '', email: '', subject: '', message: '' }); // Clear the form fields + }) + .catch((err) => { + console.error('Failed to send email:', err); + setSubmissionStatus('error'); // Set error status + }) + .finally(() => { + setIsSubmitting(false); // Reset submitting state regardless of success/failure + setShowToast(true); // Show the toast notification + }); + } else { + // Handle case where EmailJS library hasn't loaded (e.g., network issue) + console.error('EmailJS library is not loaded. Cannot send email.'); + setSubmissionStatus('error'); + setIsSubmitting(false); + setShowToast(true); + } + }; + + return ( + // Main container div with dynamic background color and font based on dark mode +
+ {/* Background gradient overlay, transitions with isVisible for a soft entrance */} +
+
+
- {/* Animated Description */} -

- Have a question or feedback? We'd love to hear from you. -

- - {/* Clickable Mailto Link, styled as a button */} -
- {/* Stylish Heading */} -

- Get In Touch -

- - {/* Subheading */} -

- Whether it's a question, suggestion, or feedback—drop us a line! -

- - {/* Button with icon before text */} - -
- - - -
- - {/* Developer Details */} -
-

- This project is maintained by: -

-
- - Tanmay Kalra -
+ {/* Dark Mode Toggle button: Positioned at the top right */} +
+
-
+ {/* Main Content Area: Centered horizontally and vertically */} +
+
+ {/* Main Content Card: Animated with shadow, border, and blur effect */} +
+ {/* Left Section: Contact Information */} +
{/* Dynamic background for left panel */} +
+

Contact Us

+

Feel free to reach out to us through any of these channels.

+ +
+ {/* Location Info */} +
+ +

123 Main Street, Anytown, USA

+
+ {/* Email Info */} +
+ +

info@smartlog.com

+
+ {/* Phone Info */} +
+ +

+1 (555) 123-4567

+
+
+
+ + {/* Social Media Icons */} +
+ + + +
+
+ + {/* Right Section: Get In Touch Form */} +
{/* Dynamic background for right panel */} +

+ Get In Touch +

+ +

+ Have a question or feedback? We'd love to hear from you. +

+ +
+ {/* Form fields with animated entry */} + {/* Name Input */} +
+
+ +
+ + {errors.name &&

{errors.name}

} +
+ + {/* Email Input */} +
+
+ +
+ + {errors.email &&

{errors.email}

} +
+ + {/* Subject Input */} +
+
+ +
+ +
+ + {/* Message Textarea */} +
+
+ +
+ + {errors.message &&

{errors.message}

} +
+ + {/* Submit Button with loading spinner */} +
+ +
+
+ + {/* Project Maintainer Info */} +
+

+ This project is maintained by: +

+
+ + Tanmay Kalra +
+
+
+
+
+
+ + {/* Confirmation Toast Notification: Appears at the bottom right */} + {showToast && ( +
+ {submissionStatus === 'success' ? : } + {submissionStatus === 'success' ? 'Message sent successfully!' : 'Failed to send message.'} +
+ )} + + {/* Render the Footer component */} +
-
- -
- ); + ); }; - -export default ContactPage; \ No newline at end of file