Upload index.html
Browse files- index.html +20 -20
index.html
CHANGED
|
@@ -34,7 +34,7 @@
|
|
| 34 |
</script>
|
| 35 |
|
| 36 |
<style>
|
| 37 |
-
/* ββ
|
| 38 |
.frm-wrapper {
|
| 39 |
font-family: 'Inter', system-ui, sans-serif;
|
| 40 |
font-size: 0.875rem;
|
|
@@ -295,9 +295,9 @@
|
|
| 295 |
<!-- ββ Marked.js βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ -->
|
| 296 |
<script src="https://cdn.jsdelivr.net/npm/marked@9/marked.min.js"></script>
|
| 297 |
|
| 298 |
-
<!-- ββ
|
| 299 |
<script>
|
| 300 |
-
const
|
| 301 |
|
| 302 |
class Rule {
|
| 303 |
static required(msg = 'This field is required') { return { type: 'required', message: msg }; }
|
|
@@ -392,10 +392,10 @@ function renderField(f) {
|
|
| 392 |
function renderForm(form) {
|
| 393 |
if (form._multiStep && form._steps.length > 0) return renderMultiStep(form);
|
| 394 |
const id = form._id;
|
| 395 |
-
return `<div class="frm-wrapper" data-
|
| 396 |
${form._title?`<div class="frm-title">${_fEsc(form._title)}</div>`:''}
|
| 397 |
${form._description?`<div class="frm-desc">${_fEsc(form._description)}</div>`:''}
|
| 398 |
-
<form class="frm-form" data-
|
| 399 |
${form._fields.map(renderField).join('\n')}
|
| 400 |
<div class="frm-actions"><button type="submit" class="frm-btn-submit">${_fEsc(form._submitLabel)}</button></div>
|
| 401 |
<div class="frm-success" style="display:none">${_fEsc(form._successMsg)}</div>
|
|
@@ -411,11 +411,11 @@ function renderMultiStep(form) {
|
|
| 411 |
${step.title?`<div class="frm-step-title">${_fEsc(step.title)}</div>`:''}
|
| 412 |
${step.fields.map(renderField).join('\n')}
|
| 413 |
</div>`).join('\n');
|
| 414 |
-
return `<div class="frm-wrapper" data-
|
| 415 |
${form._title?`<div class="frm-title">${_fEsc(form._title)}</div>`:''}
|
| 416 |
<div class="frm-progress-label">Step 1 of ${total}</div>
|
| 417 |
<div class="frm-progress-bar"><div class="frm-progress-fill" id="${_fEsc(id)}-progress" style="width:${Math.round(100/total)}%"></div></div>
|
| 418 |
-
<form class="frm-form" data-
|
| 419 |
${stepsHtml}
|
| 420 |
<div class="frm-nav">
|
| 421 |
<button type="button" class="frm-btn-back" style="display:none">Back</button>
|
|
@@ -428,14 +428,14 @@ function renderMultiStep(form) {
|
|
| 428 |
}
|
| 429 |
|
| 430 |
function attachForms(container) {
|
| 431 |
-
container.querySelectorAll('form[data-
|
| 432 |
_attachConditions(form); _attachMultiStep(form);
|
| 433 |
form.addEventListener('submit', e => {
|
| 434 |
e.preventDefault();
|
| 435 |
if (!_validate(form)) return;
|
| 436 |
const result = _collect(form);
|
| 437 |
_showSuccess(form);
|
| 438 |
-
window.dispatchEvent(new CustomEvent('
|
| 439 |
});
|
| 440 |
});
|
| 441 |
}
|
|
@@ -468,7 +468,7 @@ function _attachMultiStep(form) {
|
|
| 468 |
const stepEls = form.querySelectorAll('.frm-step');
|
| 469 |
if (!stepEls.length) return;
|
| 470 |
const total = stepEls.length; let current = 0;
|
| 471 |
-
const formId = form.getAttribute('data-
|
| 472 |
const progress = document.getElementById(`${formId}-progress`);
|
| 473 |
const label = form.closest('.frm-wrapper')?.querySelector('.frm-progress-label');
|
| 474 |
const btnBack = form.querySelector('.frm-btn-back');
|
|
@@ -517,7 +517,7 @@ function _clearError(el) {
|
|
| 517 |
el.parentNode?.querySelectorAll('.frm-error-msg').forEach(e=>e.remove());
|
| 518 |
}
|
| 519 |
function _collect(form) {
|
| 520 |
-
const formId=form.getAttribute('data-
|
| 521 |
const data={}, typed={};
|
| 522 |
form.querySelectorAll('input,select,textarea').forEach(el => {
|
| 523 |
if (!el.name) return;
|
|
@@ -536,13 +536,13 @@ function _showSuccess(form) {
|
|
| 536 |
|
| 537 |
class FormResult {
|
| 538 |
constructor(formId,data,typedData) { this.formId=formId; this.data=data; this.typedData=typedData; }
|
| 539 |
-
toMessage() { return
|
| 540 |
asText() { return Object.entries(this.typedData).filter(([,v])=>v!==''&&v!==false).map(([k,v])=>`${k.replace(/_/g,' ')}: ${v}`).join(', '); }
|
| 541 |
}
|
| 542 |
-
function
|
| 543 |
function parseFormoraMessage(msg) {
|
| 544 |
-
if (!
|
| 545 |
-
try { const p=JSON.parse(msg.slice(
|
| 546 |
}
|
| 547 |
</script>
|
| 548 |
|
|
@@ -799,20 +799,20 @@ function injectForm(formHtml, intent) {
|
|
| 799 |
const bubble = addBotRow('');
|
| 800 |
bubble.style.padding = '12px';
|
| 801 |
bubble.innerHTML = formHtml;
|
| 802 |
-
const formEl = bubble.querySelector('form[data-
|
| 803 |
-
const formId = formEl?.getAttribute('data-
|
| 804 |
attachForms(bubble);
|
| 805 |
-
function
|
| 806 |
const { parsed } = e.detail;
|
| 807 |
if (parsed.formId !== formId) return;
|
| 808 |
-
window.removeEventListener('
|
| 809 |
const requestNumber = nextTicket();
|
| 810 |
const pill = document.createElement('div');
|
| 811 |
pill.className = 'inc-pill'; pill.textContent = `β ${requestNumber}`;
|
| 812 |
bubble.insertBefore(pill, bubble.firstChild);
|
| 813 |
handleFormSubmit(parsed, requestNumber, intent);
|
| 814 |
}
|
| 815 |
-
window.addEventListener('
|
| 816 |
scrollBottom();
|
| 817 |
return bubble;
|
| 818 |
}
|
|
|
|
| 34 |
</script>
|
| 35 |
|
| 36 |
<style>
|
| 37 |
+
/* ββ barq-chat-form.css ββ */
|
| 38 |
.frm-wrapper {
|
| 39 |
font-family: 'Inter', system-ui, sans-serif;
|
| 40 |
font-size: 0.875rem;
|
|
|
|
| 295 |
<!-- ββ Marked.js βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ -->
|
| 296 |
<script src="https://cdn.jsdelivr.net/npm/marked@9/marked.min.js"></script>
|
| 297 |
|
| 298 |
+
<!-- ββ barq-chat-form.js (inlined) ββββββββββββββββββββββββββββββββββββββββββββββββ -->
|
| 299 |
<script>
|
| 300 |
+
const BARQ_PREFIX = '__barq__';
|
| 301 |
|
| 302 |
class Rule {
|
| 303 |
static required(msg = 'This field is required') { return { type: 'required', message: msg }; }
|
|
|
|
| 392 |
function renderForm(form) {
|
| 393 |
if (form._multiStep && form._steps.length > 0) return renderMultiStep(form);
|
| 394 |
const id = form._id;
|
| 395 |
+
return `<div class="frm-wrapper" data-barq-id="${_fEsc(id)}">
|
| 396 |
${form._title?`<div class="frm-title">${_fEsc(form._title)}</div>`:''}
|
| 397 |
${form._description?`<div class="frm-desc">${_fEsc(form._description)}</div>`:''}
|
| 398 |
+
<form class="frm-form" data-barq-id="${_fEsc(id)}" novalidate>
|
| 399 |
${form._fields.map(renderField).join('\n')}
|
| 400 |
<div class="frm-actions"><button type="submit" class="frm-btn-submit">${_fEsc(form._submitLabel)}</button></div>
|
| 401 |
<div class="frm-success" style="display:none">${_fEsc(form._successMsg)}</div>
|
|
|
|
| 411 |
${step.title?`<div class="frm-step-title">${_fEsc(step.title)}</div>`:''}
|
| 412 |
${step.fields.map(renderField).join('\n')}
|
| 413 |
</div>`).join('\n');
|
| 414 |
+
return `<div class="frm-wrapper" data-barq-id="${_fEsc(id)}">
|
| 415 |
${form._title?`<div class="frm-title">${_fEsc(form._title)}</div>`:''}
|
| 416 |
<div class="frm-progress-label">Step 1 of ${total}</div>
|
| 417 |
<div class="frm-progress-bar"><div class="frm-progress-fill" id="${_fEsc(id)}-progress" style="width:${Math.round(100/total)}%"></div></div>
|
| 418 |
+
<form class="frm-form" data-barq-id="${_fEsc(id)}" novalidate>
|
| 419 |
${stepsHtml}
|
| 420 |
<div class="frm-nav">
|
| 421 |
<button type="button" class="frm-btn-back" style="display:none">Back</button>
|
|
|
|
| 428 |
}
|
| 429 |
|
| 430 |
function attachForms(container) {
|
| 431 |
+
container.querySelectorAll('form[data-barq-id]').forEach(form => {
|
| 432 |
_attachConditions(form); _attachMultiStep(form);
|
| 433 |
form.addEventListener('submit', e => {
|
| 434 |
e.preventDefault();
|
| 435 |
if (!_validate(form)) return;
|
| 436 |
const result = _collect(form);
|
| 437 |
_showSuccess(form);
|
| 438 |
+
window.dispatchEvent(new CustomEvent('barq:submit', { detail:{ raw:result.toMessage(), parsed:result } }));
|
| 439 |
});
|
| 440 |
});
|
| 441 |
}
|
|
|
|
| 468 |
const stepEls = form.querySelectorAll('.frm-step');
|
| 469 |
if (!stepEls.length) return;
|
| 470 |
const total = stepEls.length; let current = 0;
|
| 471 |
+
const formId = form.getAttribute('data-barq-id') || form.closest('[data-barq-id]')?.getAttribute('data-barq-id') || '';
|
| 472 |
const progress = document.getElementById(`${formId}-progress`);
|
| 473 |
const label = form.closest('.frm-wrapper')?.querySelector('.frm-progress-label');
|
| 474 |
const btnBack = form.querySelector('.frm-btn-back');
|
|
|
|
| 517 |
el.parentNode?.querySelectorAll('.frm-error-msg').forEach(e=>e.remove());
|
| 518 |
}
|
| 519 |
function _collect(form) {
|
| 520 |
+
const formId=form.getAttribute('data-barq-id')||form.closest('[data-barq-id]')?.getAttribute('data-barq-id')||'';
|
| 521 |
const data={}, typed={};
|
| 522 |
form.querySelectorAll('input,select,textarea').forEach(el => {
|
| 523 |
if (!el.name) return;
|
|
|
|
| 536 |
|
| 537 |
class FormResult {
|
| 538 |
constructor(formId,data,typedData) { this.formId=formId; this.data=data; this.typedData=typedData; }
|
| 539 |
+
toMessage() { return BARQ_PREFIX+JSON.stringify({ form_id:this.formId, data:this.data, typed_data:this.typedData }); }
|
| 540 |
asText() { return Object.entries(this.typedData).filter(([,v])=>v!==''&&v!==false).map(([k,v])=>`${k.replace(/_/g,' ')}: ${v}`).join(', '); }
|
| 541 |
}
|
| 542 |
+
function isBarqMessage(msg) { return typeof msg==='string'&&msg.startsWith(BARQ_PREFIX); }
|
| 543 |
function parseFormoraMessage(msg) {
|
| 544 |
+
if (!isBarqMessage(msg)) return null;
|
| 545 |
+
try { const p=JSON.parse(msg.slice(BARQ_PREFIX.length)); return new FormResult(p.form_id,p.data,p.typed_data); } catch { return null; }
|
| 546 |
}
|
| 547 |
</script>
|
| 548 |
|
|
|
|
| 799 |
const bubble = addBotRow('');
|
| 800 |
bubble.style.padding = '12px';
|
| 801 |
bubble.innerHTML = formHtml;
|
| 802 |
+
const formEl = bubble.querySelector('form[data-barq-id]');
|
| 803 |
+
const formId = formEl?.getAttribute('data-barq-id');
|
| 804 |
attachForms(bubble);
|
| 805 |
+
function onBarqSubmit(e) {
|
| 806 |
const { parsed } = e.detail;
|
| 807 |
if (parsed.formId !== formId) return;
|
| 808 |
+
window.removeEventListener('barq:submit', onBarqSubmit);
|
| 809 |
const requestNumber = nextTicket();
|
| 810 |
const pill = document.createElement('div');
|
| 811 |
pill.className = 'inc-pill'; pill.textContent = `β ${requestNumber}`;
|
| 812 |
bubble.insertBefore(pill, bubble.firstChild);
|
| 813 |
handleFormSubmit(parsed, requestNumber, intent);
|
| 814 |
}
|
| 815 |
+
window.addEventListener('barq:submit', onBarqSubmit);
|
| 816 |
scrollBottom();
|
| 817 |
return bubble;
|
| 818 |
}
|