<script>
/**
* @file LiveChat Widget — GTM Listener
* @description Listens to LiveChat Widget JS API callbacks and pushes events
* to the dataLayer for Google Tag Manager consumption.
* Tracks: chat initiated, chat ended, form submissions (pre-chat,
* offline/ticket, post-chat survey), and chat ratings.
*
* @see {@link https://platform.text.com/docs/extending-chat-widget/javascript-api} LiveChat Widget JS API
*
* @author Praba Ponnambalam (praba@measureschool.com)
* @date 2026-03-03
*/
(function () {
// ─── Guard ─────────────────────────────────────────────────────────
if (typeof LiveChatWidget === 'undefined' || typeof window.dataLayer === 'undefined') {
console.warn('[LiveChat GTM] LiveChatWidget or dataLayer not found.');
return;
}
var chatInitiated = false;
var previousCustomerStatus = null;
// ─── 1. Chat Initiated ─────────────────────────────────────────────
// Fires on the first message sent by the customer (not a greeting).
// author.type === 'customer' + no greeting = genuine customer message.
// chatInitiated flag ensures this fires only once per page load.
LiveChatWidget.on('new_event', function (data) {
if (chatInitiated) return;
if (data.author.type !== 'customer') return;
if (data.greeting) return;
chatInitiated = true;
var payload = {
event: 'livechat_chat_initiated',
livechat_message_type: data.type, // 'message' | 'rich_message' | 'file'
};
window.dataLayer.push(payload);
});
// ─── 2. Chat Ended ─────────────────────────────────────────────────
// No dedicated API event — best proxy is the 'chatting' → 'browsing'
// status transition, which fires when the customer ends the chat
// (before post-chat survey, if enabled).
LiveChatWidget.on('customer_status_changed', function (data) {
if (previousCustomerStatus === 'chatting' && data.status === 'browsing') {
var payload = { event: 'livechat_chat_ended' };
window.dataLayer.push(payload);
}
previousCustomerStatus = data.status;
});
// ─── 3. Form Submitted ─────────────────────────────────────────────
// Split into three distinct events so each surfaces separately in GA4.
//
// prechat → livechat_prechat_submitted (agents online)
// ticket → livechat_offline_submitted (agents offline, same form)
// postchat → livechat_survey_submitted
var FORM_EVENT_NAMES = {
prechat: 'livechat_prechat_submitted',
ticket: 'livechat_offline_submitted',
postchat: 'livechat_survey_submitted',
};
LiveChatWidget.on('form_submitted', function (data) {
var payload = {
event: FORM_EVENT_NAMES[data.type] || 'livechat_form_submitted',
};
window.dataLayer.push(payload);
});
// ─── 4. Rating Submitted ───────────────────────────────────────────
// Note: value is passed as a bare string, not wrapped in an object.
// 'none' = customer removed a previously submitted rating.
var RATING_LABELS = {
good: 'Thumbs Up',
bad: 'Thumbs Down',
none: 'Rating Removed',
};
LiveChatWidget.on('rating_submitted', function (value) {
var payload = {
event: 'livechat_rating_submitted',
livechat_rating_value: RATING_LABELS[value] || value,
};
window.dataLayer.push(payload);
});
})();
</script>