John2121 commited on
Commit
f2be9fe
·
1 Parent(s): 60c1ea9

Style: Merge automotive theme with APT pipeline for industry-standard UI/UX

Browse files
Files changed (1) hide show
  1. quote-calculator.html +271 -165
quote-calculator.html CHANGED
@@ -6,97 +6,138 @@
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
  <title>Hooper Auto - Service Estimate</title>
8
  <link rel="stylesheet" href="../llama-universal-netlify-proxy/style.css">
9
- <link rel="preconnect" href="https://fonts.googleapis.com">
10
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
- <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
12
- <script src="https://cdn.tailwindcss.com"></script>
13
- <script>
14
- tailwind.config = {
15
- theme: {
16
- extend: {
17
- colors: {
18
- automotive: {
19
- 50: '#f3f4f6', // light silver
20
- 100: '#e5e7eb',
21
- 200: '#d1d5db',
22
- 300: '#9ca3af',
23
- 400: '#6b7280', // steel gray
24
- 500: '#374151', // dark steel
25
- 600: '#1f2937', // near-black
26
- 700: '#111827', // black
27
- 800: '#18181b', // deep black
28
- 900: '#0f0f10',
29
- },
30
- accent: {
31
- red: '#e11d48', // automotive red
32
- yellow: '#fbbf24', // accent yellow
33
- }
34
- },
35
- fontFamily: {
36
- 'body': ['Inter', 'Arial', 'sans-serif'],
37
- }
38
- }
39
- }
40
- }
41
- </script>
42
  <style>
43
- @media (max-width: 640px) {
44
- body {
45
- padding: 0 !important;
46
- }
47
 
48
- .mobile-form {
49
- padding: 0.5rem !important;
50
- }
 
 
51
 
52
- .mobile-section {
53
- padding: 0.5rem !important;
54
- }
 
 
 
 
 
55
 
56
- .mobile-title {
57
- font-size: 1.3rem !important;
58
- }
 
 
59
 
60
- .mobile-btn {
61
- font-size: 1rem !important;
62
- padding: 0.7rem 0.5rem !important;
63
- }
64
  }
65
 
66
- .form-group {
67
- margin-bottom: 0.5rem;
68
  }
69
 
70
- .form-group .form-toggle {
71
- display: flex;
72
- align-items: center;
73
- justify-content: space-between;
 
 
 
 
 
74
  cursor: pointer;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  font-weight: 600;
76
- color: #fbbf24;
77
- background: #374151;
78
- border-radius: 0.5rem;
79
- padding: 0.5rem 1rem;
80
- margin-bottom: 0.1rem;
81
  }
82
 
83
- .form-group .form-content {
84
- display: none;
85
- padding: 0.5rem 1rem 0.7rem 1rem;
86
- background: #1f2937;
87
- border-radius: 0.5rem;
 
88
  }
89
 
90
  .form-group.open .form-content {
91
- display: block;
92
  }
93
 
94
- .form-group .arrow {
95
- transition: transform 0.2s;
96
  }
97
 
98
- .form-group.open .arrow {
99
- transform: rotate(90deg);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  }
101
  </style>
102
  </head>
@@ -157,25 +198,81 @@
157
  </div>
158
  <!-- Step 4: Output Recommendation -->
159
  <div id="step4" class="mb-4 hidden">
