Czym jest WebComponent CustomerHero i do czego służy?


WebComponent CustomerHero to zaawansowane, osadzalne narzędzie interfejsu użytkownika (UI), które pozwala na płynne wyświetlanie interaktywnych ankiet bezpośrednio w strukturze Twojej strony internetowej lub aplikacji. Narzędzie to jest ściśle zintegrowane z platformą badawczą Customerhero.com.

Jego głównym celem jest zbieranie cennego feedbacku (np. wskaźnika NPS, ocen satysfakcji z procesu, opinii tekstowych) w kluczowych momentach ścieżki klienta, bez wyrywania go z obecnego kontekstu. Dzięki temu użytkownik nie musi przechodzić na zewnętrzne podstrony, co drastycznie zwiększa wskaźnik wypełnialności ankiet. Narzędzie dba o to, by ankiety pojawiały się we właściwym czasie, odpowiedniej grupie docelowej (dzięki zaawansowanym regułom cappingu) i przesyłały zgromadzone dane w sposób bezpieczny i uporządkowany – zarówno dla użytkowników zalogowanych, jak i anonimowych.


Konfiguracja główna

Bazowe parametry przekazywane do obiektu config:

  • id: Identyfikator elementu HTML (DOM), w którym zostanie wyrenderowana ankieta. Jest to parametr wymagany do umieszczenia ankiety w odpowiednim miejscu na stronie.
  • rootElement: Atrybut rootElement pozwala na określenie elementu DOM, w którym ma być wyrenderowana ankieta. Może być funkcją zwracającą element DOM, bezpośrednim elementem DOM lub selektorem CSS, a gdy nie jest podany, używane jest standardowe zachowanie z document.getElementById(id).
  • surveyId: Identyfikator ankiety utworzonej na platformie Customerhero.com. Ten identyfikator jest uzyskiwany po skopiowaniu ankiety bazowej z przedrostkiem SEQ i jest niezbędny do wywołania API.
  • marketingConsent: Wartość logiczna (true lub false) określająca, czy użytkownik wyraził zgodę marketingową.
  • externalKey: Opcjonalne pole, które może zawierać dodatkową zmienną opisującą kontekst respondenta, na przykład identyfikator kanału.
  • userToken: Opcjonalny token służący do identyfikacji użytkownika.
  • accessToken: Opcjonalny token API, który w starszej wersji konfiguracji był pobierany z window.omni.accessToken.
  • prefillUrl: Opcjonalny adres URL używany do wstępnego wypełnienia ankiety. Jest wykorzystywany w przypadku ankiet dla użytkowników niezalogowanych.
  • validationText: Globalny tekst błędu walidacji dla pytań, które nie mają zdefiniowanego własnego validationText.
  • submitButtonText: Tekst na głównym przycisku wysyłającym ankietę.
  • submitButtonLoadingText: Tekst wyświetlany na przycisku wysyłania po jego kliknięciu, w trakcie przetwarzania.


Zdarzenia

  • onFirstClick: Funkcja wywoływana przy pierwszym kliknięciu dowolnego elementu ankiety przez użytkownika.
  • onSubmit: Funkcja wywoływana po kliknięciu przycisku "Wyślij" i pomyślnej walidacji formularza.
  • onRender: Funkcja wywoływana po pełnym wyrenderowaniu komponentu ankiety w strukturze DOM.
  • onClose: Funkcja wywoływana po zamknięciu ankiety (poprzez kliknięcie przycisku "Zamknij" na stronie z podziękowaniem) i zakończeniu animacji zamykania.
  • onError: Funkcja wywoływana w momencie wystąpienia błędu w webcomponencie (błąd konfiguracji, błąd połączenia).


Obiekt "cookie"

Obiekt ten zarządza ustawieniami pliku cookie używanego przez ankietę.

  • name: Nazwa pliku cookie, w którym przechowywane są dane ankiety.
  • age: Określa czas życia pliku cookie w dniach.


Obiekt "capping"

