feat: Redesign the contact page by adding a contact image and updating the form layout and styling.
This commit is contained in:
@@ -4,314 +4,349 @@
|
||||
* Pixel-perfect implementation based on Webflow design
|
||||
* Includes form validation, submission handling, and responsive layout
|
||||
*/
|
||||
import Layout from '../layouts/Layout.astro'
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
|
||||
// Metadata for SEO
|
||||
const title = '聯絡我們 | 恩群數位行銷'
|
||||
const description = '有任何問題嗎?歡迎聯絡恩群數位行銷,我們的專業團隊將竭誠為您服務。電話: 02 5570 0527,Email: enchuntaiwan@gmail.com'
|
||||
const title = "聯絡我們 | 恩群數位行銷";
|
||||
const description =
|
||||
"有任何問題嗎?歡迎聯絡恩群數位行銷,我們的專業團隊將竭誠為您服務。電話: 02 5570 0527,Email: enchuntaiwan@gmail.com";
|
||||
---
|
||||
|
||||
<Layout title={title} description={description}>
|
||||
<section class="py-16 bg-background scroll-mt-20 lg:py-8 md:py-8" id="contact">
|
||||
<div class="grid grid-cols-2 gap-12 items-center max-w-[1200px] mx-auto px-5 lg:grid-cols-1 lg:gap-8">
|
||||
<section class="py-20 bg-[#f4f5f7] scroll-mt-20 lg:py-10" id="contact">
|
||||
<div
|
||||
class="grid grid-cols-2 gap-16 items-start max-w-3xl mx-auto px-5 lg:grid-cols-2 lg:gap-10"
|
||||
>
|
||||
<!-- Contact Image Side -->
|
||||
<div class="flex justify-center items-center w-full lg:order-1 pt-4">
|
||||
<img
|
||||
src="https://enchun-cms.anlstudio.cc/api/media/file/61f24aa108528b532e942d09_Contact%20us-bro%201.svg"
|
||||
alt="聯絡我們插圖"
|
||||
class="w-full h-auto object-contain drop-shadow-sm"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Contact Form Side -->
|
||||
<div class="w-full">
|
||||
<h1 class="text-[2.5rem] font-bold leading-tight text-text-primary mb-4 lg:text-2xl md:text-[1.5rem]">聯絡我們</h1>
|
||||
<p class="text-lg font-normal leading-relaxed text-slate-600 mb-2">
|
||||
有任何問題嗎?歡迎聯絡我們,我們將竭誠為您服務。
|
||||
<div class="w-full lg:order-2">
|
||||
<h2
|
||||
class="text-4xl font-bold leading-snug text-[#2a5b83] mb-3 lg:text-3xl"
|
||||
>
|
||||
與我們聯絡
|
||||
</h2>
|
||||
<p
|
||||
class="text-lg font-medium text-[#2a5b83] mb-2 tracking-wide lg:text-base"
|
||||
>
|
||||
任何關於行銷的相關訊息或是詢價,歡迎與我們聯絡
|
||||
</p>
|
||||
<p class="text-sm italic text-text-muted mb-8">
|
||||
* 標註欄位為必填
|
||||
<p class="text-sm italic text-slate-500 mb-8">
|
||||
有星號的地方 (*) 是必填欄位
|
||||
</p>
|
||||
|
||||
<!-- Contact Form -->
|
||||
<form id="contact-form" class="p-8 bg-surface rounded-lg shadow-lg lg:p-6 md:p-4" novalidate>
|
||||
<form id="contact-form" class="w-full flex flex-col gap-6" novalidate>
|
||||
<!-- Success Message -->
|
||||
<div id="form-success" class="p-4 px-6 rounded-md mt-4 text-center font-medium bg-[#d4edda] text-[#155724] border border-[#c3e6cb] animate-[slideUp_0.3s_ease-out]" style="display: none;">
|
||||
<div
|
||||
id="form-success"
|
||||
class="p-4 px-6 rounded-lg text-center font-medium bg-[#d4edda] text-[#155724] border border-[#c3e6cb] animate-[slideUp_0.3s_ease-out]"
|
||||
style="display: none;"
|
||||
>
|
||||
感謝您的留言!我們會盡快回覆您。
|
||||
</div>
|
||||
|
||||
<!-- Error Message -->
|
||||
<div id="form-error" class="p-4 px-6 rounded-md mt-4 text-center font-medium bg-[#f8d7da] text-[#721c24] border border-[#f5c6cb] animate-[slideUp_0.3s_ease-out]" style="display: none;">
|
||||
<div
|
||||
id="form-error"
|
||||
class="p-4 px-6 rounded-lg text-center font-medium bg-[#f8d7da] text-[#721c24] border border-[#f5c6cb] animate-[slideUp_0.3s_ease-out]"
|
||||
style="display: none;"
|
||||
>
|
||||
送出失敗,請稍後再試或直接來電。
|
||||
</div>
|
||||
|
||||
<!-- Form Fields -->
|
||||
<div class="grid grid-cols-2 gap-6 mb-6 md:grid-cols-1 md:gap-4">
|
||||
<!-- Name Field -->
|
||||
<div class="flex flex-col mb-6">
|
||||
<label for="Name" class="text-sm font-semibold text-text-primary mb-2 block">
|
||||
姓名 <span class="text-primary">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="Name"
|
||||
name="Name"
|
||||
class="w-full px-4 py-3 border border-border rounded-md text-base leading-normal bg-background transition-all duration-150 font-sans text-text-primary focus:outline-none focus:border-primary focus:shadow-[0_0_0_3px_rgba(56,152,236,0.1)] focus:-translate-y-0.5 placeholder:text-text-muted [&.error]:border-[#dc3545] [&.error]:bg-[#fff5f5]"
|
||||
required
|
||||
minlength="2"
|
||||
maxlength="256"
|
||||
placeholder="請輸入您的姓名"
|
||||
/>
|
||||
<span class="error-message text-[#dc3545] text-sm mt-1 hidden" id="Name-error"></span>
|
||||
</div>
|
||||
<!-- Name Field -->
|
||||
<div class="flex flex-col">
|
||||
<label
|
||||
for="Name"
|
||||
class="text-[0.95rem] font-medium text-slate-500 mb-2 block"
|
||||
>
|
||||
姓名*
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="Name"
|
||||
name="Name"
|
||||
class="w-full px-4 py-2.5 border border-[#4a90e2] rounded-lg text-base bg-white focus:outline-none focus:ring-2 focus:ring-[#4a90e2]/20 transition-all shadow-sm [&.error]:border-[#dc3545]"
|
||||
required
|
||||
minlength="2"
|
||||
maxlength="256"
|
||||
/>
|
||||
<span
|
||||
class="error-message text-[#dc3545] text-sm mt-1 hidden"
|
||||
id="Name-error"></span>
|
||||
</div>
|
||||
|
||||
<!-- Phone Field -->
|
||||
<div class="flex flex-col mb-6">
|
||||
<label for="Phone" class="text-sm font-semibold text-text-primary mb-2 block">
|
||||
聯絡電話 <span class="text-primary">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="tel"
|
||||
id="Phone"
|
||||
name="Phone"
|
||||
class="w-full px-4 py-3 border border-border rounded-md text-base leading-normal bg-background transition-all duration-150 font-sans text-text-primary focus:outline-none focus:border-primary focus:shadow-[0_0_0_3px_rgba(56,152,236,0.1)] focus:-translate-y-0.5 placeholder:text-text-muted [&.error]:border-[#dc3545] [&.error]:bg-[#fff5f5]"
|
||||
required
|
||||
placeholder="請輸入您的電話號碼"
|
||||
/>
|
||||
<span class="error-message text-[#dc3545] text-sm mt-1 hidden" id="Phone-error"></span>
|
||||
</div>
|
||||
<!-- Phone Field -->
|
||||
<div class="flex flex-col">
|
||||
<label
|
||||
for="Phone"
|
||||
class="text-[0.95rem] font-medium text-slate-500 mb-2 block"
|
||||
>
|
||||
聯絡電話*
|
||||
</label>
|
||||
<input
|
||||
type="tel"
|
||||
id="Phone"
|
||||
name="Phone"
|
||||
class="w-full px-4 py-2.5 border border-white shadow-sm rounded-lg text-base bg-white focus:outline-none focus:border-[#4a90e2] focus:ring-2 focus:ring-[#4a90e2]/20 transition-all [&.error]:border-[#dc3545]"
|
||||
required
|
||||
/>
|
||||
<span
|
||||
class="error-message text-[#dc3545] text-sm mt-1 hidden"
|
||||
id="Phone-error"></span>
|
||||
</div>
|
||||
|
||||
<!-- Email Field -->
|
||||
<div class="flex flex-col mb-6">
|
||||
<label for="Email" class="text-sm font-semibold text-text-primary mb-2 block">
|
||||
Email <span class="text-primary">*</span>
|
||||
<div class="flex flex-col">
|
||||
<label
|
||||
for="Email"
|
||||
class="text-[0.95rem] font-medium text-slate-500 mb-2 block"
|
||||
>
|
||||
Email *
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
id="Email"
|
||||
name="Email"
|
||||
class="w-full px-4 py-3 border border-border rounded-md text-base leading-normal bg-background transition-all duration-150 font-sans text-text-primary focus:outline-none focus:border-primary focus:shadow-[0_0_0_3px_rgba(56,152,236,0.1)] focus:-translate-y-0.5 placeholder:text-text-muted [&.error]:border-[#dc3545] [&.error]:bg-[#fff5f5]"
|
||||
class="w-full px-4 py-2.5 border border-white shadow-sm rounded-lg text-base bg-white focus:outline-none focus:border-[#4a90e2] focus:ring-2 focus:ring-[#4a90e2]/20 transition-all [&.error]:border-[#dc3545]"
|
||||
required
|
||||
placeholder="請輸入您的 Email"
|
||||
/>
|
||||
<span class="error-message text-[#dc3545] text-sm mt-1 hidden" id="Email-error"></span>
|
||||
<span
|
||||
class="error-message text-[#dc3545] text-sm mt-1 hidden"
|
||||
id="Email-error"></span>
|
||||
</div>
|
||||
|
||||
<!-- Message Field -->
|
||||
<div class="flex flex-col mb-8">
|
||||
<label for="Message" class="text-sm font-semibold text-text-primary mb-2 block">
|
||||
聯絡訊息 <span class="text-primary">*</span>
|
||||
<div class="flex flex-col">
|
||||
<label
|
||||
for="Message"
|
||||
class="text-[0.95rem] font-medium text-slate-500 mb-2 block"
|
||||
>
|
||||
聯絡訊息
|
||||
</label>
|
||||
<textarea
|
||||
id="Message"
|
||||
name="Message"
|
||||
class="w-full px-4 py-3 border border-border rounded-md text-base leading-normal bg-background transition-all duration-150 font-sans text-text-primary focus:outline-none focus:border-primary focus:shadow-[0_0_0_3px_rgba(56,152,236,0.1)] focus:-translate-y-0.5 placeholder:text-text-muted min-h-[120px] resize-y [&.error]:border-[#dc3545] [&.error]:bg-[#fff5f5]"
|
||||
minlength="10"
|
||||
maxlength="5000"
|
||||
class="w-full px-4 py-3 border border-white shadow-sm rounded-lg text-base bg-white focus:outline-none focus:border-[#4a90e2] focus:ring-2 focus:ring-[#4a90e2]/20 transition-all resize-y min-h-[120px] [&.error]:border-[#dc3545]"
|
||||
required
|
||||
placeholder="請輸入您的訊息(至少 10 個字元)"
|
||||
></textarea>
|
||||
<span class="error-message text-[#dc3545] text-sm mt-1 hidden" id="Message-error"></span>
|
||||
minlength="10"
|
||||
maxlength="5000"></textarea>
|
||||
<span
|
||||
class="error-message text-[#dc3545] text-sm mt-1 hidden"
|
||||
id="Message-error"></span>
|
||||
</div>
|
||||
|
||||
<!-- Submit Button -->
|
||||
<button type="submit" class="bg-primary text-white border-none rounded-md px-8 py-[0.875rem] text-base font-semibold cursor-pointer transition-all duration-150 text-center inline-flex items-center justify-center min-w-[200px] w-full hover:bg-primary-hover hover:-translate-y-0.5 hover:shadow-md active:translate-y-0 disabled:bg-slate-500 disabled:cursor-not-allowed disabled:opacity-80" id="submit-btn">
|
||||
<span class="button-text">送出訊息</span>
|
||||
<span class="button-loading" style="display: none;">送出中...</span>
|
||||
</button>
|
||||
<div class="flex justify-end mt-2">
|
||||
<button
|
||||
type="submit"
|
||||
class="bg-[#2a5b83] text-white border-none rounded-lg px-10 py-[0.6rem] text-[1.05rem] font-medium cursor-pointer transition-all duration-200 hover:bg-[#1f4666] shadow-sm transform hover:-translate-y-0.5 min-w-[120px] disabled:bg-slate-400 disabled:cursor-not-allowed disabled:transform-none"
|
||||
id="submit-btn"
|
||||
>
|
||||
<span class="button-text">送出</span>
|
||||
<span class="button-loading" style="display: none;"
|
||||
>送出中...</span
|
||||
>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Contact Image Side -->
|
||||
<div class="flex flex-col gap-8 justify-center items-center">
|
||||
<div class="w-full rounded-lg overflow-hidden shadow-md bg-slate-100 aspect-[3/2]">
|
||||
<img
|
||||
src="/placeholder-contact.jpg"
|
||||
alt="聯絡恩群數位"
|
||||
width="600"
|
||||
height="400"
|
||||
class="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Contact Info Card -->
|
||||
<div class="w-full p-6 bg-white rounded-lg shadow-lg">
|
||||
<h3 class="text-xl font-semibold text-text-primary mb-4">聯絡資訊</h3>
|
||||
<div class="flex items-center gap-3 mb-4 text-[0.95rem] text-text-secondary">
|
||||
<svg class="w-5 h-5 flex-shrink-0 text-primary" viewBox="0 0 24 24"><path fill="currentColor" d="M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1.45-.17 2.53-.5.36-.11.74-.47 1.02-.75l2.2-2.2c.27-.27.36-.67.24-1.02-.37-1.12-.57-2.33-.57-3.57 0-.55.17-1.45.5-2.53.36-.11.74.47-1.14.75-1.02L2.05 21.05c.15.35.48.59.84.59l2.2.73c.36.12.74.12 1.06-.05.13-.07.22-.17.28-.28l2.2-2.2c.27-.27.36-.67.24-1.02-.37-1.12-.57-2.33-.57-3.57 0-.55.17-1.45.5-2.53.36-.11.74.47 1.14.75 1.02L2.05 21.05zM19.95 2.95c-.15-.35-.48-.59-.84-.59l-2.2-.73c-.36-.12-.74-.12-1.06.05-.13.07-.22.17-.28.28l-2.2 2.2c-.27.27-.36.67-.24 1.02.37 1.12.57 2.33.57 3.57 0 .55-.17 1.45-.5 2.53-.36.11.74-.47-1.14-.75l1.02-1.02 2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1.45-.17 2.53-.5.36-.11.74-.47 1.02-.75l2.2-2.2c.27-.27.36-.67.24-1.02-.37-1.12-.57-2.33-.57-3.57 0-.55.17-1.45.5-2.53.36-.11.74.47 1.02.75 1.02L2.05 21.05c-.15-.35-.48-.59-.84-.59l-2.2-.73c-.36-.12-.74-.12-1.06.05-.13.07-.22.17-.28.28l-2.2 2.2c-.27.27-.36.67-.24 1.02.37 1.12.57 2.33.57 3.57 0 .55-.17 1.45-.5 2.53-.36-.11-.74.47-1.14.75l1.02-1.02 2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1.45-.17 2.53-.5.36-.11.74-.47 1.02-.75l2.2-2.2c.27-.27.36-.67.24-1.02-.37-1.12-.57-2.33-.57-3.57 0-.55.17-1.45.5-2.53.36-.11-.74.47 1.14.75 1.02L2.05 21.05c.15.35.48.59.84.59l2.2.73c.36.12.74.12 1.06-.05.13-.07.22-.17.28-.28l2.2-2.2c.27-.27.36-.67.24-1.02-.37-1.12-.57-2.33-.57-3.57 0-.55.17-1.45.5-2.53.36-.11.74.47 1.14.75 1.02z"/></svg>
|
||||
<span>諮詢電話: 02 5570 0527</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-3 mb-4 text-[0.95rem] text-text-secondary">
|
||||
<svg class="w-5 h-5 flex-shrink-0 text-primary" viewBox="0 0 24 24"><path fill="currentColor" d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/></svg>
|
||||
<span>Email: <a href="mailto:enchuntaiwan@gmail.com" class="text-primary no-underline hover:underline">enchuntaiwan@gmail.com</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</Layout>
|
||||
|
||||
|
||||
<script>
|
||||
// Form validation and submission handler
|
||||
function initContactForm() {
|
||||
const form = document.getElementById('contact-form') as HTMLFormElement
|
||||
const submitBtn = document.getElementById('submit-btn') as HTMLButtonElement
|
||||
const successMsg = document.getElementById('form-success') as HTMLElement
|
||||
const errorMsg = document.getElementById('form-error') as HTMLElement
|
||||
const form = document.getElementById("contact-form") as HTMLFormElement;
|
||||
const submitBtn = document.getElementById(
|
||||
"submit-btn",
|
||||
) as HTMLButtonElement;
|
||||
const successMsg = document.getElementById("form-success") as HTMLElement;
|
||||
const errorMsg = document.getElementById("form-error") as HTMLElement;
|
||||
|
||||
if (!form) return
|
||||
if (!form) return;
|
||||
|
||||
// Validation patterns
|
||||
const patterns = {
|
||||
Name: /^[\u4e00-\u9fa5a-zA-Z\s]{2,256}$/,
|
||||
Phone: /^[0-9\-\s\+]{6,20}$/,
|
||||
Email: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
|
||||
Message: /^.{10,5000}$/
|
||||
}
|
||||
Message: /^.{10,5000}$/,
|
||||
};
|
||||
|
||||
// Validation function
|
||||
function validateField(input: HTMLInputElement | HTMLTextAreaElement): boolean {
|
||||
const name = input.name
|
||||
const value = input.value.trim()
|
||||
const errorSpan = document.getElementById(`${name}-error`) as HTMLElement
|
||||
function validateField(
|
||||
input: HTMLInputElement | HTMLTextAreaElement,
|
||||
): boolean {
|
||||
const name = input.name;
|
||||
const value = input.value.trim();
|
||||
const errorSpan = document.getElementById(`${name}-error`) as HTMLElement;
|
||||
|
||||
if (!input.hasAttribute('required') && !value) {
|
||||
clearError(input, errorSpan)
|
||||
return true
|
||||
if (!input.hasAttribute("required") && !value) {
|
||||
clearError(input, errorSpan);
|
||||
return true;
|
||||
}
|
||||
|
||||
let isValid = true
|
||||
let errorMessage = ''
|
||||
let isValid = true;
|
||||
let errorMessage = "";
|
||||
|
||||
// Required check
|
||||
if (input.hasAttribute('required') && !value) {
|
||||
isValid = false
|
||||
errorMessage = '此欄位為必填'
|
||||
if (input.hasAttribute("required") && !value) {
|
||||
isValid = false;
|
||||
errorMessage = "此欄位為必填";
|
||||
}
|
||||
// Pattern validation
|
||||
else if (patterns[name as keyof typeof patterns] && !patterns[name as keyof typeof patterns].test(value)) {
|
||||
isValid = false
|
||||
else if (
|
||||
patterns[name as keyof typeof patterns] &&
|
||||
!patterns[name as keyof typeof patterns].test(value)
|
||||
) {
|
||||
isValid = false;
|
||||
switch (name) {
|
||||
case 'Name':
|
||||
errorMessage = '請輸入有效的姓名(至少 2 個字元)'
|
||||
break
|
||||
case 'Phone':
|
||||
errorMessage = '請輸入有效的電話號碼'
|
||||
break
|
||||
case 'Email':
|
||||
errorMessage = '請輸入有效的 Email 格式'
|
||||
break
|
||||
case 'Message':
|
||||
errorMessage = '訊息至少需要 10 個字元'
|
||||
break
|
||||
case "Name":
|
||||
errorMessage = "請輸入有效的姓名(至少 2 個字元)";
|
||||
break;
|
||||
case "Phone":
|
||||
errorMessage = "請輸入有效的電話號碼";
|
||||
break;
|
||||
case "Email":
|
||||
errorMessage = "請輸入有效的 Email 格式";
|
||||
break;
|
||||
case "Message":
|
||||
errorMessage = "訊息至少需要 10 個字元";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Show/hide error
|
||||
if (!isValid) {
|
||||
input.classList.add('error')
|
||||
input.classList.add("error");
|
||||
if (errorSpan) {
|
||||
errorSpan.textContent = errorMessage
|
||||
errorSpan.style.display = 'block'
|
||||
errorSpan.textContent = errorMessage;
|
||||
errorSpan.style.display = "block";
|
||||
}
|
||||
} else {
|
||||
clearError(input, errorSpan)
|
||||
clearError(input, errorSpan);
|
||||
}
|
||||
|
||||
return isValid
|
||||
return isValid;
|
||||
}
|
||||
|
||||
function clearError(input: HTMLInputElement | HTMLTextAreaElement, errorSpan: HTMLElement) {
|
||||
input.classList.remove('error')
|
||||
function clearError(
|
||||
input: HTMLInputElement | HTMLTextAreaElement,
|
||||
errorSpan: HTMLElement,
|
||||
) {
|
||||
input.classList.remove("error");
|
||||
if (errorSpan) {
|
||||
errorSpan.textContent = ''
|
||||
errorSpan.style.display = 'none'
|
||||
errorSpan.textContent = "";
|
||||
errorSpan.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
// Real-time validation on blur
|
||||
form.querySelectorAll('input, textarea').forEach((field) => {
|
||||
field.addEventListener('blur', () => {
|
||||
validateField(field as HTMLInputElement | HTMLTextAreaElement)
|
||||
})
|
||||
form.querySelectorAll("input, textarea").forEach((field) => {
|
||||
field.addEventListener("blur", () => {
|
||||
validateField(field as HTMLInputElement | HTMLTextAreaElement);
|
||||
});
|
||||
|
||||
field.addEventListener('input', () => {
|
||||
const input = field as HTMLInputElement | HTMLTextAreaElement
|
||||
if (input.classList.contains('error')) {
|
||||
validateField(input)
|
||||
field.addEventListener("input", () => {
|
||||
const input = field as HTMLInputElement | HTMLTextAreaElement;
|
||||
if (input.classList.contains("error")) {
|
||||
validateField(input);
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
// Form submission
|
||||
form.addEventListener('submit', async (e) => {
|
||||
e.preventDefault()
|
||||
form.addEventListener("submit", async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
// Validate all fields
|
||||
const inputs = form.querySelectorAll('input, textarea') as NodeListOf<HTMLInputElement | HTMLTextAreaElement>
|
||||
let isFormValid = true
|
||||
const inputs = form.querySelectorAll("input, textarea") as NodeListOf<
|
||||
HTMLInputElement | HTMLTextAreaElement
|
||||
>;
|
||||
let isFormValid = true;
|
||||
|
||||
inputs.forEach((input) => {
|
||||
if (!validateField(input)) {
|
||||
isFormValid = false
|
||||
isFormValid = false;
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
if (!isFormValid) {
|
||||
// Scroll to first error
|
||||
const firstError = form.querySelector('.input_field.error') as HTMLElement
|
||||
const firstError = form.querySelector(
|
||||
".input_field.error",
|
||||
) as HTMLElement;
|
||||
if (firstError) {
|
||||
firstError.scrollIntoView({ behavior: 'smooth', block: 'center' })
|
||||
firstError.scrollIntoView({ behavior: "smooth", block: "center" });
|
||||
}
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
// Show loading state
|
||||
submitBtn.disabled = true
|
||||
const buttonText = submitBtn.querySelector('.button-text') as HTMLElement
|
||||
const buttonLoading = submitBtn.querySelector('.button-loading') as HTMLElement
|
||||
if (buttonText) buttonText.style.display = 'none'
|
||||
if (buttonLoading) buttonLoading.style.display = 'inline'
|
||||
submitBtn.disabled = true;
|
||||
const buttonText = submitBtn.querySelector(".button-text") as HTMLElement;
|
||||
const buttonLoading = submitBtn.querySelector(
|
||||
".button-loading",
|
||||
) as HTMLElement;
|
||||
if (buttonText) buttonText.style.display = "none";
|
||||
if (buttonLoading) buttonLoading.style.display = "inline";
|
||||
|
||||
// Hide previous messages
|
||||
successMsg.style.display = 'none'
|
||||
errorMsg.style.display = 'none'
|
||||
successMsg.style.display = "none";
|
||||
errorMsg.style.display = "none";
|
||||
|
||||
// Collect form data
|
||||
const formData = new FormData(form)
|
||||
const formData = new FormData(form);
|
||||
const data = {
|
||||
name: formData.get('Name'),
|
||||
phone: formData.get('Phone'),
|
||||
email: formData.get('Email'),
|
||||
message: formData.get('Message')
|
||||
}
|
||||
name: formData.get("Name"),
|
||||
phone: formData.get("Phone"),
|
||||
email: formData.get("Email"),
|
||||
message: formData.get("Message"),
|
||||
};
|
||||
|
||||
try {
|
||||
// Submit to backend (via API proxy)
|
||||
const response = await fetch('/api/contact', {
|
||||
method: 'POST',
|
||||
const response = await fetch("/api/contact", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
})
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
// Success
|
||||
successMsg.style.display = 'block'
|
||||
form.reset()
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
successMsg.style.display = "block";
|
||||
form.reset();
|
||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||
} else {
|
||||
throw new Error('Submission failed')
|
||||
throw new Error("Submission failed");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Form submission error:', error)
|
||||
errorMsg.style.display = 'block'
|
||||
console.error("Form submission error:", error);
|
||||
errorMsg.style.display = "block";
|
||||
} finally {
|
||||
// Reset button state
|
||||
submitBtn.disabled = false
|
||||
if (buttonText) buttonText.style.display = 'inline'
|
||||
if (buttonLoading) buttonLoading.style.display = 'none'
|
||||
submitBtn.disabled = false;
|
||||
if (buttonText) buttonText.style.display = "inline";
|
||||
if (buttonLoading) buttonLoading.style.display = "none";
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize when DOM is ready
|
||||
document.addEventListener('DOMContentLoaded', initContactForm)
|
||||
if (document.readyState !== 'loading') {
|
||||
initContactForm()
|
||||
document.addEventListener("DOMContentLoaded", initContactForm);
|
||||
if (document.readyState !== "loading") {
|
||||
initContactForm();
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user