160
- <div class="bg-automotive-500/80 rounded-lg p-4 mb-2">
161
- <div class="text-lg font-bold text-accent-red mb-1">Recommended Service:</div>
162
- <div id="apt-recommend" class="text-automotive-50 mb-2"></div>
163
- <div class="text-accent-yellow font-bold" id="apt-price"></div>
164
- </div>
165
- <div class="mb-2">
166
- <label class="block text-accent-yellow font-semibold mb-1">Your Name (optional)</label>
167
- <input type="text" id="user-name"
168
- class="w-full px-4 py-2 rounded-lg bg-white/10 border border-automotive-400 text-automotive-50 mb-2"
169
- placeholder="Your Name">
170
- <label class="block text-accent-yellow font-semibold mb-1">Email (for quote reply)</label>
171
- <input type="email" id="user-email"
172
- class="w-full px-4 py-2 rounded-lg bg-white/10 border border-automotive-400 text-automotive-50 mb-2"
173
- placeholder="hooperautorepair70@gmail.com">
174
- </div>
175
- <button id="apt-submit" class="apt-btn w-full">Request Estimate</button>
176
- <div id="apt-success" class="hidden text-accent-yellow text-center font-semibold mt-2">Request sent!
177
- Hooper Automotive will contact you soon.<br>Email: <a href="mailto:hooperautorepair70@gmail.com"
178
- class="underline">hooperautorepair70@gmail.com</a><br>Phone: (715) 421-9792</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
179
  </div>
180
  </div>
181
  </section>
@@ -186,75 +283,75 @@
186
  ...existing code...
187
  -->
188
  <script>
189
- // --- Llama AI Chat Only ---
190
- (function () {
191
- const messagesDiv = document.getElementById('llama-messages');
192
- const userInput = document.getElementById('llama-user-input');
193
- const sendBtn = document.getElementById('llama-send-btn');
194
- const API_URL = "https://llama-universal-netlify-project.netlify.app/.netlify/functions/llama-proxy?path=/chat/completions";
195
- function appendMessage(text, sender) {
196
- const div = document.createElement('div');
197
- div.className = 'msg ' + sender;
198
- // Support markdown and preserve line breaks for AI output
199
- if (sender === 'ai') {
200
- // If text is markdown, render as HTML (basic)
201
- div.innerHTML = text.replace(/\n/g, '<br>');
202
- } else {
203
- div.textContent = (sender === 'user' ? 'You: ' : 'AI: ') + text;
204
- }
205
- messagesDiv.appendChild(div);
206
- messagesDiv.scrollTop = messagesDiv.scrollHeight;
207
  }
208
- async function sendMessage() {
209
- const text = userInput.value.trim();
210
- if (!text) return;
211
- appendMessage(text, 'user');
212
- userInput.value = '';
213
- sendBtn.disabled = true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
  try {
215
- const body = {
216
- model: "Llama-3.3-8B-Instruct",
217
- messages: [
218
- { role: 'system', content: "You are a helpful, friendly Hooper Automotive service phone representative. Greet the customer, ask clarifying questions, and provide expert auto advice." },
219
- { role: 'user', content: text }
220
- ]
221
- };
222
- const res = await fetch(API_URL, {
223
- method: 'POST',
224
- headers: { 'Content-Type': 'application/json' },
225
- body: JSON.stringify(body)
226
- });
227
- let data, aiMsg;
228
- try {
229
- data = await res.json();
230
- } catch (jsonErr) {
231
- appendMessage('Non-JSON response: ' + await res.text(), 'ai');
232
- return;
233
- }
234
- // Industry-standard: handle OpenAI and HF-style, fallback to completion_message.content.text
235
- if (data.choices?.[0]?.message?.content) {
236
- aiMsg = data.choices[0].message.content;
237
- } else if (data.completion_message?.content?.text) {
238
- aiMsg = data.completion_message.content.text;
239
- } else if (data.completion_message?.content) {
240
- aiMsg = data.completion_message.content;
241
- } else if (data.error) {
242
- aiMsg = 'API error: ' + (data.error.message || JSON.stringify(data.error));
243
- } else {
244
- aiMsg = 'Raw response: ' + JSON.stringify(data);
245
- }
246
- appendMessage(aiMsg, 'ai');
247
- } catch (e) {
248
- appendMessage('Error: ' + e.message, 'ai');
249
- } finally {
250
- sendBtn.disabled = false;
251
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
  }
253
- sendBtn.onclick = sendMessage;
254
- userInput.addEventListener('keydown', e => {
255
- if (e.key === 'Enter') sendMessage();
256
- });
257
- })();
 
258
  // --- APT Step-by-Step Calculator ---
259
  // Variables: x1=category, x2=symptom, r1=service/price, x3=vehicle info, y1=output
260
  // Pipeline: y1 = f(r1(x1, x2), x3)
@@ -343,19 +440,28 @@
343
  // Output recommendation
344
  document.getElementById('apt-recommend').textContent = `${selectedService} (${selectedSymptom})`;
345
  document.getElementById('apt-price').textContent = selectedPrice;
 
 
 
 
 
346
  document.getElementById('step4').classList.remove('hidden');
347
  };