Służy do zarządzania ograniczeniami wyświetlania ankiety.

  • value: Maksymalna liczba wyświetleń ankiety dla jednego użytkownika (na podstawie pliku cookie). Wartość undefined oznacza brak limitu.
  • pageViewsTrigger: Określa, co ile odsłon strony ankieta ma być wyświetlana. Wartość undefined oznacza brak tego ograniczenia.
  • hideFilled: Wartość logiczna (true lub false). Jeśli ustawiona na true, ankieta nie pokaże się użytkownikowi, który już ją wypełnił.
  • showChance: Określa prawdopodobieństwo (od 0.0 do 1.0) pojawienia się ankiety. Wartość undefined oznacza, że ankieta będzie pokazywana zawsze.


Obiekt "attributes"

Pozwala na przekazanie dodatkowych atrybutów opisujących respondenta. Zastępuje wcześniejszą sekcję labels. Maksymalnie: 15 pól.

  • a1, a2, wiek, lang, ...: Przykładowe atrybuty, które mogą być przekazane, takie jak imię, nazwisko, wiek czy język.


Tablica "questions"

Jest to tablica obiektów, gdzie każdy obiekt definiuje jedno pytanie w ankiecie. Ankieta może zawierać różne typy pytań.


Wspólne Parametry Pytań

Są to wspólne parametry, które można zdefiniować w każdym pytaniu:

  • type: Typ pytania. Dostępne opcje to rate, nps, text, singleChoice.
  • title: Główny tytuł lub treść pytania.
  • description: Dodatkowy opis lub instrukcja do pytania.
  • required: Opcjonalna wartość logiczna. Domyślnie true dla pytań zamkniętych (np. nps) i false dla pytań otwartych (np. text).
  • style: Opcjonalny obiekt definiujący wyrównanie. Dostępne wartości dla align to 'left', 'center', 'right'. Domyślnie jest to 'center' dla pytań NPS i 'left' dla pozostałych.


Pytanie Typu "rate" (Ocena)

  • answers: Tablica ciągów znaków, które reprezentują etykiety dla kolejnych ocen. Przykład: ['tak', 'nie']


Pytanie Typu "nps" (Net Promoter Score)

  • minLabel: Etykieta dla minimalnej wartości skali.
  • maxLabel: Etykieta dla maksymalnej wartości skali.
  • minValue: Minimalna wartość na skali (np. 0).
  • maxValue: Maksymalna wartość na skali (np. 10).


Pytanie Typu "text" (Otwarte)

  • placeholder: Tekst zawierający dodatkowe wskazówki, wyświetlany pod polem tekstowym.
  • maxlength: Maksymalna dozwolona liczba znaków w odpowiedzi.
  • validationLimitExceededText: Domyślnie: 'Przekroczono limit znaków'. Tekst wyświetlany, gdy użytkownik przekroczy maksymalną liczbę znaków i chce wysłać formularz.
  • validationText: Tekst błędu walidacji wyświetlany, gdy odpowiedź jest wymagana, a nie została udzielona. Jeśli nie jest zdefiniowany, używana jest globalna wartość config.validationText.
  • waitForPreviousQuestion: Opcjonalna wartość logiczna. Domyślnie false. Jeśli true, pytanie pojawi się dopiero po udzieleniu odpowiedzi na poprzednie pytanie.
  • counter: Opcjonalna wartość logiczna (true/false), która włącza widoczność licznika znaków dla pola tekstowego. Domyślnie ustawiona jest na false.
    • false (wartość domyślna): Licznik znaków jest ukryty. Pole tekstowe blokuje możliwość wpisania większej liczby znaków, niż określono w maxlength. Przy próbie wklejenia dłuższego tekstu, nadmiarowe znaki są automatycznie usuwane, a użytkownik jest o tym informowany za pomocą alertu.
    • true: Wyświetla licznik znaków. Użytkownik może wpisać więcej tekstu, niż wynosi limit maxlength, ale pole jest walidowane. Przy próbie wklejenia dłuższego tekstu, nadmiarowe znaki są automatycznie usuwane, a użytkownik jest o tym informowany za pomocą alertu.


