WF Chat Widget — Техническа документация
Управляван от JSON конфигурация разговорен уиджет с фиксирано позициониране. Потребителят преминава през структурирани стъпки — избор на опции, въвеждане на контакти, разглеждане на имоти и статии — без сървърен код.
Инсталация
Включете chat.css в <head>. Поставете HTML структурата на уиджета в края на <body>, след което заредете chat.js и конфигурационния JSON скрипт. Уиджетът не изпраща данни автоматично — обработката на събраните отговори е
отговорност на интегриращия код.
<link rel="stylesheet" href="./assets/css/chat.css" />
<div class="wf_chat_widget" data-show-after-scroll="true" ...>
<button class="wf_chat_widget__launcher" aria-label="Отвори чат">
<span class="wf_chat_widget__launcher-face">
<img class="wf_chat_widget__launcher-img" src="..." alt="" />
</span>
<span class="wf_chat_widget__launcher-dot"></span>
</button>
<div class="wf_chat_widget__window" role="dialog" aria-hidden="true">
<div class="wf_chat_widget__header-btns">
<button class="wf_chat_widget__btn-reset" ...>...</button>
<button class="wf_chat_widget__btn-close" ...>...</button>
</div>
<div class="wf_chat_widget__messages"></div>
<div class="wf_chat_widget__input-area"></div>
</div>
</div>
<script src="./assets/js/chat.js"></script>
<script type="application/json" id="wf-chat-config">
{ ... }
</script>
Атрибути на контейнера
- data-show-after-scroll="true" — уиджетът се появява едва след превъртане. Прагът в пиксели се задава с
data-show-after-scroll-offset(по подразбиране 120). - data-hide-at-footer="true" — уиджетът се скрива когато футърът навлезе в изгледа. Селекторът на футъра се задава с
data-hide-at-footer-selector(по подразбиране"footer"). - wf_chat_widget--attention — добавя ритмична анимация на лаунчъра за привличане на внимание.
- wf_chat_widget--toast-mode — режим за кратко известяване: центрира съобщенията и скрива зоната за въвеждане. На мобилни прозорецът остава плаващ вместо да заема цял екран.
JSON конфигурация — основни полета
- brand — наименование, използвано в ARIA атрибута на прозореца.
- avatar.src / avatar.initials — снимка или инициали за аватара на бота.
srcима приоритет. -
colors — обект с CSS custom properties:
botBubbleBg,botBubbleText,userBubbleBg,inputBorder,mutedText,errorColor,activeSend. - ui — речник с надписи на потребителския интерфейс.
- data — данни за апартаменти, статии и локация.
- script — масив от стъпки, изпълнявани последователно.
Секция ui
Всички видими надписи се задават тук, за да работи уиджетът на всеки език:
confirmLabel— бутонът за потвърждение при multi-select.searchPlaceholder— placeholder на търсачките.selectPlaceholder— placeholder на затворения searchselect компонент.skipLabel— надпис на бутона „Пропуснете".invalidValue— съобщение при невалидна стойност.sendLabel— ARIA надпис на бутона за изпращане.removeLabel— ARIA надпис на бутона за премахване на чип в searchselect.phoneSearchPlaceholder— placeholder на търсачката в падащото меню за телефонни кодове.viewApartment— CTA текст в картата на апартамент.readArticle— CTA текст в картата на статия.noApartments— съобщение при липса на резултати от филтриране.noArticles— съобщение при липса на статии.contactsTitle— заглавие на картата с директни контакти.rentType/rentUnit— текст и единица за наем (напр."Наем"/"€/мес").rentTypeсе сравнява сapartment.typeпри рендериране.saleUnit— единица за продажна цена (напр."€").roomsUnit/areaUnit— единици за брой стаи и площ.allTopic/generalCategory— при избор наallTopicили при статии сgeneralCategoryкатегория, статиите се показват без филтриране.
Типове стъпки
Полето script е масив от обекти, всеки задължително с поле type. Изпълнението е последователно — всяка стъпка стартира следващата след действие на потребителя или след изтичане на автоматичното забавяне.
bot
Показва съобщение от бота с анимирани точки. Забавянето се изчислява пропорционално на дължината на текста (минимум 600 мс, максимум 3200 мс). Последователни bot стъпки се показват наредени, без повтарящ се аватар.
{ "type": "bot", "message": "Трудно Ви е да намерите точния имот?" }
user-choice
Хоризонтален ред с бутони-хапки, рендериран директно под последното съобщение на бота. При натискане надписът се показва като потребителско съобщение и разговорът продължава. Опциите могат да са низове или обекти с label — всички допълнителни полета (напр. min,
max) се записват в сесията под <collect>Meta и са достъпни за стъпките render-*.
{
"type": "user-choice",
"collect": "budget",
"choices": [
{ "label": "До 100 000 €", "max": 100000 },
{ "label": "100–200 000 €", "min": 100000, "max": 200000 },
{ "label": "Над 200 000 €", "min": 200000 }
]
}
user-multi
Множествен избор с хапки и бутон „Потвърди". Бутонът е деактивиран докато не е избрана поне една стойност. Избраните стойности се записват в сесията като масив от низове.
{
"type": "user-multi",
"collect": "features",
"choices": ["Паркинг", "Асансьор", "Двор"],
"confirmLabel": "Потвърди"
}
user-radio
Мрежа от карти (2 колони) с незадължителна SVG икона. При избор картата се маркира визуално, отговорът се записва и разговорът продължава.
{
"type": "user-radio",
"collect": "deal",
"options": [
{ "label": "Покупка", "icon": "<svg viewBox='0 0 24 24' ...>...</svg>" },
{ "label": "Наем", "icon": "<svg viewBox='0 0 24 24' ...>...</svg>" }
]
}
user-searchselect
Падащо меню с вградена търсачка и мулти-избор чрез чипове. Подходящо за дълги списъци като градски райони. При отваряне фокусът пада върху търсачката. Избраните стойности са чипове с бутон за индивидуално премахване.
{
"type": "user-searchselect",
"collect": "districts",
"placeholder": "Изберете район(и)...",
"searchPlaceholder": "Търси район...",
"choices": ["Лозенец", "Младост", "Витоша", "Център"]
}
user-input
Текстово поле с валидация. inputType може да е "text", "email" или "tel". При "tel" се зарежда lazy intl-tel-input с търсачка за държавен код; initialCountry задава предизбраната държава (по
подразбиране "bg"). Грешката се показва с анимация при неуспешен опит за изпращане. При skippable: true в списъка се появява бутон за пропускане — стойността в сесията се записва като null.
{
"type": "user-input",
"inputType": "tel",
"placeholder": "0888 123 456",
"validate": "phone",
"collect": "phone",
"initialCountry": "bg",
"skippable": true,
"skipLabel": "Пропуснете",
"skip_text": "Предпочитам да не споделям телефон",
"errorMessage": "Моля, въведете валиден телефонен номер."
}
user-nav
Мрежа от навигационни бутони (до 3 на ред с икони), показвана след приключване на основния поток. Всеки бутон стартира собствен под-скрипт чрез полето script. Блокът се запомня вътрешно, за да може repeat-nav да го покаже отново след всяко взаимодействие.
{
"type": "user-nav",
"items": [
{
"label": "Апартаменти",
"icon": "<svg ...>...</svg>",
"script": [
{ "type": "bot", "message": "Колко стаи търсите?" },
{ "type": "user-choice", "collect": "rooms", "choices": ["1 стая", "2 стаи", "3 стаи", "4+ стаи"] },
{ "type": "render-apartments" },
{ "type": "repeat-nav", "message": "Мога ли да помогна с нещо друго?" }
]
}
]
}
render-apartments
Рендерира карти за апартаменти от data.apartments, филтрирани по текущата сесия. Активни филтри: deal (тип сделка), districts (масив с райони), budgetMeta.min / budgetMeta.max (ценови диапазон от предишен
user-choice), rooms (брой стаи; суфикс + означава „и повече"). Всеки филтър се прилага само ако би дал резултати — при пълно изключване се пропуска. При крайна липса на резултати се показва ui.noApartments.
render-articles
Рендерира карти за статии от data.articles, филтрирани по session.topic. Статии с category равна на ui.generalCategory се показват при всяка тема. При избор на ui.allTopic се показват всички статии без филтриране. При крайна
липса на резултати се показва ui.noArticles.
render-location
Рендерира карта (OpenStreetMap чрез Leaflet, зарежда се lazy при първо използване) с персонализиран SVG маркер и детайли от data.location: адрес, телефон (като tel: линк) и работно време.
contacts-outcome
Проверява дали и session.email, и session.phone са null (двата пропуснати). При пропуснати и двата контакта показва skippedMessage и рендерира карта с директни контакти (телефон, имейл, часове от data.location). При наличие
на поне един контакт показва successMessage.
{
"type": "contacts-outcome",
"successMessage": "Благодарим! Ще се свържем с Вас до края на деня.",
"skippedMessage": "Няма проблем! Свържете се с нас директно:"
}
repeat-nav
Повторно показва последния user-nav блок. При зададено message се показва бот съобщение преди навигацията. Преди рендерирането rooms, topic и timeline се изтриват от сесията, за да могат под-скриптовете да работят отначало.
{ "type": "repeat-nav", "message": "Мога ли да помогна с нещо друго?" }
Динамични избори
Когато наличните опции зависят от предишен отговор, задайте choices (или options) като обект вместо масив и добавете choicesBy (или optionsBy) с ключа на сесийната променлива. Ключовете са възможните стойности; "default" се
използва при липса на съвпадение.
{
"type": "user-choice",
"collect": "timeline",
"choicesBy": "deal",
"choices": {
"Покупка": ["До 6 месеца", "До 1 година", "Гъвкаво"],
"Наем": ["Веднага", "До 3 месеца", "Гъвкаво"],
"default": ["Гъвкаво"]
}
}
Секция data
data.apartments — масив от обекти с полета: title, district (низ, съвпадащ с елементите в user-searchselect), price (число), type (трябва да съвпада с ui.rentType за наемни имоти),
rooms (число), sqm (число), badge (низ или null), img, url.
data.articles — масив с title, category, excerpt, img, url.
data.location — обект с lat, lng, address, phone, email, hours и незадължителен pin обект (color, dotColor, size) за персонализация на маркера.
Сесия и събиране на данни
Вътрешният обект session се попълва от стъпките с поле collect. Низовите стойности се записват директно; при обектни опции допълнителните метаданни (напр. min/max) се записват под <ключ>Meta. При пропускане стойността е
null. Стъпките render-* и contacts-outcome четат от сесията при рендериране. Натискането на бутона за нулиране изчиства сесията и рестартира разговора от началото.
Изпращане на данни
При достигане на финална стъпка уиджетът извиква вътрешната функция submitSession(). Тя винаги записва събраните данни в конзолата и опционално ги изпраща към сървър.
console.log
При всяко завършване в конзолата на браузъра се появява запис с всички събрани стойности:
[wf-chat] session: {
deal: "Покупка",
budget: "100–200 000 €",
budgetMeta: { min: 100000, max: 200000 },
districts: ["Лозенец", "Младост"],
rooms: "3 стаи",
email: "ivan@example.com",
phone: null
}
cfg.action
Добавете поле "action" в конфигурацията за автоматичен POST:
{
"brand": "Имот Асистент",
"action": "https://your-endpoint.com/leads",
...
}
Уиджетът изпраща POST заявка с Content-Type: application/json и тяло — копие на session в момента на завършване. Заявката е fire-and-forget: грешките не се показват на потребителя.
Кога се извиква
- contacts-outcome — при достигане на стъпката, независимо дали потребителят е оставил контакт или е пропуснал и двете полета. Сесията съдържа и другите събрани данни (
deal,districts,budgetи т.н.). - success-toast — само когато поне един контакт (имейл или телефон) е предоставен. При пропускане и на двете полетата, стъпката не рендерира тоста и
submitSessionне се извиква.