348
 
349
- // Step 4: Submit (simulate request)
350
- document.getElementById('apt-submit').onclick = function (e) {
351
- e.preventDefault();
352
- document.getElementById('apt-submit').disabled = true;
353
- document.getElementById('apt-success').classList.remove('hidden');
354
- setTimeout(() => {
355
- document.getElementById('apt-submit').disabled = false;
356
- document.getElementById('apt-success').classList.add('hidden');
357
- }, 3000);
358
- };
 
 
 
 
359
 
360
  // Style for APT buttons
361
  const style = document.createElement('style');
 
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
  <title>Hooper Auto - Service Estimate</title>
8
  <link rel="stylesheet" href="../llama-universal-netlify-proxy/style.css">
9
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tailwindcss@3.3.3/dist/tailwind.min.css">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  <style>
11
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
 
 
 
12
 
13
+ body {
14
+ font-family: 'Inter', Arial, sans-serif;
15
+ background: #111827;
16
+ color: #f3f4f6;
17
+ }
18
 
19
+ .main-card,
20
+ .mobile-section {
21
+ background: #1f2937 !important;
22
+ border: 4px solid #e11d48 !important;
23
+ border-radius: 1.25rem !important;
24
+ box-shadow: 0 4px 32px 0 #0004, 0 1.5px 6px 0 #e11d48;
25
+ padding: 2rem !important;
26
+ }
27
 
28
+ .form-group .form-toggle {
29
+ background: #374151 !important;
30
+ color: #fbbf24 !important;
31
+ border-radius: 0.5rem !important;
32
+ }
33
 
34
+ .form-group .form-content {
35
+ background: #18181b !important;
36
+ border-radius: 0.5rem !important;
 
37
  }
38
 
39
+ .form-group.open .form-content {
40
+ background: #18181b !important;
41
  }
42
 
43
+ .apt-btn,
44
+ .mobile-btn,
45
+ .form-content button[type=submit] {
46
+ background: #e11d48 !important;
47
+ color: #fff !important;
48
+ font-weight: 700;
49
+ border-radius: 0.5rem !important;
50
+ padding: 0.9rem 1.2rem !important;
51
+ border: none;
52
  cursor: pointer;
53
+ transition: background 0.2s;
54
+ font-size: 1.15rem;
55
+ box-shadow: 0 2px 8px #e11d4822;
56
+ text-transform: uppercase;
57
+ letter-spacing: 0.04em;
58
+ }
59
+
60
+ .apt-btn:hover,
61
+ .mobile-btn:hover,
62
+ .form-content button[type=submit]:hover {
63
+ background: #fbbf24 !important;
64
+ color: #1f2937 !important;
65
+ }
66
+
67
+ .form-group input,
68
+ .form-group select,
69
+ .form-group textarea {
70
+ background: #23272f !important;
71
+ color: #f3f4f6 !important;
72
+ border: 1.5px solid #6b7280 !important;
73
+ border-radius: 0.5rem !important;
74
+ font-size: 1.05rem;
75
+ }
76
+
77
+ .form-group input::placeholder,
78
+ .form-group textarea::placeholder {
79
+ color: #9ca3af !important;
80
+ }
81
+
82
+ .form-group label {
83
+ color: #fbbf24 !important;
84
  font-weight: 600;
 
 
 
 
 
85
  }
86
 
87
+ .form-group {
88
+ border-radius: 0.5rem !important;
89
+ }
90
+
91
+ .form-content {
92
+ border-radius: 0.5rem !important;
93
  }