Pytanie Typu "singleChoice" (Jednokrotny Wybór)

  • answers: Tablica obiektów, gdzie każdy obiekt ma:
    • label: Etykieta odpowiedzi widoczna dla użytkownika.
    • value: Wartość odpowiedzi przesyłana w danych.
  • direction: Opcjonalny parametr określający ułożenie odpowiedzi: 'vertical' (pionowo) lub 'horizontal' (poziomo). Domyślnie 'horizontal'.


Obiekt "thankYouPage" (Strona z podziękowaniem)

Konfiguruje stronę z podziękowaniem, która pojawia się po wysłaniu ankiety.

  • title: Tytuł na stronie z podziękowaniem.
  • description: Tekst pod tytułem na stronie z podziękowaniem.
  • closeButtonText: Tekst na przycisku zamykającym ankietę. Jeśli ten atrybut nie zostanie podany, przycisk się nie wyświetli.
  • image: Opcjonalny obiekt do zarządzania obrazkiem na stronie z podziękowaniem. Gdy brak - pokazuje się serduszko.
    • hidden: Wartość logiczna (true lub false) decydująca o ukryciu domyślnego obrazka.
    • code: Kod SVG, który ma zostać wyświetlony jako obrazek.


Obiekt "wcag"

Obiekt służący do konfiguracji tekstów związanych z dostępnością (Web Content Accessibility Guidelines).

  • text: Zawiera obiekty z tekstami dla czytników ekranu:
    • thankYouPageAlert: Tekst odczytywany przez czytnik po przesłaniu odpowiedzi.
    • thankYouPageButton: Etykieta dla przycisku zamykającego ankietę, odczytywana przez czytnik.


Dokumentacja Metod Obiektu CustomerHero

Poniżej znajduje się opis metod dostępnych w globalnym obiekcie window.CustomerHero.


CustomerHero.init(config)

Inicjalizuje i renderuje ankietę na stronie na podstawie przekazanego obiektu konfiguracyjnego.

Działanie:

  1. Tworzy wewnętrzny magazyn (store) z konfiguracją ankiety.
  2. Sprawdza, czy istnieje plik cookie ankiety. Jeśli nie, tworzy go. Jeśli istnieje, inkrementuje licznik odsłon strony.
  3. Niszczy poprzednią instancję ankiety, jeśli taka istniała, aby zapobiec duplikatom.
  4. Wywołuje metodę checkRenderConditions(), aby zweryfikować, czy ankieta może zostać wyświetlona.
  5. Jeśli warunki są spełnione, renderuje ankietę w elemencie DOM określonym przez config.id.

Zwraca:

  • true, jeśli ankieta została pomyślnie zainicjalizowana i wyrenderowana.
  • false, jeśli warunki do wyświetlenia ankiety nie zostały spełnione.

CustomerHero.show()

Wyświetla ankietę, która została wcześniej zainicjalizowana i jest ukryta.

Działanie:

  1. Sprawdza, czy ankieta może zostać wyświetlona za pomocą wewnętrznej funkcji Renderer.showSurvey().
    • Weryfikuje, czy w konfiguracji podano accessToken lub prefillUrl. Bez jednego z nich ankieta nie może funkcjonować.
  2. Jeśli tak, inkrementuje licznik wyświetleń ankiety (increaseSurveyViews), co jest istotne dla mechanizmu capping.

CustomerHero.hide()

Ukrywa ankietę bez niszczenia jej komponentu. Ankieta pozostaje w strukturze DOM i może być ponownie wyświetlona za pomocą metody show().

Działanie:

  1. Wywołuje wewnętrzną funkcję Renderer.hideSurvey(), która ukrywa wizualnie komponent ankiety.

CustomerHero.destroy()

Całkowicie usuwa ankietę ze strony.

Działanie:

  1. Wywołuje wewnętrzną funkcję Renderer.destroySurvey(), która usuwa komponent ankiety ze struktury DOM oraz czyści powiązane z nim zdarzenia. Aby ponownie wyświetlić ankietę, należy ją zainicjalizować od nowa metodą init().

