From dd8843fa4ed42aa0ce24b322ad4697ec05e5b192 Mon Sep 17 00:00:00 2001 From: vimal-tech-starter Date: Tue, 14 Apr 2026 09:14:11 +0530 Subject: [PATCH] Contact form working now Handled via nginx Signed-off-by: vimal-tech-starter --- .vscode/settings.json | 6 +- contactUs/index.html | 42 ++++++--- css/contactUs.css | 103 ++++++++++++++++++++- js/contactUs.js | 202 +++++++++++++++++++++++++++++++++++++----- 4 files changed, 313 insertions(+), 40 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index da1ed86..e994928 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,7 @@ { - "liveServer.settings.port": 5501, + "liveServer.settings.port": 3000, "files.refactoring.autoSave": false, - "notebook.codeActionsOnSave": {} + "notebook.codeActionsOnSave": {}, + "liveServer.settings.cors": false, + "liveServer.settings.AdvanceCustomBrowserCmdLine": "" } \ No newline at end of file diff --git a/contactUs/index.html b/contactUs/index.html index 04150e0..1269488 100644 --- a/contactUs/index.html +++ b/contactUs/index.html @@ -232,22 +232,25 @@

Official Registration

Start a Conversation

-
+
- - + + +
- + +
- + +
- + +
+ + 0 / 1000 +
- + +
+ Your message has been sent. I’ll + get back to you soon. +
+ +

+ Having trouble? Please use the provided email, phone, or WhatsApp to reach out directly. +

I typically respond within 24 hours.

