Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"docker:prod": "docker compose --profile prod up -d --build"
},
"dependencies": {
"@emailjs/browser": "^4.4.1",
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.15.6",
Expand Down
226 changes: 118 additions & 108 deletions src/pages/Contact/Contact.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,57 @@
import { useState, useContext } from "react";
import emailjs from "@emailjs/browser";
import {
CheckCircle,
Github,
Mail,
Phone,
Send,
X,
CheckCircle,
} from "lucide-react";
import { ThemeContext } from "../../context/ThemeContext";
import { useContext, useRef, useState } from "react";
import type { ThemeContextType } from "../../context/ThemeContext";
import { ThemeContext } from "../../context/ThemeContext";

function Contact() {
const [showPopup, setShowPopup] = useState(false);
const [isSubmitting, setIsSubmitting] = useState(false);
const themeContext = useContext(ThemeContext) as ThemeContextType;
const { mode } = themeContext;

const handleSubmit = async () => {
// Refs for form fields
const nameRef = useRef<HTMLInputElement>(null);
const emailRef = useRef<HTMLInputElement>(null);
const subjectRef = useRef<HTMLSelectElement>(null);
const messageRef = useRef<HTMLTextAreaElement>(null);

Comment on lines +20 to +25
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Inputs are uncontrolled; Issue #203 requests controlled components.

The linked issue explicitly calls for useState-managed, controlled inputs with validation. Consider switching refs to state for consistency and easier validation UX.

Example (sketch, not a full diff):

// state
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [subject, setSubject] = useState("");
const [message, setMessage] = useState("");

// inputs
<input value={name} onChange={(e) => setName(e.target.value)} ... />
<input type="email" value={email} onChange={(e) => setEmail(e.target.value)} ... />
<select value={subject} onChange={(e) => setSubject(e.target.value)} required>...</select>
<textarea value={message} onChange={(e) => setMessage(e.target.value)} ... />

// template params
const templateParams = { from_name: name.trim(), from_email: email.trim(), subject: subject.trim(), message: message.trim() };

// clear after success
setName(""); setEmail(""); setSubject(""); setMessage("");

If you want, I can generate a complete patch converting all four fields.

Also applies to: 234-242, 259-263, 278-285, 311-315, 36-41, 47-51

🤖 Prompt for AI Agents
In src/pages/Contact/Contact.tsx around lines 20-25 (also apply to 36-41, 47-51,
234-242, 259-263, 278-285, 311-315): replace the uncontrolled refs for name,
email, subject and message with useState-managed controlled inputs; create state
variables and setters for each field, bind each input/select/textarea value to
its state and add onChange handlers to update state, enforce validation
(required and email format) on inputs, when building templateParams use trimmed
state values, and after a successful submit reset all state fields to empty
strings; remove the refs and any direct DOM reads and ensure any existing logic
that cleared or referenced refs is updated to use the new state variables.


const handleSubmit = async (e?: React.MouseEvent<HTMLButtonElement>) => {
if (e) e.preventDefault();
setIsSubmitting(true);
Comment on lines +27 to 29
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Submit via form onSubmit to leverage native required-field validation.

Right now required attributes won’t prevent the button click. Hook the handler to the form’s onSubmit and make the button type="submit".

Apply this diff:

-  const handleSubmit = async (e?: React.MouseEvent<HTMLButtonElement>) => {
-    if (e) e.preventDefault();
+  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
+    e.preventDefault();
-            <div className="space-y-3 sm:space-y-4 flex-1 flex flex-col">
+            <form onSubmit={handleSubmit} className="space-y-3 sm:space-y-4 flex-1 flex flex-col" noValidate>
-                  <button
-                    onClick={handleSubmit}
-                    disabled={isSubmitting}
+                  <button
+                    type="submit"
+                    disabled={isSubmitting}

Also applies to: 221-221, 318-324


// Simulate API call
await new Promise((resolve) => setTimeout(resolve, 1500));
// EmailJS config (replace with your own)
const serviceId = import.meta.env.VITE_EMAILJS_SERVICE_ID;
const templateId = import.meta.env.VITE_EMAILJS_TEMPLATE_ID
const publicKey = import.meta.env.VITE_EMAILJS_USER_ID;

Comment on lines +31 to 35
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Fix env var name for EmailJS public key and guard missing config.

Use VITE_EMAILJS_PUBLIC_KEY (per your PR docs) instead of VITE_EMAILJS_USER_ID. Also bail out early if any config is missing to avoid silent failures.

Apply this diff:

-    const serviceId = import.meta.env.VITE_EMAILJS_SERVICE_ID;
-    const templateId = import.meta.env.VITE_EMAILJS_TEMPLATE_ID
-    const publicKey = import.meta.env.VITE_EMAILJS_USER_ID;
+    const serviceId = import.meta.env.VITE_EMAILJS_SERVICE_ID;
+    const templateId = import.meta.env.VITE_EMAILJS_TEMPLATE_ID;
+    const publicKey = import.meta.env.VITE_EMAILJS_PUBLIC_KEY;
+
+    if (!serviceId || !templateId || !publicKey) {
+      // Surface a clear signal in dev and prevent the send call
+      console.error("Missing EmailJS configuration (serviceId/templateId/publicKey).");
+      setIsSubmitting(false);
+      return;
+    }

Run to find inconsistent usages across the repo (and docs):


🏁 Script executed:

#!/bin/bash
rg -nP 'VITE_EMAILJS_(SERVICE_ID|TEMPLATE_ID|PUBLIC_KEY|USER_ID)'

Length of output: 285


Fix EmailJS env var and guard missing config

Replace the publicKey assignment to use VITE_EMAILJS_PUBLIC_KEY and bail out early if any of the three vars is unset to prevent silent failures:

 src/pages/Contact/Contact.tsx
@@ -31,3 +31,9 @@
     const serviceId = import.meta.env.VITE_EMAILJS_SERVICE_ID;
     const templateId = import.meta.env.VITE_EMAILJS_TEMPLATE_ID;
-    const publicKey  = import.meta.env.VITE_EMAILJS_USER_ID;
+    const publicKey = import.meta.env.VITE_EMAILJS_PUBLIC_KEY;
+
+    if (!serviceId || !templateId || !publicKey) {
+      console.error("Missing EmailJS configuration (serviceId/templateId/publicKey).");
+      setIsSubmitting(false);
+      return;
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// EmailJS config (replace with your own)
const serviceId = import.meta.env.VITE_EMAILJS_SERVICE_ID;
const templateId = import.meta.env.VITE_EMAILJS_TEMPLATE_ID
const publicKey = import.meta.env.VITE_EMAILJS_USER_ID;
// EmailJS config (replace with your own)
const serviceId = import.meta.env.VITE_EMAILJS_SERVICE_ID;
const templateId = import.meta.env.VITE_EMAILJS_TEMPLATE_ID;
const publicKey = import.meta.env.VITE_EMAILJS_PUBLIC_KEY;
if (!serviceId || !templateId || !publicKey) {
console.error("Missing EmailJS configuration (serviceId/templateId/publicKey).");
setIsSubmitting(false);
return;
}
🤖 Prompt for AI Agents
In src/pages/Contact/Contact.tsx around lines 31 to 35, the publicKey is
assigned from the wrong env var and there is no guard for missing EmailJS
config; change the publicKey assignment to use
import.meta.env.VITE_EMAILJS_PUBLIC_KEY and add an early exit when any of
serviceId, templateId or publicKey is falsy (e.g., log an error and return from
the component or disable form submission) to prevent silent failures when env
vars are not set.

setIsSubmitting(false);
setShowPopup(true);
const templateParams = {
from_name: nameRef.current?.value || "",
from_email: emailRef.current?.value || "",
subject: subjectRef.current?.value || "",
message: messageRef.current?.value || "",
};

try {
await emailjs.send(serviceId, templateId, templateParams, publicKey);
setShowPopup(true);
// Optionally clear fields
if (nameRef.current) nameRef.current.value = "";
if (emailRef.current) emailRef.current.value = "";
if (subjectRef.current) subjectRef.current.value = "";
if (messageRef.current) messageRef.current.value = "";
} catch (error) {
// Optionally handle error (show error popup, etc.)
}
setIsSubmitting(false);
// Auto-close popup after 5 seconds
Comment on lines +43 to 55
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Show user-facing errors and always reset submitting state in finally.

Errors are swallowed; users won’t know a send failed. Also prefer finally for resetting the loading flag.

Apply this diff:

-    try {
-      await emailjs.send(serviceId, templateId, templateParams, publicKey);
-      setShowPopup(true);
-      // Optionally clear fields
-      if (nameRef.current) nameRef.current.value = "";
-      if (emailRef.current) emailRef.current.value = "";
-      if (subjectRef.current) subjectRef.current.value = "";
-      if (messageRef.current) messageRef.current.value = "";
-    } catch (error) {
-      // Optionally handle error (show error popup, etc.)
-    }
-    setIsSubmitting(false);
+    try {
+      await emailjs.send(serviceId, templateId, templateParams, publicKey);
+      setShowPopup(true);
+      // Optionally clear fields
+      if (nameRef.current) nameRef.current.value = "";
+      if (emailRef.current) emailRef.current.value = "";
+      if (subjectRef.current) subjectRef.current.value = "";
+      if (messageRef.current) messageRef.current.value = "";
+    } catch (error) {
+      // Provide a user-visible notification
+      // (see import addition below)
+      toast.error("Failed to send your message. Please try again.");
+      console.error("EmailJS send failed:", error);
+    } finally {
+      setIsSubmitting(false);
+    }

Add this import (top of file):

+import { toast } from "react-hot-toast";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try {
await emailjs.send(serviceId, templateId, templateParams, publicKey);
setShowPopup(true);
// Optionally clear fields
if (nameRef.current) nameRef.current.value = "";
if (emailRef.current) emailRef.current.value = "";
if (subjectRef.current) subjectRef.current.value = "";
if (messageRef.current) messageRef.current.value = "";
} catch (error) {
// Optionally handle error (show error popup, etc.)
}
setIsSubmitting(false);
// Auto-close popup after 5 seconds
// At the top of src/pages/Contact/Contact.tsx
import { toast } from "react-hot-toast";
// …
// In your submit handler, replace lines 43–55 with:
try {
await emailjs.send(serviceId, templateId, templateParams, publicKey);
setShowPopup(true);
// Optionally clear fields
if (nameRef.current) nameRef.current.value = "";
if (emailRef.current) emailRef.current.value = "";
if (subjectRef.current) subjectRef.current.value = "";
if (messageRef.current) messageRef.current.value = "";
} catch (error) {
// Provide a user-visible notification
toast.error("Failed to send your message. Please try again.");
console.error("EmailJS send failed:", error);
} finally {
setIsSubmitting(false);
}
// Auto-close popup after 5 seconds
🤖 Prompt for AI Agents
In src/pages/Contact/Contact.tsx around lines 43 to 55, the send-email error is
currently swallowed and setIsSubmitting(false) is called outside a finally
block; update the try/catch to show a user-facing error (for example call
toast.error or set an error state and avoid showing the success popup) inside
the catch, and move setIsSubmitting(false) into a finally block so it always
runs; also add the required import at the top of the file (e.g., import { toast
} from 'react-toastify') and ensure the success popup is only shown on
successful send.

setTimeout(() => {
setShowPopup(false);
Expand All @@ -37,11 +64,10 @@ function Contact() {

return (
<div
className={`min-h-screen w-screen relative overflow-y-auto ${
mode === "dark"
? "bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900"
: "bg-gradient-to-br from-indigo-100 via-purple-100 to-indigo-100"
}`}
className={`min-h-screen w-screen relative overflow-y-auto ${mode === "dark"
? "bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900"
: "bg-gradient-to-br from-indigo-100 via-purple-100 to-indigo-100"
}`}
>
{/* Animated background elements */}
<div className="absolute inset-0">
Expand All @@ -55,9 +81,8 @@ function Contact() {
<div className="text-center mb-8 flex-shrink-0">
<div className="flex flex-col sm:flex-row justify-center items-center gap-2 sm:gap-3 mb-4">
<div
className={`inline-flex items-center justify-center w-14 h-14 sm:w-20 sm:h-20 shadow-2xl transition-transform transform hover:scale-105 overflow-hidden rounded-2xl sm:rounded-3xl ${
mode === "dark" ? "bg-white" : "bg-purple-200"
}`}
className={`inline-flex items-center justify-center w-14 h-14 sm:w-20 sm:h-20 shadow-2xl transition-transform transform hover:scale-105 overflow-hidden rounded-2xl sm:rounded-3xl ${mode === "dark" ? "bg-white" : "bg-purple-200"
}`}
>
<img
src="/crl-icon.png"
Expand All @@ -73,9 +98,8 @@ function Contact() {
</h1>
</div>
<p
className={`text-sm sm:text-lg max-w-xl md:max-w-2xl mx-auto leading-relaxed ${
mode === "dark" ? "text-gray-300" : "text-gray-700"
}`}
className={`text-sm sm:text-lg max-w-xl md:max-w-2xl mx-auto leading-relaxed ${mode === "dark" ? "text-gray-300" : "text-gray-700"
}`}
>
Get in touch with us to discuss your project tracking needs or
report any issues
Expand All @@ -87,16 +111,14 @@ function Contact() {
<div className="space-y-4 sm:space-y-6 h-full flex flex-col">
<div className="text-center lg:text-left flex-shrink-0">
<h2
className={`text-lg sm:text-2xl font-bold mb-2 sm:mb-3 ${
mode === "dark" ? "text-white" : "text-gray-800"
}`}
className={`text-lg sm:text-2xl font-bold mb-2 sm:mb-3 ${mode === "dark" ? "text-white" : "text-gray-800"
}`}
>
Let's Connect
</h2>
<p
className={`text-xs sm:text-base ${
mode === "dark" ? "text-gray-400" : "text-gray-600"
}`}
className={`text-xs sm:text-base ${mode === "dark" ? "text-gray-400" : "text-gray-600"
}`}
>
We're here to help you track and manage your GitHub
repositories more effectively
Expand Down Expand Up @@ -133,49 +155,44 @@ function Contact() {
return (
<div
key={title}
className={`group p-3 sm:p-5 rounded-xl sm:rounded-2xl backdrop-blur-lg transition-all duration-300 hover:scale-105 ${
mode === "dark"
? "bg-white/10 border border-white/20 hover:bg-white/20"
: "bg-white border border-gray-300 hover:bg-gray-100"
}`}
className={`group p-3 sm:p-5 rounded-xl sm:rounded-2xl backdrop-blur-lg transition-all duration-300 hover:scale-105 ${mode === "dark"
? "bg-white/10 border border-white/20 hover:bg-white/20"
: "bg-white border border-gray-300 hover:bg-gray-100"
}`}
>
<div className="flex items-center gap-3 sm:gap-4">
<div
className={`p-2 sm:p-2.5 rounded-full transition-transform duration-300 group-hover:scale-110 bg-gradient-to-r ${iconBg}`}
>
<Icon
className={`w-4 h-4 sm:w-5 sm:h-5 ${
mode === "dark"
? "text-white"
: "text-gray-800"
}`}
className={`w-4 h-4 sm:w-5 sm:h-5 ${mode === "dark"
? "text-white"
: "text-gray-800"
}`}
/>
</div>
<div>
<h3
className={`text-sm sm:text-base font-semibold ${
mode === "dark"
? "text-white"
: "text-gray-800"
}`}
className={`text-sm sm:text-base font-semibold ${mode === "dark"
? "text-white"
: "text-gray-800"
}`}
>
{title}
</h3>
<p
className={`text-xs sm:text-sm ${
mode === "dark"
? "text-gray-300"
: "text-gray-600"
}`}
className={`text-xs sm:text-sm ${mode === "dark"
? "text-gray-300"
: "text-gray-600"
}`}
>
{detail}
</p>
<p
className={`text-xs ${
mode === "dark"
? "text-gray-400"
: "text-gray-500"
}`}
className={`text-xs ${mode === "dark"
? "text-gray-400"
: "text-gray-500"
}`}
>
{sub}
</p>
Expand All @@ -189,16 +206,14 @@ function Contact() {

{/* Contact Form */}
<div
className={`p-4 sm:p-6 rounded-xl sm:rounded-3xl shadow-2xl h-full flex flex-col backdrop-blur-lg ${
mode === "dark"
? "bg-white/10 border border-white/20"
: "bg-white border border-gray-300"
}`}
className={`p-4 sm:p-6 rounded-xl sm:rounded-3xl shadow-2xl h-full flex flex-col backdrop-blur-lg ${mode === "dark"
? "bg-white/10 border border-white/20"
: "bg-white border border-gray-300"
}`}
>
<h2
className={`text-base sm:text-xl font-bold mb-4 text-center flex-shrink-0 ${
mode === "dark" ? "text-white" : "text-gray-800"
}`}
className={`text-base sm:text-xl font-bold mb-4 text-center flex-shrink-0 ${mode === "dark" ? "text-white" : "text-gray-800"
}`}
>
Send us a Message
</h2>
Expand All @@ -208,68 +223,65 @@ function Contact() {
{/* Full Name */}
<div>
<label
className={`block text-xs font-medium mb-1 ${
mode === "dark"
? "text-gray-300"
: "text-gray-700"
}`}
className={`block text-xs font-medium mb-1 ${mode === "dark"
? "text-gray-300"
: "text-gray-700"
}`}
>
Full Name
</label>
<input
type="text"
placeholder="Enter your full name"
required
className={`w-full p-2 sm:p-3 rounded-lg sm:rounded-xl text-sm sm:text-base transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-purple-500 ${
mode === "dark"
? "bg-white/5 border border-white/20 text-white placeholder-gray-400"
: "bg-gray-50 border border-gray-300 text-gray-800 placeholder-gray-500"
}`}
ref={nameRef}
className={`w-full p-2 sm:p-3 rounded-lg sm:rounded-xl text-sm sm:text-base transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-purple-500 ${mode === "dark"
? "bg-white/5 border border-white/20 text-white placeholder-gray-400"
: "bg-gray-50 border border-gray-300 text-gray-800 placeholder-gray-500"
}`}
/>
</div>

{/* Email */}
<div>
<label
className={`block text-xs font-medium mb-1 ${
mode === "dark"
? "text-gray-300"
: "text-gray-700"
}`}
className={`block text-xs font-medium mb-1 ${mode === "dark"
? "text-gray-300"
: "text-gray-700"
}`}
>
Email Address
</label>
<input
type="email"
placeholder="your.email@example.com"
required
className={`w-full p-2 sm:p-3 rounded-lg sm:rounded-xl text-sm sm:text-base transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-purple-500 ${
mode === "dark"
? "bg-white/5 border border-white/20 text-white placeholder-gray-400"
: "bg-gray-50 border border-gray-300 text-gray-800 placeholder-gray-500"
}`}
ref={emailRef}
className={`w-full p-2 sm:p-3 rounded-lg sm:rounded-xl text-sm sm:text-base transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-purple-500 ${mode === "dark"
? "bg-white/5 border border-white/20 text-white placeholder-gray-400"
: "bg-gray-50 border border-gray-300 text-gray-800 placeholder-gray-500"
}`}
/>
</div>

{/* Subject */}
<div>
<label
className={`block text-xs font-medium mb-1 ${
mode === "dark"
? "text-gray-300"
: "text-gray-700"
}`}
className={`block text-xs font-medium mb-1 ${mode === "dark"
? "text-gray-300"
: "text-gray-700"
}`}
>
Subject
</label>
<select
className={`w-full p-2 sm:p-3 rounded-lg sm:rounded-xl text-sm sm:text-base transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-purple-500 ${
mode === "dark"
? "bg-white/5 border border-white/20 text-white placeholder-gray-400"
: "bg-gray-50 border border-gray-300 text-gray-800 placeholder-gray-500"
}`}
className={`w-full p-2 sm:p-3 rounded-lg sm:rounded-xl text-sm sm:text-base transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-purple-500 ${mode === "dark"
? "bg-white/5 border border-white/20 text-white placeholder-gray-400"
: "bg-gray-50 border border-gray-300 text-gray-800 placeholder-gray-500"
}`}
required
defaultValue=""
ref={subjectRef}
>
<option value="" disabled>
Select a subject
Expand All @@ -284,33 +296,32 @@ function Contact() {
{/* Message */}
<div className="relative">
<label
className={`block text-xs font-medium mb-1 ${
mode === "dark"
? "text-gray-300"
: "text-gray-700"
}`}
className={`block text-xs font-medium mb-1 ${mode === "dark"
? "text-gray-300"
: "text-gray-700"
}`}
>
Message
</label>

<textarea
placeholder="Type your message here..."
required
rows={4}
className={`w-full p-2 sm:p-3 rounded-lg sm:rounded-xl text-sm sm:text-base resize-none transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-purple-500 ${
mode === "dark"
? "bg-white/5 border border-white/20 text-white placeholder-gray-400"
: "bg-gray-50 border border-gray-300 text-gray-800 placeholder-gray-500"
}`}
ref={messageRef}
className={`w-full p-2 sm:p-3 rounded-lg sm:rounded-xl text-sm sm:text-base resize-none transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-purple-500 ${mode === "dark"
? "bg-white/5 border border-white/20 text-white placeholder-gray-400"
: "bg-gray-50 border border-gray-300 text-gray-800 placeholder-gray-500"
}`}
></textarea>

<button
onClick={handleSubmit}
disabled={isSubmitting}
className={`absolute bottom-2 sm:bottom-3 right-2 sm:right-3 flex items-center gap-1.5 sm:gap-2 rounded-full px-3 sm:px-4 py-1.5 sm:py-2 text-xs sm:text-sm font-semibold transition-all duration-300 hover:scale-105 focus:outline-none focus:ring-2 focus:ring-purple-500 ${
isSubmitting
? "bg-purple-400 cursor-wait text-white"
: "bg-purple-600 hover:bg-purple-700 text-white"
}`}
className={`absolute bottom-2 sm:bottom-3 right-2 sm:right-3 flex items-center gap-1.5 sm:gap-2 rounded-full px-3 sm:px-4 py-1.5 sm:py-2 text-xs sm:text-sm font-semibold transition-all duration-300 hover:scale-105 focus:outline-none focus:ring-2 focus:ring-purple-500 ${isSubmitting
? "bg-purple-400 cursor-wait text-white"
: "bg-purple-600 hover:bg-purple-700 text-white"
}`}
>
{isSubmitting ? "Sending..." : "Send"}
<Send className="w-4 h-4" />
Expand All @@ -325,11 +336,10 @@ function Contact() {
{/* Success Popup */}
{showPopup && (
<div
className={`fixed top-4 sm:top-6 left-1/2 transform -translate-x-1/2 z-50 w-[90%] sm:w-auto max-w-sm sm:max-w-md px-4 sm:px-6 py-3 sm:py-4 rounded-xl sm:rounded-2xl shadow-lg flex items-center gap-3 sm:gap-4 ${
mode === "dark"
? "bg-green-900 border border-green-700 text-green-100"
: "bg-green-100 border border-green-400 text-green-900"
}`}
className={`fixed top-4 sm:top-6 left-1/2 transform -translate-x-1/2 z-50 w-[90%] sm:w-auto max-w-sm sm:max-w-md px-4 sm:px-6 py-3 sm:py-4 rounded-xl sm:rounded-2xl shadow-lg flex items-center gap-3 sm:gap-4 ${mode === "dark"
? "bg-green-900 border border-green-700 text-green-100"
: "bg-green-100 border border-green-400 text-green-900"
}`}
>
<CheckCircle className="w-6 h-6 sm:w-7 sm:h-7" />
<div className="flex-1 text-xs sm:text-sm font-semibold">
Expand Down