94
 
95
  .form-group.open .form-content {
96
+ border-radius: 0.5rem !important;
97
  }
98
 
99
+ .text-accent-red {
100
+ color: #e11d48 !important;
101
  }
102
 
103
+ .text-accent-yellow {
104
+ color: #fbbf24 !important;
105
+ }
106
+
107
+ .bg-accent-red {
108
+ background: #e11d48 !important;
109
+ }
110
+
111
+ .bg-accent-yellow {
112
+ background: #fbbf24 !important;
113
+ }
114
+
115
+ .text-automotive-50 {
116
+ color: #f3f4f6 !important;
117
+ }
118
+
119
+ .text-automotive-100 {
120
+ color: #e5e7eb !important;
121
+ }
122
+
123
+ .text-automotive-200 {
124
+ color: #d1d5db !important;
125
+ }
126
+
127
+ .bg-automotive-700 {
128
+ background: #111827 !important;
129
+ }
130
+
131
+ .bg-automotive-600 {
132
+ background: #1f2937 !important;
133
+ }
134
+
135
+ .border-accent-red {
136
+ border-color: #e11d48 !important;
137
+ }
138
+
139
+ .shadow-xl {
140
+ box-shadow: 0 4px 32px 0 #0004, 0 1.5px 6px 0 #e11d48 !important;
141
  }
142
  </style>
143
  </head>
 
198
  </div>
199
  <!-- Step 4: Output Recommendation -->
200
  <div id="step4" class="mb-4 hidden">