diff --git a/css/contactUs.css b/css/contactUs.css index 9e03eea..d8f2f7f 100644 --- a/css/contactUs.css +++ b/css/contactUs.css @@ -63,7 +63,7 @@ body.dark #particles-js canvas { } .mobile-card-content { - margin-top: 5px; + margin-top: 10px; } .mobile-new-line-msme { @@ -189,7 +189,7 @@ body.dark .input-group textarea:focus { } .info-card:hover { - transform: translateY(-5px); + transform: translateY(-10px); box-shadow: 0 15px 30px rgba(0, 0, 0, 0.1); } @@ -494,4 +494,103 @@ body.dark .udyam-box { font-size: 1rem; /* Prevents iOS Safari from auto-zooming on focus */ } +} + +/* --- Form Validation & State Styling --- */ + +/* Error Messages */ +.field-error { + color: #ef4444; + /* Red */ + font-size: 0.8rem; + font-weight: 500; + margin-top: 4px; + display: none; +} + +.field-error.visible { + display: block; + animation: fadeIn 0.3s ease; +} + +/* Input Error State */ +.input-error { + border-color: #ef4444 !important; + background-color: rgba(239, 68, 68, 0.05) !important; +} + +/* Character Counter Layout */ +.char-count-wrapper { + display: flex; + justify-content: space-between; + align-items: flex-start; + margin-top: 6px; + width: 100%; + /* Ensures it stretches across the bottom */ +} + +.char-count { + font-size: 0.8rem; + /* Changed from white to a sleek slate/gray color for light backgrounds */ + color: #64748b; + margin-left: auto; + transition: color 0.3s ease; + font-weight: 500; +} + +.char-count.near-limit { + color: #f59e0b; + /* Amber/Warning */ +} + +.char-count.at-limit { + color: #ef4444; + /* Red/Error */ +} + +/* Success Message */ +.form-success { + display: none; + margin-top: 15px; + padding: 12px 15px; + background: rgba(46, 204, 113, 0.1); + /* Transparent Green */ + border: 1px solid rgba(46, 204, 113, 0.3); + border-radius: 8px; + color: #2ecc71; + font-size: 0.9rem; + font-weight: 500; + text-align: center; +} + +.form-success.visible { + display: block; + animation: fadeIn 0.4s ease; +} + +/* Button Loading State */ +button.loading { + opacity: 0.7; + pointer-events: none; +} + +/* Fallback Text */ +.fallback-text { + font-size: 0.8rem; + color: rgba(0, 0, 0, 0.4); + text-align: center; + margin-top: 15px; + line-height: 1.5; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(-5px); + } + + to { + opacity: 1; + transform: translateY(0); + } } \ No newline at end of file diff --git a/js/contactUs.js b/js/contactUs.js index 94057a4..94c9c81 100644 --- a/js/contactUs.js +++ b/js/contactUs.js @@ -13,27 +13,181 @@ // Above all js code handled in global.js, // this file is only for contact us page specific js code like form submission logic -// document.addEventListener("DOMContentLoaded", () => { -// // Simple Form Submission Mock -// const contactForm = document.getElementById('contactForm'); -// const formStatus = document.getElementById('formStatus'); - -// if (contactForm) { -// contactForm.addEventListener('submit', (e) => { -// e.preventDefault(); // Stop page reload - -// // Show Success Message -// formStatus.textContent = "✓ Message sent successfully! I will get back to you soon."; -// formStatus.className = "form-status success"; - -// // Clear inputs -// contactForm.reset(); - -// // Hide message after 5 seconds -// setTimeout(() => { -// formStatus.style.display = 'none'; -// formStatus.className = "form-status"; -// }, 5000); -// }); -// } -// }); \ No newline at end of file +document.addEventListener("DOMContentLoaded", function () { + + const messageInput = document.getElementById("message"); + const charCountSpan = document.getElementById("charCount"); + const maxChars = 1000; + + // --- Character Counter Logic --- + if (messageInput && charCountSpan) { + messageInput.addEventListener("input", () => { + const currentLength = messageInput.value.length; + charCountSpan.textContent = `${currentLength} / ${maxChars}`; + + if (currentLength >= maxChars) { + charCountSpan.classList.add("at-limit"); + messageInput.value = messageInput.value.substring(0, maxChars); + } else if (currentLength > maxChars * 0.9) { + charCountSpan.classList.add("near-limit"); + charCountSpan.classList.remove("at-limit"); + } else { + charCountSpan.classList.remove("near-limit", "at-limit"); + } + }); + } + + const form = document.getElementById("contactForm"); + const submitBtn = document.getElementById("submitBtn"); + const successMsg = document.getElementById("formSuccess"); + const submitIcon = submitBtn ? submitBtn.querySelector(".submit-icon") : null; + + // --- Form Submission & Validation Logic --- + if (form) { + form.addEventListener("submit", function (e) { + e.preventDefault(); + + let isValid = true; + + // 1. Clear previous errors + document.querySelectorAll(".field-error").forEach((el) => el.classList.remove("visible")); + document.querySelectorAll(".input-error").forEach((el) => el.classList.remove("input-error")); + + // 2. Validate Name + const name = document.getElementById("name"); + if (!name.value.trim()) { + showError("name", "Name is required"); + isValid = false; + } + + // 3. Validate Email + const email = document.getElementById("email"); + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!email.value.trim()) { + showError("email", "Email is required"); + isValid = false; + } else if (!emailRegex.test(email.value)) { + showError("email", "Please enter a valid email address"); + isValid = false; + } + + // 4. Validate Service Dropdown (NEW) + const service = document.getElementById("service"); + if (!service.value) { + showError("service", "Please select a service"); + isValid = false; + } + + // 5. Validate Message + const message = document.getElementById("message"); + const messageText = message.value.trim(); + + if (!messageText) { + showError("message", "Please enter a message"); + isValid = false; + } else if (messageText.length < 10) { + showError("message", "Message must be at least 10 characters"); + isValid = false; + } + + // --- API Submission --- + if (isValid) { + // UI Loading State + submitBtn.classList.add("loading"); + submitBtn.querySelector(".btn-text").textContent = "Sending..."; + if (submitIcon) { + submitIcon.classList.remove("fa-paper-plane"); + submitIcon.classList.add("fa-spinner", "fa-spin"); + } + + // Prepare Data for Java Backend + const formData = { + name: name.value.trim(), + email: email.value.trim(), + // Maps the selected dropdown option to the "subject" key expected by your backend + subject: service.value, + message: messageText, + }; + + const API_BASE = window.location.hostname === "localhost" + ? "http://localhost:8080" + : "https://api.vimaltech.dev"; + + // Force the live VPS URL for testing + // const API_BASE = "https://api.vimaltech.dev"; + + // Send to Backend + fetch(`${API_BASE}/api/v1/contacts`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(formData), + }) + .then(async (response) => { + const data = await response.json(); + if (!response.ok) { + throw new Error(data.message || "Server error"); + } + return data; + }) + .then((data) => { + if (data.success) { + // Reset UI & Show Success + submitBtn.classList.remove("loading"); + submitBtn.querySelector(".btn-text").textContent = "Message Sent"; + if (submitIcon) { + submitIcon.classList.remove("fa-spinner", "fa-spin"); + submitIcon.classList.add("fa-check"); + } + + // Reset form fields + form.reset(); + // Reset character counter + if (charCountSpan) charCountSpan.textContent = `0 / ${maxChars}`; + + // Show success message + successMsg.classList.add("visible"); + + // Revert Button text after 3 seconds + setTimeout(() => { + submitBtn.querySelector(".btn-text").textContent = "Send Message"; + if (submitIcon) { + submitIcon.classList.remove("fa-check"); + submitIcon.classList.add("fa-paper-plane"); + } + successMsg.classList.remove("visible"); + }, 3000); + } else { + throw new Error(data.message); + } + }) + .catch((error) => { + console.error("API Error:", error); + + // Revert Loading UI + submitBtn.classList.remove("loading"); + submitBtn.querySelector(".btn-text").textContent = "Send Message"; + if (submitIcon) { + submitIcon.classList.remove("fa-spinner", "fa-spin"); + submitIcon.classList.add("fa-paper-plane"); + } + + alert("Submission failed. Please try again or use the fallback contact methods."); + }); + } + }); + } + + // --- Helper Function to Show Errors --- + function showError(fieldId, message) { + const input = document.getElementById(fieldId); + const errorSpan = document.getElementById(`error-${fieldId}`); + + if (input) input.classList.add("input-error"); + if (errorSpan) { + errorSpan.textContent = message; + errorSpan.classList.add("visible"); + } + } +}); \ No newline at end of file