|
1 | | - |
| 1 | +<!DOCTYPE html> |
2 | 2 | <html> |
3 | 3 | <head> |
4 | 4 | <meta name="title" content="JTech - Jewish Tech & Filtering Forum" /> |
|
13 | 13 | <meta name="robots" content="index, follow" /> |
14 | 14 | <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> |
15 | 15 | <meta name="language" content="English" /> |
16 | | - <meta name="author" content="doniel@mesnan.com" /> |
| 16 | + <meta name="author" content="tripleuworld@gmail.com" /> |
17 | 17 | <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
18 | | - <link rel="icon" href="/img/home/metadata.png"> |
| 18 | + <link rel="icon" href="/img/home/metadata.png" /> |
19 | 19 | <script src="https://unpkg.com/@tailwindcss/browser@4" defer></script> |
20 | 20 | <link |
21 | 21 | rel="stylesheet" |
22 | 22 | href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css" |
23 | | - integrity="sha512-Evv84Mr4kqVGRNSgIGL/F/aIDqQb7xQ2vcrdIwxfjThSH8CSR7PBEakCr51Ck+w+/U6swU2Im1vVX0SVk9ABhg==" |
24 | 23 | crossorigin="anonymous" |
25 | 24 | referrerpolicy="no-referrer" |
26 | 25 | /> |
27 | 26 | <link rel="stylesheet" href="/constants/loader.css" /> |
28 | 27 | <script src="/constants/loader.js"></script> |
29 | 28 | <style> |
30 | | - img { |
31 | | - pointer-events: none; |
32 | | - } |
| 29 | + img { pointer-events: none; } |
33 | 30 | .text-glow { |
34 | 31 | text-shadow: |
35 | 32 | 0 0 8px rgba(59, 130, 246, 0.8), |
36 | 33 | 0 0 20px rgba(59, 130, 246, 0.5); |
37 | 34 | } |
38 | 35 | </style> |
| 36 | + <title>Contact Us - JTech</title> |
39 | 37 | </head> |
40 | 38 | <body class="bg-black"> |
41 | 39 | <div id="header-container"></div> |
42 | 40 |
|
43 | | - <!--Header--> |
| 41 | + <!-- Header --> |
44 | 42 | <div class="bg-black px-6 pt-24 pb-12 lg:px-8"> |
45 | | - <h2 |
46 | | - class="mt-20 text-5xl font-semibold tracking-tight text-white sm:text-7xl text-center text-glow" |
47 | | - > |
| 43 | + <h2 class="mt-20 text-5xl font-semibold tracking-tight text-white sm:text-7xl text-center text-glow"> |
48 | 44 | Contact Us |
49 | 45 | </h2> |
50 | 46 | </div> |
| 47 | + |
51 | 48 | <div class="mx-auto max-w-6xl px-6 sm:px-6 pb-24"> |
52 | | - <div |
53 | | - class="relative isolate overflow-hidden bg-gray-900 px-6 py-6 shadow-2xl sm:rounded-3xl sm:px-6" |
54 | | - > |
55 | | - <!-- ✅ Using FormSubmit instead of mailto --> |
| 49 | + <div class="relative isolate overflow-hidden bg-gray-900 px-6 py-6 shadow-2xl sm:rounded-3xl sm:px-6"> |
| 50 | + <!-- Contact Form --> |
56 | 51 | <form |
57 | 52 | id="contact-form" |
58 | | - action="https://formsubmit.co/tripleuworld@gmail.com" |
| 53 | + action="https://jtechcontact.therealadmins.workers.dev/contact" |
59 | 54 | method="POST" |
60 | 55 | class="grid grid-cols-1 gap-6 sm:grid-cols-2" |
61 | 56 | > |
62 | | - <!-- Prevent spam --> |
63 | | - <input type="hidden" name="_captcha" value="false" /> |
| 57 | + <!-- Honeypot --> |
| 58 | + <input type="text" name="_honey" class="hidden" tabindex="-1" autocomplete="off" /> |
64 | 59 |
|
65 | 60 | <div class="flex flex-col"> |
66 | | - <label |
67 | | - for="name" |
68 | | - class="block text-sm font-medium text-gray-300" |
69 | | - >Name</label |
70 | | - > |
| 61 | + <label for="name" class="block text-sm font-medium text-gray-300">Name</label> |
71 | 62 | <input |
72 | 63 | type="text" |
73 | 64 | id="name" |
|
76 | 67 | class="mt-1 w-full rounded-md border-gray-700 bg-gray-800 text-white focus:border-blue-500 focus:ring-blue-500" |
77 | 68 | /> |
78 | 69 | </div> |
| 70 | + |
79 | 71 | <div class="flex flex-col"> |
80 | | - <label |
81 | | - for="phone" |
82 | | - class="block text-sm font-medium text-gray-300" |
83 | | - >Phone Number</label |
84 | | - > |
| 72 | + <label for="phone" class="block text-sm font-medium text-gray-300">Phone Number</label> |
85 | 73 | <input |
86 | 74 | type="tel" |
87 | 75 | id="phone" |
|
90 | 78 | class="mt-1 w-full rounded-md border-gray-700 bg-gray-800 text-white focus:border-blue-500 focus:ring-blue-500" |
91 | 79 | /> |
92 | 80 | </div> |
| 81 | + |
93 | 82 | <div class="flex flex-col sm:col-span-2"> |
94 | | - <label |
95 | | - for="email" |
96 | | - class="block text-sm font-medium text-gray-300" |
97 | | - >Email</label |
98 | | - > |
| 83 | + <label for="email" class="block text-sm font-medium text-gray-300">Email</label> |
99 | 84 | <input |
100 | 85 | type="email" |
101 | 86 | id="email" |
|
104 | 89 | class="mt-1 w-full rounded-md border-gray-700 bg-gray-800 text-white focus:border-blue-500 focus:ring-blue-500" |
105 | 90 | /> |
106 | 91 | </div> |
| 92 | + |
107 | 93 | <div class="flex flex-col sm:col-span-2"> |
108 | | - <label |
109 | | - for="message" |
110 | | - class="block text-sm font-medium text-gray-300" |
111 | | - >Message</label |
112 | | - > |
| 94 | + <label for="message" class="block text-sm font-medium text-gray-300">Message</label> |
113 | 95 | <textarea |
114 | 96 | id="message" |
115 | 97 | name="message" |
|
118 | 100 | class="mt-1 w-full rounded-md border-gray-700 bg-gray-800 text-white focus:border-blue-500 focus:ring-blue-500" |
119 | 101 | ></textarea> |
120 | 102 | </div> |
| 103 | + |
| 104 | + <!-- ✅ Turnstile widget --> |
| 105 | + <div class="sm:col-span-2"> |
| 106 | + <div class="cf-turnstile" data-sitekey="0x4AAAAAABxAWXqdcjbzdErE"></div> |
| 107 | + </div> |
| 108 | + |
121 | 109 | <div class="sm:col-span-2"> |
122 | 110 | <button |
| 111 | + id="submit-btn" |
123 | 112 | type="submit" |
124 | 113 | class="w-full rounded-md bg-blue-600 px-4 py-2 text-sm font-semibold text-white hover:bg-blue-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-500" |
125 | 114 | > |
126 | 115 | Send |
127 | 116 | </button> |
128 | | - <p |
129 | | - id="success-message" |
130 | | - class="mt-2 hidden text-center text-sm text-green-500" |
131 | | - > |
| 117 | + <p id="success-message" class="mt-2 hidden text-center text-sm text-green-500"> |
132 | 118 | Message has been sent, thank you. |
133 | 119 | </p> |
| 120 | + <p id="error-message" class="mt-2 hidden text-center text-sm text-red-500"></p> |
134 | 121 | </div> |
135 | 122 | </form> |
136 | 123 | </div> |
137 | 124 | </div> |
138 | 125 |
|
139 | | - <script |
140 | | - type="module" |
141 | | - src="https://unpkg.com/@material-tailwind/html@latest/scripts/tooltip.js" |
142 | | - ></script> |
| 126 | + <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script> |
143 | 127 |
|
144 | 128 | <div id="footer-container"></div> |
145 | 129 |
|
146 | 130 | <script> |
147 | 131 | document.addEventListener("DOMContentLoaded", () => { |
| 132 | + // Load footer & header |
148 | 133 | fetch("/constants/footer.html") |
149 | | - .then((response) => response.text()) |
150 | | - .then((data) => { |
151 | | - document.getElementById("footer-container").innerHTML = data; |
152 | | - }); |
| 134 | + .then((r) => r.text()) |
| 135 | + .then((html) => (document.getElementById("footer-container").innerHTML = html)); |
153 | 136 |
|
154 | 137 | fetch("/constants/headerDark.html") |
155 | | - .then((response) => response.text()) |
156 | | - .then((data) => { |
157 | | - document.getElementById("header-container").innerHTML = data; |
| 138 | + .then((r) => r.text()) |
| 139 | + .then((html) => { |
| 140 | + document.getElementById("header-container").innerHTML = html; |
158 | 141 | const mobileMenu = document.getElementById("mobile-menu"); |
159 | 142 | const openButton = document.getElementById("mobile-menu-button"); |
160 | 143 | const closeButton = document.getElementById("mobile-menu-close"); |
161 | | - |
162 | | - openButton.addEventListener("click", function () { |
163 | | - mobileMenu.classList.remove("hidden"); |
164 | | - }); |
165 | | - |
166 | | - closeButton.addEventListener("click", function () { |
167 | | - mobileMenu.classList.add("hidden"); |
168 | | - }); |
| 144 | + openButton?.addEventListener("click", () => mobileMenu?.classList.remove("hidden")); |
| 145 | + closeButton?.addEventListener("click", () => mobileMenu?.classList.add("hidden")); |
169 | 146 | }); |
170 | 147 |
|
171 | 148 | const form = document.getElementById("contact-form"); |
172 | 149 | const successMessage = document.getElementById("success-message"); |
| 150 | + const errorMessage = document.getElementById("error-message"); |
| 151 | + const submitBtn = document.getElementById("submit-btn"); |
| 152 | + |
173 | 153 | form.addEventListener("submit", async (e) => { |
174 | 154 | e.preventDefault(); |
175 | | - const formData = new FormData(form); |
176 | | - const response = await fetch(form.action, { |
177 | | - method: "POST", |
178 | | - body: formData, |
179 | | - headers: { |
180 | | - Accept: "application/json", |
181 | | - }, |
182 | | - }); |
183 | | - if (response.ok) { |
| 155 | + successMessage.classList.add("hidden"); |
| 156 | + errorMessage.classList.add("hidden"); |
| 157 | + submitBtn.disabled = true; |
| 158 | + |
| 159 | + try { |
| 160 | + const fd = new FormData(form); |
| 161 | + const resp = await fetch(form.action, { method: "POST", body: fd }); |
| 162 | + const data = await resp.json(); |
| 163 | + if (!resp.ok || !data.ok) throw new Error(data.error || "Submission failed"); |
184 | 164 | form.reset(); |
185 | 165 | successMessage.classList.remove("hidden"); |
| 166 | + } catch (err) { |
| 167 | + errorMessage.textContent = err.message || "Network error. Please try again."; |
| 168 | + errorMessage.classList.remove("hidden"); |
| 169 | + } finally { |
| 170 | + submitBtn.disabled = false; |
186 | 171 | } |
187 | 172 | }); |
188 | 173 | }); |
|
192 | 177 | .slide-up { |
193 | 178 | transform: translateY(100%); |
194 | 179 | opacity: 0; |
195 | | - transition: |
196 | | - transform 0.5s ease, |
197 | | - opacity 0.5s ease; |
| 180 | + transition: transform 0.5s ease, opacity 0.5s ease; |
198 | 181 | } |
199 | | - |
200 | 182 | .slide-up.show { |
201 | 183 | transform: translateY(0); |
202 | 184 | opacity: 1; |
|
0 commit comments