CustomerHero.setAuthorization(accessToken)

Ustawia token autoryzacyjny (accessToken) dla usług (serwisów) wykorzystywanych przez ankietę.


Parametry:

  • accessToken (string): Token API służący do autoryzacji żądań.


Działanie:

  1. Przekazuje token do modułu services, gdzie jest on przechowywany i wykorzystywany przy komunikacji z API.


CustomerHero.setPrefillUrl(prefillUrl)

Ustawia adres URL prefilla (prefillUrl).


Parametry:

  • prefillUrl (string): Adres URL służący do autoryzacji żądań.

Działanie:

  1. Przekazuje adres URL do modułu services, gdzie jest on przechowywany i wykorzystywany przy komunikacji z API.


CustomerHero.checkRenderConditions()

Wewnętrzna metoda sprawdzająca, czy spełnione są wszystkie warunki konieczne do wyświetlenia ankiety.


Działanie: Sprawdza kolejno:

  1. Zgoda marketingowa: Sprawdza, czy marketingConsent ma wartość true.
  2. Liczba odsłon: Weryfikuje, czy warunek pageViewsTrigger z obiektu capping został spełniony.
  3. Capping: Sprawdza, czy nie został przekroczony limit maksymalnej liczby wyświetleń ankiety (value w obiekcie capping).
  4. Status wypełnienia: Sprawdza, czy ankieta nie została już wcześniej wypełniona przez użytkownika (jeśli hideFilled jest ustawione na true).

Zwraca:

  • true, jeśli wszystkie warunki są spełnione.
  • false, jeśli którykolwiek z warunków nie jest spełniony. W konsoli deweloperskiej pojawi się odpowiednie ostrzeżenie (console.warn).


Utworzenie komponentu (Przykład)

Na stronie może być zainicjowana tylko jedna ankieta. W przypadku zainicjowania kolejnej - poprzednia jest usuwana. Komponent ankiety jest tworzony za pomocą funkcji init().

const config = {
    id: 'customerhero-js', // ID elementu DOM, w który wyrenderuje się ankieta
    surveyId: '548662', //ID ankiety stworzonej na koncie klienta
    marketingConsent: true, // Zgoda marketingowa
    externalKey: 'ChannelID', // Opcjonalnie
    userToken: 'UserID', // Opcjonalnie
    prefillUrl: 'https://example.com/prefill', // (opcjonalnie) URL do prefillu ankiety.
    cookie: {
        name: 'snt-survey', // Nazwa ciastka z danymi ankiety
        age: 90, // Czas życia ciastka w dniach
    },
    capping: {
        // Maksymalna liczba wyświetleń ankiety per ciastko
        // Przedział od 0 do nieskończoności lub undefined - brak limitu
        value: undefined,
        // Wyświetlenie ankiety: Raz na X wyświetleń
        // Przedział od 0 do nieskończoności lub undefined - brak limitu
        pageViewsTrigger: undefined,
        // Gdy <true> - ankieta nie pokaże się jeśli użytkownik już ją wypełnił
        hideFilled: false,
        // Szansa na pokazanie ankiety.
        // Przedział od 0.0 do 1.0 lub undefined - zawsze pokazuje ankietę
        showChance: undefined,
    },
    attributes: {
        a1: 'imie',
        a2: 'nazwisko',
        wiek: 35,
        lang: 'pl', // Atrybut z językiem
    },
    questions: [
        {
            type: 'rate',
            title: 'Jak oceniasz proces [nazwa procesu]?',
            description: 'Twój głos jest dla nas ważny',
            answers: [
                'Bardzo niezadowolony',
                'Niezadowolony',
                'Neutralny',
                'Zadowolony',
                'Bardzo zadowolony',
            ]
        },
        {
            type: 'text',
            title: 'Dlaczego?',
            placeholder: 'W komentarzu nie umieszczaj swoich danych',
        },
        {
            type: 'singleChoice',
            title: 'Jak oceniasz proces otwarcia konta?',
            answers: [
                { label: 'bardzo trudny', value: 'o1' },
                { label: 'trudny', value: 'o2' },
                { label: 'średni', value: 'o3' },
                { label: 'łatwy', value: 'o4' },
                { label: 'bardzo łatwy', value: 'o5' }
            ]
        }
    ],
    thankYouPage: {
        title: 'Dziękujemy!',
        description: 'Twoja opinia pomoże nam się rozwijać',
        closeButtonText: 'Zamknij',
    },
    validationText: 'Wybierz ocenę',
    submitButtonText: 'Prześlij opinię',
    submitButtonLoadingText: 'Poczekaj...',
    onFirstClick: function (item) {
        console.log('onFirstClick:', item);
    },
    onSubmit: function (item) {
        console.log('onSubmit:', item);
    },
    onRender: function () {
        console.log('onRender');
    },
    wcag: {
        text: {
            thankYouPageAlert: 'Twoja odpowiedź została przesłana',
            thankYouPageButton: 'Zamknij ankietę',
        }
    }
};