201
+ <form id="apt-form" action="https://formspree.io/f/mjkaolga" method="POST"
202
+ class="bg-green-900/80 p-6 rounded-xl shadow-lg space-y-2 mobile-form overflow-visible">
203
+ <h1 class="text-2xl font-bold text-green-200 mb-2 mobile-title">Request a Quote</h1>
204
+ <p class="text-green-100 mb-2">Get a fast, friendly quote! Tell us about your project and how we
205
+ can help you.</p>
206
+ <div class="form-group open">
207
+ <div class="form-toggle" onclick="toggleGroup(this)">Contact Info <span
208
+ class="arrow">▶</span></div>
209
+ <div class="form-content">
210
+ <label for="user-name" class="block text-green-100 font-semibold mb-1">Name <span
211
+ aria-hidden="true" class="text-red-400">*</span></label>
212
+ <input type="text" id="user-name" name="name" required autocomplete="name"
213
+ aria-required="true"
214
+ class="w-full px-4 py-2 rounded-lg bg-white/10 border border-green-700 text-white placeholder-green-200 focus:ring-2 focus:ring-green-400 focus:border-transparent transition mb-2"
215
+ placeholder="Your Name">
216
+ <label for="user-email" class="block text-green-100 font-semibold mb-1">Email <span
217
+ aria-hidden="true" class="text-red-400">*</span></label>
218
+ <input type="email" id="user-email" name="email" required autocomplete="email"
219
+ aria-required="true"
220
+ class="w-full px-4 py-2 rounded-lg bg-white/10 border border-green-700 text-white placeholder-green-200 focus:ring-2 focus:ring-green-400 focus:border-transparent transition mb-2"
221
+ placeholder="you@email.com">
222
+ <label for="user-phone" class="block text-green-100 font-semibold mb-1">Phone</label>
223
+ <input type="tel" id="user-phone" name="phone" autocomplete="tel"
224
+ class="w-full px-4 py-2 rounded-lg bg-white/10 border border-green-700 text-white placeholder-green-200 focus:ring-2 focus:ring-green-400 focus:border-transparent transition"
225
+ placeholder="(optional)">
226
+ </div>
227
+ </div>
228
+ <div class="form-group">
229
+ <div class="form-toggle" onclick="toggleGroup(this)">Service & Price <span
230
+ class="arrow">▶</span></div>
231
+ <div class="form-content">
232
+ <label for="form-service" class="block text-green-100 font-semibold mb-1">Service
233
+ Requested <span aria-hidden="true" class="text-red-400">*</span></label>
234
+ <input type="text" id="form-service" name="service" required
235
+ class="w-full px-4 py-2 rounded-lg bg-white/10 border border-green-700 text-white placeholder-green-200 focus:ring-2 focus:ring-green-400 focus:border-transparent transition mb-2"
236
+ placeholder="Service">
237
+ <label for="form-price" class="block text-green-100 font-semibold mb-1">Price</label>
238
+ <input type="text" id="form-price" name="price"
239
+ class="w-full px-4 py-2 rounded-lg bg-white/10 border border-green-700 text-white placeholder-green-200 focus:ring-2 focus:ring-green-400 focus:border-transparent transition mb-2"
240
+ placeholder="Price">
241
+ </div>
242
+ </div>
243
+ <div class="form-group">
244
+ <div class="form-toggle" onclick="toggleGroup(this)">Project Details <span
245
+ class="arrow">▶</span></div>
246
+ <div class="form-content">
247
+ <label for="form-vehicle" class="block text-green-100 font-semibold mb-1">Vehicle
248
+ Make/Model/Year</label>
249
+ <input type="text" id="form-vehicle" name="vehicle"
250
+ class="w-full px-4 py-2 rounded-lg bg-white/10 border border-green-700 text-white placeholder-green-200 focus:ring-2 focus:ring-green-400 focus:border-transparent transition mb-2"
251
+ placeholder="e.g. 2020 Ford F-150">
252
+ <label for="form-mileage"
253
+ class="block text-green-100 font-semibold mb-1">Mileage</label>
254
+ <input type="text" id="form-mileage" name="mileage"
255
+ class="w-full px-4 py-2 rounded-lg bg-white/10 border border-green-700 text-white placeholder-green-200 focus:ring-2 focus:ring-green-400 focus:border-transparent transition mb-2"
256
+ placeholder="e.g. 42,000">
257
+ <label for="form-details" class="block text-green-100 font-semibold mb-1">Problem
258
+ Description</label>
259
+ <textarea id="form-details" name="details" rows="3"
260
+ class="w-full px-4 py-2 rounded-lg bg-white/10 border border-green-700 text-white placeholder-green-200 focus:ring-2 focus:ring-green-400 focus:border-transparent transition"
261
+ placeholder="Describe the issue, symptoms, or service needed..."></textarea>
262
+ </div>
263
+ </div>
264
+ <div class="form-group open">
265
+ <div class="form-content">
266
+ <button type="submit"
267
+ class="w-full bg-green-600 hover:bg-green-500 text-white font-bold py-3 rounded-lg transition-colors focus:outline-none focus:ring-2 focus:ring-amber-400 focus:ring-offset-2 mobile-btn">Send
268
+ Request</button>
269
+ </div>
270
+ </div>
271
+ <div id="apt-success" class="hidden text-green-200 text-center font-semibold mt-2">Request sent!
272
+ Hooper Automotive will contact you soon.<br>Email: <a
273
+ href="mailto:hooperautorepair70@gmail.com"
274
+ class="underline">hooperautorepair70@gmail.com</a><br>Phone: (715) 421-9792</div>
275
+ </form>
276
  </div>
277
  </div>
278
  </section>
 
283
  ...existing code...
284
  -->
285
  <script>
