diff --git a/.gitignore b/.gitignore index 292a4329..627f7813 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,11 @@ .docusaurus .cache-loader +# Docusaurus build artifacts (transpiled JS from TS/TSX) +docusaurus.config.js +sidebars.js +src/**/*.js + # Misc .DS_Store .env.local diff --git a/package.json b/package.json index b42bb8b0..7fba5f5a 100644 --- a/package.json +++ b/package.json @@ -15,12 +15,12 @@ "typecheck": "tsc" }, "dependencies": { - "@docusaurus/core": "^3.8.1", + "@docusaurus/core": "3.8.1", "@docusaurus/plugin-content-docs": "3.8.1", - "@docusaurus/plugin-google-analytics": "^3.8.1", + "@docusaurus/plugin-google-analytics": "3.8.1", "@docusaurus/plugin-ideal-image": "3.8.1", - "@docusaurus/preset-classic": "^3.8.1", - "@docusaurus/theme-classic": "^3.8.1", + "@docusaurus/preset-classic": "3.8.1", + "@docusaurus/theme-classic": "3.8.1", "@docusaurus/theme-mermaid": "3.8.1", "@docusaurus/theme-search-algolia": "3.8.1", "@floating-ui/react": "^0.27.8", @@ -57,9 +57,9 @@ "vanilla-tilt": "^1.8.1" }, "devDependencies": { - "@docusaurus/module-type-aliases": "^3.8.1", - "@docusaurus/tsconfig": "^3.8.1", - "@docusaurus/types": "^3.8.1", + "@docusaurus/module-type-aliases": "3.8.1", + "@docusaurus/tsconfig": "3.8.1", + "@docusaurus/types": "3.8.1", "@tailwindcss/postcss": "^4.1.4", "@types/canvas-confetti": "^1.9.0", "@types/react": "^19.1.9", diff --git a/src/pages/contact-us/index.css b/src/pages/contact-us/index.css index dba5797c..3552551a 100644 --- a/src/pages/contact-us/index.css +++ b/src/pages/contact-us/index.css @@ -503,4 +503,86 @@ html[data-theme='light'] { .resources-title { font-size: 1.25rem; } -} \ No newline at end of file +} + +/* Form validation and status styles */ +.form-error { + display: block; + color: #ef4444; + font-size: 0.875rem; + margin-top: 0.25rem; + font-weight: 500; +} + +.form-input.error, +.form-select.error, +.form-textarea.error { + border-color: #ef4444; + background: rgba(239, 68, 68, 0.05); +} + +.form-input.error:focus, +.form-select.error:focus, +.form-textarea.error:focus { + border-color: #ef4444; + box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.2); +} + +.required { + color: #ef4444; + font-weight: 600; +} + +.form-status-message { + padding: 1rem; + border-radius: 0.75rem; + margin-bottom: 1.5rem; + font-weight: 500; + animation: slideIn 0.3s ease-out; +} + +.form-status-message.success { + background: rgba(16, 185, 129, 0.1); + border: 1px solid rgba(16, 185, 129, 0.3); + color: #10b981; +} + +[data-theme='light'] .form-status-message.success { + background: rgba(16, 185, 129, 0.15); + border: 1px solid rgba(16, 185, 129, 0.4); + color: #059669; +} + +.form-status-message.error { + background: rgba(239, 68, 68, 0.1); + border: 1px solid rgba(239, 68, 68, 0.3); + color: #ef4444; +} + +[data-theme='light'] .form-status-message.error { + background: rgba(239, 68, 68, 0.15); + border: 1px solid rgba(239, 68, 68, 0.4); + color: #dc2626; +} + +.submit-button:disabled { + opacity: 0.6; + cursor: not-allowed; + transform: none; +} + +.submit-button:disabled:hover { + box-shadow: 0 5px 15px rgba(102, 126, 234, 0.3); + transform: none; +} + +@keyframes slideIn { + from { + opacity: 0; + transform: translateY(-10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} diff --git a/src/pages/contact-us/index.tsx b/src/pages/contact-us/index.tsx index ac07b87a..06f1aa76 100644 --- a/src/pages/contact-us/index.tsx +++ b/src/pages/contact-us/index.tsx @@ -1,9 +1,131 @@ -import React from "react"; +import React, { useState } from "react"; import Layout from "@theme/Layout"; import { Mail, MapPin, Clock } from "lucide-react"; import "./index.css"; +interface FormData { + firstName: string; + lastName: string; + email: string; + subject: string; + message: string; +} + +interface FormErrors { + firstName?: string; + lastName?: string; + email?: string; + subject?: string; + message?: string; +} + const ContactUs: React.FC = () => { + const [formData, setFormData] = useState({ + firstName: "", + lastName: "", + email: "", + subject: "", + message: "", + }); + + const [errors, setErrors] = useState({}); + const [isSubmitting, setIsSubmitting] = useState(false); + const [submitStatus, setSubmitStatus] = useState<{ + type: "success" | "error" | null; + message: string; + }>({ type: null, message: "" }); + + // Handle input changes + const handleChange = ( + e: React.ChangeEvent + ) => { + const { name, value } = e.target; + setFormData((prev) => ({ + ...prev, + [name]: value, + })); + // Clear error for this field when user starts typing + if (errors[name as keyof FormErrors]) { + setErrors((prev) => ({ + ...prev, + [name]: undefined, + })); + } + }; + + // Validate form + const validateForm = (): boolean => { + const newErrors: FormErrors = {}; + + if (!formData.firstName.trim()) { + newErrors.firstName = "First name is required"; + } + + if (!formData.lastName.trim()) { + newErrors.lastName = "Last name is required"; + } + + if (!formData.email.trim()) { + newErrors.email = "Email is required"; + } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) { + newErrors.email = "Please enter a valid email address"; + } + + if (!formData.subject) { + newErrors.subject = "Please select a subject"; + } + + if (!formData.message.trim()) { + newErrors.message = "Message is required"; + } else if (formData.message.trim().length < 10) { + newErrors.message = "Message must be at least 10 characters long"; + } + + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }; + + // Handle form submission + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + // Validate form + if (!validateForm()) { + return; + } + + setIsSubmitting(true); + setSubmitStatus({ type: null, message: "" }); + + try { + // Simulate API call (replace with actual API endpoint) + await new Promise((resolve) => setTimeout(resolve, 1500)); + + // Success + setSubmitStatus({ + type: "success", + message: "Thank you for your message! We'll get back to you within 24-48 hours.", + }); + + // Reset form + setFormData({ + firstName: "", + lastName: "", + email: "", + subject: "", + message: "", + }); + } catch (error) { + // Error + setSubmitStatus({ + type: "error", + message: "Something went wrong. Please try again later.", + }); + } finally { + setIsSubmitting(false); + } + }; + return ( { Send us a message -
+ {submitStatus.type && ( +
+ {submitStatus.message} +
+ )} + +
+ {errors.firstName && ( + + {errors.firstName} + + )}
+ {errors.lastName && ( + + {errors.lastName} + + )}
+ {errors.email && ( + + {errors.email} + + )}
+ {errors.subject && ( + + {errors.subject} + + )}
+ {errors.message && ( + + {errors.message} + + )}
diff --git a/src/theme/MDXComponents.js b/src/theme/MDXComponents.js deleted file mode 100644 index 39b1b869..00000000 --- a/src/theme/MDXComponents.js +++ /dev/null @@ -1,17 +0,0 @@ -import BrowserWindow from "@site/src/components/BrowserWindow"; -import MDXComponents from "@theme-original/MDXComponents"; -import TabItem from "@theme/TabItem"; -import Tabs from "@theme/Tabs"; -import Comming from "../components/Comming"; -import Image from "@theme/IdealImage"; -import GiscusComments from "../components/giscus"; - -export default { - ...MDXComponents, - BrowserWindow, - Tabs, - TabItem, - Comming, - Image, - GiscusComments, -};