CustomerHero.init(config);

if (CustomerHero.checkRenderConditions()) {
    CustomerHero.setAuthorization('v2-YnEJ10ii-PRslF0iz-JUNf7ii0-GP5sMan');
    CustomerHero.show();
}

// Pokazanie
CustomerHero.show();

// Ukrywanie
CustomerHero.hide();

// Niszczenie
CustomerHero.destroy();

Zasada działania

W przypadku użytkownika zalogowanego

Po zainicjowaniu komponentu budujemy element HTML z nadanym atrybutem id, który pełni rolę kontenera ankiety, np. <div id=”feedbackSurveyPanel”></div>. Wartość atrybutu należy przekazać w parametrze id wywołania funkcji CustomerHero.init(). Klient wypełnia ankietę. Po kliknięciu na przycisk Wyślij wysyłane jest żądanie HTTP z parametrami poniżej. Szczegóły żądania można podejrzeć w dodatku deweloperskim przeglądarki w zakładce Sieć -> XHR.

  • Method = POST
  • URL = /**********/api/v1/feedback/survey
  • POST param:
    Survey-URL: [ID_SURVEY]/index.html?q1=[ANS_1]&q2=[ANS_2]&external_key=[EXTERNAL_KEY]

Usługa /**********/api/v1/feedback/survey:

  1. Pobiera parameter Survey-URL.
  2. Tworzy bezwzględny adres URL do CustomerHero.
  3. Do URLa dodaje parameter user_token z losowo wygenerowanym identyfikatorem klienta.
  4. Wysyła żądanie do serwera CustomerHero.
  5. Przekazuje do serwera wartość user_token oraz CIF klienta.


Bez względu na status HTTP odpowiedzi prezentowany jest komunikat z podziękowaniem za wypełnienie ankiety.


Przykład URL ankiety wypełnianej po wykonaniu transakcji w Kantorze:
https://ankieta.bank.pl/ankieta/491634/badanie-satysfakcji.html?user_token=xxxxxxxxxx&l1=internet&l2=I&l3=SPOT

  • 491634 to identyfikator ankiety opisany w punkcie konfiguracji.
  • user_token oznacza unikalny identyfikator respondenta.
  • l1 oznacza kanał transakcji.
  • l2 oznacza typ klienta (I=klient indywidualny).
  • l3 oznacza rodzaj transakcji.


Wysyłanie żądania w trybie prefill (użytkownik niezalogowany)

W trybie prefill proces wysyłania danych ankiety różni się od trybu dla użytkownika zalogowanego. Żądanie jest wysyłane bezpośrednio z przeglądarki klienta na serwer ankietowy, z pominięciem wewnętrznej usługi /**********/api/v1/feedback/survey. Cała logika budowania adresu URL i dołączania parametrów odbywa się po stronie klienta (w przeglądarce).