286
+ // --- Llama AI Chat Only ---
287
+ (function () {
288
+ const messagesDiv = document.getElementById('llama-messages');
289
+ const userInput = document.getElementById('llama-user-input');
290
+ const sendBtn = document.getElementById('llama-send-btn');
291
+ const API_URL = "https://llama-universal-netlify-project.netlify.app/.netlify/functions/llama-proxy?path=/chat/completions";
292
+ function appendMessage(text, sender) {
293
+ const div = document.createElement('div');
294
+ div.className = 'msg ' + sender;
295
+ // Support markdown and preserve line breaks for AI output
296
+ if (sender === 'ai') {
297
+ // If text is markdown, render as HTML (basic)
298
+ div.innerHTML = text.replace(/\n/g, '<br>');
299
+ } else {
300
+ div.textContent = (sender === 'user' ? 'You: ' : 'AI: ') + text;
 
 
 
301
  }
302
+ messagesDiv.appendChild(div);
303
+ messagesDiv.scrollTop = messagesDiv.scrollHeight;
304
+ }
305
+ async function sendMessage() {
306
+ const text = userInput.value.trim();
307
+ if (!text) return;
308
+ appendMessage(text, 'user');
309
+ userInput.value = '';
310
+ sendBtn.disabled = true;
311
+ try {
312
+ const body = {
313
+ model: "Llama-3.3-8B-Instruct",
314
+ messages: [
315
+ { role: 'system', content: "You are a helpful, friendly Hooper Automotive service phone representative. Greet the customer, ask clarifying questions, and provide expert auto advice." },
316
+ { role: 'user', content: text }
317
+ ]
318
+ };
319
+ const res = await fetch(API_URL, {
320
+ method: 'POST',
321
+ headers: { 'Content-Type': 'application/json' },
322
+ body: JSON.stringify(body)
323
+ });
324
+ let data, aiMsg;
325
  try {
326
+ data = await res.json();
327
+ } catch (jsonErr) {
328
+ appendMessage('Non-JSON response: ' + await res.text(), 'ai');
329
+ return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
330
  }
331
+ // Industry-standard: handle OpenAI and HF-style, fallback to completion_message.content.text
332
+ if (data.choices?.[0]?.message?.content) {
333
+ aiMsg = data.choices[0].message.content;
334
+ } else if (data.completion_message?.content?.text) {
335
+ aiMsg = data.completion_message.content.text;
336
+ } else if (data.completion_message?.content) {
337
+ aiMsg = data.completion_message.content;
338
+ } else if (data.error) {
339
+ aiMsg = 'API error: ' + (data.error.message || JSON.stringify(data.error));
340
+ } else {
341
+ aiMsg = 'Raw response: ' + JSON.stringify(data);
342
+ }
343
+ appendMessage(aiMsg, 'ai');
344
+ } catch (e) {
345
+ appendMessage('Error: ' + e.message, 'ai');
346
+ } finally {
347
+ sendBtn.disabled = false;
348
  }
349
+ }
350
+ sendBtn.onclick = sendMessage;
351
+ userInput.addEventListener('keydown', e => {
352
+ if (e.key === 'Enter') sendMessage();
353
+ });
354
+ })();
355
  // --- APT Step-by-Step Calculator ---
356
  // Variables: x1=category, x2=symptom, r1=service/price, x3=vehicle info, y1=output
357
  // Pipeline: y1 = f(r1(x1, x2), x3)
 
440
  // Output recommendation
441
  document.getElementById('apt-recommend').textContent = `${selectedService} (${selectedSymptom})`;
442
  document.getElementById('apt-price').textContent = selectedPrice;
443
+ // Autofill form fields for Formspree
444
+ document.getElementById('form-service').value = selectedService || '';
445
+ document.getElementById('form-price').value = selectedPrice || '';
446
+ document.getElementById('form-vehicle').value = document.getElementById('vehicle')?.value || '';
447
+ document.getElementById('form-mileage').value = document.getElementById('mileage')?.value || '';
448
  document.getElementById('step4').classList.remove('hidden');
449
  };
450
 
451
+ // Collapsible form groups (BuckRub style)
452
+ function toggleGroup(el) {
453
+ const group = el.parentElement;
454
+ group.classList.toggle('open');
455
+ }
456
+ // Show success message after submit
457
+ const aptForm = document.getElementById('apt-form');
458
+ if (aptForm) {
459
+ aptForm.addEventListener('submit', function (e) {
460
+ setTimeout(() => {
461
+ document.getElementById('apt-success').classList.remove('hidden');
462
+ }, 500);
463
+ });
464
+ }
465
 
466
  // Style for APT buttons
467
  const style = document.createElement('style');