Poniżej znajduje się opis kroków, które następują po kliknięciu przycisku "Wyślij":

  1. Pobranie bazowego adresu URL: Skrypt pobiera adres prefillUrl z obiektu konfiguracyjnego przekazanego podczas inicjalizacji.
  2. Konstrukcja dynamicznego adresu URL: Do bazowego adresu URL dodawane są kolejne parametry zapytania (query parameters):
    • Odpowiedzi na pytania: Każda odpowiedź z ankiety (payload) jest dodawana jako parametr, np. q1=[ANS_1], q2=[ANS_2].
    • Klucz zewnętrzny: Jeśli w konfiguracji zdefiniowano externalKey, jest on dołączany jako parametr external_key=[EXTERNAL_KEY].
    • Token użytkownika: Jeśli w konfiguracji zdefiniowano userToken, jest on dołączany jako parametr user_token=[USER_TOKEN].
    • Atrybuty: Wszystkie wartości z obiektu attributes w konfiguracji są dodawane jako kolejne parametry l1, l2, l3 itd.


Wysłanie żądania HTTP:

  • Metoda: GET
  • URL: W pełni skonstruowany adres URL zawierający wszystkie powyższe parametry.


Obsługa odpowiedzi:
Skrypt nasłuchuje na odpowiedź od serwera, aby zakończyć proces (operacja Promise resolve lub reject w zależności od statusu odpowiedzi). Po otrzymaniu odpowiedzi prezentowany jest komunikat z podziękowaniem.


Przykład finalnego adresu URL w trybie prefill:
https://ankieta.bank.pl/prefill/123456/ankieta-satysfakcji.html?q1=5&q2=Bardzo pomocna&external_key=ChannelID&user_token=UserID-123&l1=internet&l2=indywidualny&l3=konto


Kluczowe różnice w stosunku do trybu zalogowanego

CECHATRYB ZALOGOWANYTRYB PREFILL
Adresat żądaniaWewnętrzna usługa banku (/centrum24-rest/...)Zewnętrzny serwer ankietowy (z prefillUrl)
Metoda HTTPPOSTGET
Miejsce budowy URLLogika po stronie serwera bankuLogika po stronie klienta (w przeglądarce)
Przesyłanie danychW parametrze Survey-URL w ciele żądania POSTJako parametry zapytania w adresie GET


Dodatkowe informacje

  • Dokumentacja jak wygenerować prefill
  • Przekazywanie odpowiedzi do pytania w linku ankiety
  • Przykłady implementacji różnych wariantów:
    • Badanie - Polska
    • Prefill: Badanie NPS lub Ocena - Prefill - Polska

Reguły CSP

Jeśli korzystasz z trybu prefill i masz na swojej stronie skonfigurowaną politykę bezpieczeństwa treści (CSP), z dużym prawdopodobieństwem będziesz musiał ją zaktualizować. Wynika to z faktu, że w trybie prefill przeglądarka użytkownika wysyła żądanie GET bezpośrednio na zewnętrzny adres URL (prefillUrl), aby wysłać dane ankiety. Twoja polityka CSP musi jawnie zezwalać na tego typu połączenia.

Załóżmy, że Twoja strona znajduje się w domenie twoja-strona.pl, a prefillUrl ankiety to https://ankieta.bank.pl/prefill/123456/ankieta-satysfakcji.html.

Dyrektywa connect-src (absolutne minimum):
Musisz zezwolić na połączenia z domeną, na którą wysyłane jest żądanie prefill.


Przykład:

Content-Security-Policy: connect-src 'self' Portal finansowy | Miesięcznik Finansowy BANK ;
  • 'self' pozwala na połączenia w obrębie Twojej własnej domeny (dobra praktyka).
  • Wskazana domena zewnętrzna jawnie zezwala na połączenia z serwerem ankietowym.

W celu zapewnienia pełnego bezpieczeństwa i spójności z infrastrukturą firmy, zdecydowanie zalecamy skontaktowanie się z wewnętrznym zespołem IT, bezpieczeństwa lub DevOps.