v5 ist live

BIS ZU

60% RABATT
Heißer Dezember-Sale
Launch-Sale
0 TAGE
:
0 STUNDEN
:
0 MINUTEN
:
0 SEKUNDEN
JETZT ERHALTEN percent icon

Front‑End‑Leitfaden

Erweiterung der Buchungs- und Zahlungsabläufe von Booknetic mit JavaScript-Hooks, der Objekt‑API und der Addon‑Architektur.

Version:
Kategorien

Die Frontend-Schicht von Booknetic bietet Entwicklern eine flexible Möglichkeit, das Buchungserlebnis zu erweitern, benutzerdefinierte UI zu integrieren, Request-Daten zu verändern und erweiterte Addons zu erstellen, ohne das Core-Plugin direkt zu ändern.

Dieser Leitfaden behandelt die komplette Erweiterungsschicht des Frontends, einschließlich des Buchungspanels, das deine Kunden verwenden, der JavaScript-Struktur des Admin-Panels, des clientseitigen Hook-Systems, der Frontend-Architektur, der Integration des Zahlungsflusses, der Styling-Regeln und der Referenzen zur Dateistruktur.

Technologie-Stack

Das Frontend von Booknetic basiert auf einem leichten und vertrauten Stack:

TechnologieVerwendung
jQuery 3.3.1+Zentrales JavaScript-Framework
Vanilla JavaScript (ES6)Ergänzende Logik
Bootstrap 4.xCSS-Framework
Select2Erweiterte Select-Felder
FlatpickrDatumsauswahl
intlTelInputInternationales Telefonnummernfeld
NiceScrollBenutzerdefinierte Scrollbar

Booknetic verwendet kein Build-Tool. Es gibt weder webpack noch gulp und keinen Kompilierungsschritt. JavaScript- und CSS-Dateien werden direkt so geladen, wie sie sind. Das bedeutet, dass du Dateien hinzufügen oder bearbeiten kannst, ohne einen Asset-Build-Prozess zu benötigen.

JavaScript-Hook-System

Booknetic enthält ein clientseitiges Hook-System, das von den WordPress-Hooks inspiriert ist. Das ist der wichtigste Erweiterungsmechanismus für Frontend-Addons und benutzerdefinierte Verhaltensweisen im Buchungspanel.

Das Objekt bookneticHooks

Das globale Objekt window.bookneticHooks stellt vier zentrale Methoden bereit:

// Einen Filter registrieren (verändert und gibt einen Wert zurück)
bookneticHooks.addFilter(key, callback, uniqueId);

// Einen Filter ausführen (leitet einen Wert durch alle registrierten Callbacks)
let result = bookneticHooks.doFilter(key, initialValue, ...extraArgs);

// Eine Action registrieren (Seiteneffekt, kein Rückgabewert erforderlich)
bookneticHooks.addAction(key, callback, uniqueId);

// Eine Action ausführen
bookneticHooks.doAction(key, ...params);

Actions und Filter haben unterschiedliche Aufgaben:

  • Actions führen deinen Callback für Seiteneffekte aus, zum Beispiel DOM aktualisieren, Events binden oder AJAX-Anfragen auslösen. Ihr Rückgabewert wird ignoriert.
  • Filters leiten einen Wert durch deinen Callback. Du musst den veränderten Wert zurückgeben, sonst erhält der nächste Teil des Codes undefined.

Das optionale Argument uniqueId macht es möglich, später einen bestimmten Hook zu ersetzen oder zu entfernen, indem addFilter(key, null, uniqueId) aufgerufen wird.

Groß- und Kleinschreibung bei Hook-Keys

Alle Hook-Keys werden intern automatisch in Kleinbuchstaben umgewandelt. Das bedeutet, dass booking_panel_loaded, Booking_Panel_Loaded und BOOKING_PANEL_LOADED alle auf denselben Hook verweisen.

Verwende der Konsistenz halber das Namensformat snake_case.

Referenz der Actions

Actions ermöglichen es dir, auf wichtige Lifecycle-Ereignisse im Buchungspanel, im AJAX-System, im Zahlungsfluss und im Kundenpanel zu reagieren.

Lifecycle des Buchungspanels

booking_panel_loaded

Wird einmal ausgelöst, nachdem das Buchungspanel vollständig initialisiert wurde. Das ist der zentrale Einstiegspunkt für dein Addon-JavaScript. Verwende diesen Hook, um Event-Listener zu binden und benutzerdefinierte UI vorzubereiten.

Parameter:

ParameterTypBeschreibung
bookneticobjectDie Instanz des Buchungspanels
bookneticHooks.addAction('booking_panel_loaded', function (booknetic) {
    let panel = booknetic.panel_js; // jQuery-Element dieses Panels

    // Event-Listener per Delegation binden
    panel.on('click', '.my_addon_button', function () {
        // Klick verarbeiten
    });
});

Eine einzelne Seite kann mehrere Buchungspanels enthalten. Jedes Panel löst sein eigenes booking_panel_loaded-Event aus und hat seine eigene booknetic-Instanz. Verwende für DOM-Abfragen immer booknetic.panel_js statt globaler Selektoren.

before_step_loading

Wird direkt vor dem Beginn eines Schrittwechsels ausgelöst, bevor die AJAX-Anfrage gesendet wird.

Parameter:

ParameterTypBeschreibung
bookneticobjectDie Instanz des Buchungspanels
next_stepstringDie ID des Schritts, der gleich geladen wird
current_stepstringDie ID des Schritts, der verlassen wird
bookneticHooks.addAction('before_step_loading', function (booknetic, nextStep, currentStep) {
    console.log(`Navigiere von ${currentStep} zu ${nextStep}`);
});

start_step_loading

Wird ausgelöst, wenn die AJAX-Anfrage für den nächsten Schritt startet.

Parameter:

ParameterTypBeschreibung
bookneticobjectDie Instanz des Buchungspanels
next_stepstringDer Schritt, der geladen wird
current_stepstringDer vorherige Schritt

loaded_step

Wird ausgelöst, nachdem der Inhalt eines Schritts geladen und gerendert wurde. Verwende das, um die UI eines bestimmten Schritts zu erweitern oder zu verbessern.

Parameter:

ParameterTypBeschreibung
bookneticobjectDie Instanz des Buchungspanels
step_idstringDie ID des geladenen Schritts
current_stepstringDie ID des vorherigen Schritts
resultobjectDie AJAX-Response-Daten
bookneticHooks.addAction('loaded_step', function (booknetic, stepId, currentStep, result) {
    if (stepId !== 'confirm_details') return;

    // Ein Coupon-Feld in den Bestätigungsschritt einfügen
    let html = '<div class="my_coupon_section">'
        + '<input type="text" placeholder="' + booknetic.__('enter_coupon') + '">'
        + '<button class="my_apply_btn">' + booknetic.__('apply') + '</button>'
        + '</div>';

    booknetic.panel_js.find('.booknetic_confirm_details').append(html);
});

loaded_step_{step_id}

Dynamische Version von loaded_step. Dieser Hook wird nur für den angegebenen Schritt ausgelöst, sodass kein manueller if-Check nötig ist.

Verfügbare Schritt-IDs:

location, staff, service, service_extras, date_time, recurring_info, information, cart, confirm_details

Parameter:

ParameterTypBeschreibung
bookneticobjectDie Instanz des Buchungspanels
resultobjectDie AJAX-Response-Daten
bookneticHooks.addAction('loaded_step_date_time', function (booknetic, result) {
    // Läuft nur, wenn der Schritt Datum & Uhrzeit geladen wird
    // Hier das Kalenderverhalten anpassen
});

loaded_cached_step

Wird ausgelöst, wenn ein zuvor besuchter Schritt aus dem Cache geladen wird.

Parameter:

ParameterTypBeschreibung
bookneticobjectDie Instanz des Buchungspanels
step_idstringDer aus dem Cache geladene Schritt

step_end_{step_id}

Wird ausgelöst, nachdem ein Schritt die Validierung bestanden hat und der Benutzer kurz davor ist, weiterzugehen. Das ist nützlich, wenn du auf einen abgeschlossenen Schritt reagieren möchtest, bevor der nächste geladen wird.

Parameter:

ParameterTypBeschreibung
bookneticobjectDie Instanz des Buchungspanels
bookneticHooks.addAction('step_end_service', function (booknetic) {
    // Der Benutzer hat einen Service ausgewählt und geht weiter
    let serviceId = booknetic.getSelected.service();
    console.log('Ausgewählter Service:', serviceId);
});

step_loaded_with_error

Wird ausgelöst, wenn ein Schritt nicht geladen werden kann, weil der Server einen Fehler zurückgegeben hat.

Parameter:

ParameterTypBeschreibung
bookneticobjectDie Instanz des Buchungspanels
next_stepstringDer Schritt, der nicht geladen werden konnte
current_stepstringDer Schritt, zu dem zurückgekehrt wird
resultobjectDie Fehler-Response

AJAX-Lifecycle

ajax_before_{action}

Wird ausgelöst, bevor eine AJAX-Anfrage gesendet wird. {action} ist der Name der AJAX-Action, zum Beispiel get_data, confirm oder check_customer_exist.

Parameter:

ParameterTypBeschreibung
paramsobjectDie Anfrageparameter
bookneticobjectDie Instanz des Buchungspanels
bookneticHooks.addAction('ajax_before_confirm', function (params, booknetic) {
    // Einen Ladeindikator hinzufügen, Buttons deaktivieren usw.
    console.log('Terminbestätigung wird gleich gestartet ...');
});

ajax_after_{action}_success

Wird ausgelöst, nachdem eine AJAX-Response erfolgreich zurückgegeben wurde.

Parameter:

ParameterTypBeschreibung
bookneticobjectDie Instanz des Buchungspanels
paramsobjectDie ursprünglichen Anfrageparameter
resultobjectDie Server-Response
bookneticHooks.addAction('ajax_after_get_data_success', function (booknetic, params, result) {
    // Schrittdaten wurden erfolgreich geladen
});

ajax_after_{action}_error

Wird ausgelöst, nachdem eine AJAX-Anfrage fehlgeschlagen ist.

Parameter:

ParameterTypBeschreibung
bookneticobjectDie Instanz des Buchungspanels
paramsobjectDie ursprünglichen Anfrageparameter
resultobjectDie Fehler-Response, die bei Netzwerkfehlern undefined sein kann

Zahlungs-Lifecycle

before_processing_payment

Wird ausgelöst, bevor der Zahlungsvorgang beginnt. Addons für Zahlungs-Gateways verwenden diesen Hook, um ihren Ablauf zu initialisieren, zum Beispiel indem ein Popup-Fenster geöffnet wird.

Parameter:

ParameterTypBeschreibung
payment_methodstringDie ausgewählte Zahlungsmethode, zum Beispiel 'local', 'stripe' oder 'paypal'
dataobjectVom Server zurückgegebene Zahlungsdaten, einschließlich Tokens, URLs und ähnlicher Werte
bookneticHooks.addAction('before_processing_payment', function (paymentMethod, data) {
    if (paymentMethod !== 'my_gateway') return;

    // Popup-Fenster für das Zahlungs-Gateway öffnen
    myPaymentWindow = window.open('', 'booknetic_payment_window', 'width=1000,height=700');
});

after_processing_payment

Wird ausgelöst, nachdem der Zahlungsvorgang initialisiert wurde. Das wird typischerweise verwendet, um das Popup zur Seite des Zahlungs-Gateways weiterzuleiten.

Parameter:

ParameterTypBeschreibung
payment_methodstringDie Zahlungsmethode
statusbooleanOb die Initialisierung der Zahlung erfolgreich war
dataobjectDie Zahlungs-Response-Daten
bookneticHooks.addAction('after_processing_payment', function (paymentMethod, status, data) {
    if (paymentMethod !== 'my_gateway') return;

    if (!status) {
        myPaymentWindow.close();
        return;
    }

    // Das Popup zur Checkout-Seite des Gateways weiterleiten
    myPaymentWindow.location.href = data['url'];
});

payment_completed

Wird ausgelöst, wenn der Zahlungsvorgang erfolgreich abgeschlossen wurde.

Parameter:

ParameterTypBeschreibung
statusbooleanErfolgsstatus der Zahlung
paymentDataobjectDaten des Zahlungsergebnisses

payment_completed_deprecated

Veralteter Hook, der aus Gründen der Abwärtskompatibilität beibehalten wird. Verwende stattdessen payment_completed.

Parameter:

ParameterTypBeschreibung
bookneticobjectDie Instanz des Buchungspanels

payment_error

Wird ausgelöst, wenn ein Zahlungsversuch fehlschlägt.

Parameter:

ParameterTypBeschreibung
bookneticobjectDie Instanz des Buchungspanels

booking_finished_successfully

Wird ausgelöst, wenn der gesamte Buchungsfluss erfolgreich abgeschlossen ist und der Abschlussscreen angezeigt wird.

Parameter:

ParameterTypBeschreibung
bookneticobjectDie Instanz des Buchungspanels
bookneticHooks.addAction('booking_finished_successfully', function (booknetic) {
    // Conversion-Tracking, Analytics-Event usw. auslösen
    if (typeof gtag !== 'undefined') {
        gtag('event', 'booking_completed', {
            appointment_id: booknetic.appointmentId
        });
    }
});

Kundenpanel

customer_panel_loaded

Wird ausgelöst, wenn das Kundenpanel geladen wird. Das ist die Oberfläche, in der bestehende Kunden ihre Buchungen verwalten.

Parameter: Keine

bookneticHooks.addAction('customer_panel_loaded', function () {
    // Einen benutzerdefinierten Tab zum Kundenpanel hinzufügen
    let tabHtml = '<li class="my_addon_tab" data-tab="my_tab">'
        + '<span>Mein benutzerdefinierter Tab</span></li>';
    $('.booknetic_cp_tabs').append(tabHtml);

    $(document).on('click', '.my_addon_tab', function () {
        booknetic.ajax('my_addon_get_tab_content', {}, function (result) {
            $('.booknetic_cp_content').html(result.html);
        });
    });
});

Referenz der Filter

Filter erlauben es, ausgehende Anfragedaten, Ergebnisse der Schrittvalidierung, Warenkorbeinträge und gerendertes Frontend-HTML zu verändern, bevor Booknetic sie verwendet.

AJAX-Filter

ajax

Filtert alle ausgehenden AJAX-Anfrageparameter. Dieser Hook wird für jede AJAX-Anfrage ausgeführt, die vom Buchungspanel gesendet wird.

Parameter:

ParameterTypBeschreibung
paramsobjectDie Anfrageparameter
bookneticobjectDie Instanz des Buchungspanels

Gibt zurück: object — Die modifizierten Parameter

bookneticHooks.addFilter('ajax', function (params, booknetic) {
    // Einen benutzerdefinierten Parameter zu jeder AJAX-Anfrage hinzufügen
    params.my_tracking_id = 'abc123';
    return params;
});

ajax_{action}

Filtert Parameter nur für eine bestimmte AJAX-Action. Zum Beispiel betrifft ajax_confirm nur die Bestätigungsanfrage.

Parameter:

ParameterTypBeschreibung
paramsobjectDie Anfrageparameter
bookneticobjectDie Instanz des Buchungspanels

Gibt zurück: object

bookneticHooks.addFilter('ajax_confirm', function (params, booknetic) {
    // Zusätzliche Daten nur zur Bestätigungsanfrage hinzufügen
    params.special_note = document.getElementById('my_note_field').value;
    return params;
});

appointment_ajax_data

Filtert das endgültige FormData, das gesendet wird, wenn der Termin bestätigt wird. Das ist der letzte Punkt, an dem du die Daten verändern kannst, bevor sie das Backend erreichen.

Parameter:

ParameterTypBeschreibung
dataFormDataDie Daten der Terminübermittlung
bookneticobjectDie Instanz des Buchungspanels

Gibt zurück: FormData

bookneticHooks.addFilter('appointment_ajax_data', function (formData, booknetic) {
    // Daten benutzerdefinierter Formularfelder anhängen
    formData.append('coupon_code', document.getElementById('my_coupon_input').value);
    formData.append('gift_card_code', document.getElementById('my_gift_card_input').value);
    return formData;
});

Schritt-Filter

step_validation_{step_id}

Validiert einen Schritt, bevor der Benutzer fortfahren kann. Gib { status: false, errorMsg: '...' } zurück, um die Navigation zu blockieren.

Verfügbare Schritt-IDs:

location, staff, service, service_extras, date_time, recurring_info, information, cart, confirm_details

Parameter:

ParameterTypBeschreibung
resultobject{ status: true, errorMsg: '' }
bookneticobjectDie Instanz des Buchungspanels

Gibt zurück: object{ status: boolean, errorMsg: string }

bookneticHooks.addFilter('step_validation_information', function (result, booknetic) {
    let customField = booknetic.panel_js.find('#my_required_field').val();
    if (!customField) {
        return {
            status: false,
            errorMsg: booknetic.__('my_field_required')
        };
    }
    return result;
});

load_step_{step_id}

Steuert, ob ein Schritt geladen werden soll. Gib false zurück, um das Laden des Schritts zu verhindern. Das ist nützlich, wenn du zuerst ein Modal anzeigen oder zusätzliche Eingaben einsammeln musst.

Parameter:

ParameterTypBeschreibung
bookneticobjectDie Instanz des Buchungspanels

Gibt zurück: boolean

bookneticHooks.addFilter('load_step_date_time', function (booknetic) {
    // Vor dem Schritt Datum/Uhrzeit ein benutzerdefiniertes Dauer-Modal anzeigen
    if (!durationSelected) {
        showDurationModal(booknetic);
        return false; // Laden des Schritts blockieren
    }
    return true; // Laden des Schritts erlauben (Standard)
});

Warenkorb-Filter

bkntc_cart

Filtert die Daten des Warenkorbeintrags, bevor sie im Warenkorb-Array gespeichert werden.

Parameter:

ParameterTypBeschreibung
cart_itemobjectDas Objekt mit den Daten des Warenkorbeintrags
bookneticobjectDie Instanz des Buchungspanels

Gibt zurück: object

bookneticHooks.addFilter('bkntc_cart', function (cartItem, booknetic) {
    // Benutzerdefinierte Daten zum Warenkorbeintrag hinzufügen
    cartItem.my_addon_data = {
        coupon_code: mySelectedCoupon,
        discount: myCalculatedDiscount
    };
    return cartItem;
});

Zeitfenster-Filter

bkntc_date_time_load

Filtert das HTML jedes Zeitfensters, bevor es im Schritt Datum & Uhrzeit gerendert wird.

Parameter:

ParameterTypBeschreibung
htmlstringDas HTML-Element des Zeitfensters
time_objobjectDie Daten des Zeitfensters, wie start_time, end_time und andere zugehörige Felder
bookneticobjectDie Instanz des Buchungspanels

Gibt zurück: string

bookneticHooks.addFilter('bkntc_date_time_load', function (html, timeObj, booknetic) {
    // Ein "Beliebt"-Badge zu stark nachgefragten Slots hinzufügen
    if (timeObj.booking_count > 5) {
        html = html.replace('</div>', '<span class="popular-badge">Beliebt</span></div>');
    }
    return html;
});

Das Objekt booknetic

Jede Instanz des Buchungspanels erstellt ihr eigenes booknetic-Objekt. Dieses fungiert als zentrale API und Zustandscontainer des Panels.

Eigenschaften

EigenschaftTypBeschreibung
panel_jsjQueryjQuery-Referenz auf das Root-Element dieses Panels
cartArrarrayWarenkorb-Array
cartCurrentIndexnumberIndex des aktuell bearbeiteten Warenkorbeintrags
calendarDateTimesobjectVerfügbare Daten und Zeitfenster
calendarYearnumberDer aktuell angezeigte Kalenderjahrgang
calendarMonthnumberDer aktuell angezeigte Kalendermonat
paymentWindowWindowReferenz auf das Zahlungs-Popup-Fenster
paymentStatusbooleanErfolgs- oder Fehlerstatus der Zahlung
appointmentIdnumberDie ID des erstellten Termins nach der Bestätigung
paymentIdnumberDie ID des Zahlungsdatensatzes

Methoden

booknetic.ajax(action, params, successCallback, loading, errorCallback, async)

Sendet eine AJAX-Anfrage an das Booknetic-Backend.

ParameterTypStandardBeschreibung
actionstring|objectName der Action. Wenn es ein String ist, fügt Booknetic bkntc_ davor. Wenn es ein Objekt ist, sollte es im Format {backend_action, frontend_action} vorliegen
paramsobject|FormDataAnfrageparameter
successCallbackfunctionCallback, der im Erfolgsfall mit (result) ausgeführt wird
loadingbooleantrueOb der Lade-Spinner angezeigt werden soll
errorCallbackfunctionCallback, der im Fehlerfall mit (result) ausgeführt wird
asyncbooleantrueOb die Anfrage asynchron ist
booknetic.ajax('my_addon_action', {
    param1: 'value1',
    param2: 'value2'
}, function (result) {
    // Erfolg
    console.log(result.data);
}, true, function (error) {
    // Fehler
    booknetic.toast(error.error_msg, 'error');
});

Der Action-Name wird automatisch mit bkntc_ präfixiert. Zum Beispiel wird 'my_addon_action' auf dem Server zu bkntc_my_addon_action.

booknetic.ajaxAsync(action, params, loading, async)

Promise-basierte Version von ajax(). Sie wird mit dem Ergebnis aufgelöst oder mit dem Fehler abgelehnt.

try {
    let result = await booknetic.ajaxAsync('my_action', { id: 123 }, true);
    console.log(result.data);
} catch (error) {
    booknetic.toast(error.error_msg, 'error');
}

booknetic.toast(message, type, duration)

Zeigt eine Toast-Benachrichtigung an.

ParameterTypBeschreibung
messagestringNachrichtentext
typestring'success', 'error' oder 'warning'
durationnumberAuto-Dismiss-Zeit in Millisekunden

booknetic.__(key)

Gibt einen lokalisierten String anhand eines Keys zurück.

booknetic.__('select_service');  // → "Service auswählen" (oder das übersetzte Äquivalent)

booknetic.getSelected

Ein Objekt mit Getter-Methoden für die aktuellen Buchungsauswahlen:

booknetic.getSelected.location();           // ID des ausgewählten Standorts
booknetic.getSelected.service();            // ID des ausgewählten Services
booknetic.getSelected.staff();              // ID des ausgewählten Mitarbeiters
booknetic.getSelected.serviceExtras();      // [{extra, quantity}, ...]
booknetic.getSelected.date();               // 'YYYY-MM-DD'
booknetic.getSelected.time();               // 'HH:MM'
booknetic.getSelected.formData();           // {email, name, surname, phone}
booknetic.getSelected.customerId();         // Kunden-ID (wenn bestehender Kunde)
booknetic.getSelected.paymentMethod();      // Schlüssel der Zahlungsmethode
booknetic.getSelected.paymentDepositFullAmount(); // 'deposit' oder 'full_amount'

// Wiederkehrende Termine
booknetic.getSelected.recurringStartDate();
booknetic.getSelected.recurringEndDate();
booknetic.getSelected.recurringTimesArr();

booknetic.stepManager

Der Controller für die Schrittnavigation:

booknetic.stepManager.goForward();          // Zum nächsten Schritt navigieren
booknetic.stepManager.goBack();             // Zum vorherigen Schritt navigieren
booknetic.stepManager.loadStep(stepId);     // Einen bestimmten Schritt laden
booknetic.stepManager.getNextStep();        // Die ID des nächsten Schritts abrufen
booknetic.stepManager.getPrevStep();        // Die ID des vorherigen Schritts abrufen
booknetic.stepManager.saveData();           // Aktuelle Auswahl im Warenkorb speichern

Utility-Methoden

booknetic.throttle(fn, delay);              // Gibt eine throttled Funktion zurück (Standard 500ms)
booknetic.debounce(fn, delay);              // Gibt eine debounced Funktion zurück (Standard 300ms)
booknetic.htmlspecialchars(string);         // Einen String HTML-encoden
booknetic.htmlspecialchars_decode(string);  // Einen HTML-String dekodieren
booknetic.sanitizeHTML(node);               // <script>-Tags aus einem DOM-Knoten entfernen
booknetic.isMobileView();                   // true, wenn Viewport ≤ 1000px
booknetic.zeroPad(number, length);          // Zahl mit führenden Nullen auffüllen (z. B. zeroPad(42, 4) → "0042")
booknetic.loading(state);                   // Den Lade-Spinner anzeigen (1) oder ausblenden (0)

Architektur des Buchungspanels

Ein Verständnis der Architektur des Buchungspanels macht die Entwicklung von Addons deutlich einfacher, besonders wenn du in Schritte, Caching-Verhalten oder den AJAX-Fluss eingreifen musst.

Buchungsfluss

Das Buchungspanel folgt einem schrittbasierten Wizard-Muster:

Location → Staff → Service → Service Extras → Date/Time → [Recurring Info] → Information → Cart → Confirmation → Finish

Schritte können in den Admin-Einstellungen ausgeblendet oder neu angeordnet werden. Navigation, Caching und Validierung werden automatisch vom Step Manager gehandhabt.

Initialisierung

Wenn eine Seite geladen wird, die den Shortcode [booknetic] enthält, passiert Folgendes:

  1. Die PHP-View rendert das HTML-Grundgerüst des Buchungspanels.
  2. Ein Inline-Script ruft bookneticInitBookingPage() für jedes Panel auf der Seite auf.
  3. Der erste Schritt wird per AJAX mit bkntc_get_data geladen.
  4. Die Action booking_panel_loaded wird ausgelöst.
// Vereinfachter Initialisierungsfluss
jQuery(".booknetic_appointment").each((i, element) => {
    window.bookneticInitBookingPage(element);
});

HTML-Struktur

<div class="booknetic_appointment" id="booknetic_theme_{themeId}">
    <div class="booknetic_appointment_steps">
        <!-- Schritt-Seitenleiste / Fortschrittsanzeige -->
    </div>
    <div class="booknetic_appointment_container">
        <div class="booknetic_appointment_container_header">
            <!-- Titel des aktuellen Schritts + Warenkorb-Icon -->
        </div>
        <div class="booknetic_appointment_container_body">
            <!-- Dynamischer Schrittinhalt (per AJAX geladen) -->
        </div>
        <div class="booknetic_appointment_container_footer">
            <!-- Zurück-, Weiter-, Bestätigen-Buttons -->
        </div>
    </div>
    <div class="booknetic_appointment_finished">
        <!-- Erfolgs-/Fehlerscreen -->
    </div>
</div>

Laden des Schrittinhalts

Jeder Schritt wird per AJAX über den Endpunkt bkntc_get_data geladen. Das Backend liefert HTML zurück, das in .booknetic_appointment_container_body eingefügt wird.

Zur Performance-Verbesserung wird der Schrittinhalt in booknetic.cartHTMLBody gecacht, sodass beim Zurückkehren zu einem vorherigen Schritt keine neue Anfrage gesendet wird.

AJAX-Anfragefluss

Jede AJAX-Anfrage folgt dieser Pipeline:

1. Die Action wird präfixiert: 'bkntc_' + action
2. tenant_id wird angehängt
3. Hook: bookneticHooks.doAction('ajax_before_' + action, params, booknetic)
4. Hook: params = bookneticHooks.doFilter('ajax', params, booknetic)
5. Hook: params = bookneticHooks.doFilter('ajax_' + action, params, booknetic)
6. $.ajax() POST → admin-ajax.php
7. Bei Erfolg: callback(result) + doAction('ajax_after_{action}_success', ...)
8. Bei Fehler:  callback(result) + doAction('ajax_after_{action}_error', ...)

Format der AJAX-Response

Alle AJAX-Responses von Booknetic verwenden die folgende Struktur:

{
    status: 'ok' | 'error',
    error_msg: 'Fehlermeldung',            // Bei Fehlern vorhanden
    errors: [{ field, message }],          // Feldbezogene Validierungsfehler
    html: '<div>...</div>',                // HTML-Inhalt des Schritts
    data: { ... }                          // Zusätzliche Daten
}

Integration von Zahlungs-Gateways

Ein Addon für ein Zahlungs-Gateway muss drei zentrale Hooks implementieren, um sich sauber in den Buchungsfluss zu integrieren.

Vollständiges Gateway-Muster

(function () {
    let paymentWindow;
    let paymentData;

    // Schritt 1: Das Zahlungsfenster öffnen, bevor die Verarbeitung beginnt
    bookneticHooks.addAction('before_processing_payment', function (paymentMethod, data) {
        if (paymentMethod !== 'my_gateway') return;

        paymentData = data;
        paymentWindow = window.open('', 'booknetic_payment_window', 'width=1000,height=700');
    });

    // Schritt 2: Das Popup zum Gateway weiterleiten, nachdem der Server die Zahlung erstellt hat
    bookneticHooks.addAction('after_processing_payment', function (paymentMethod, status, data) {
        if (paymentMethod !== 'my_gateway') return;

        if (!status) {
            paymentWindow.close();
            return;
        }

        // Zur Checkout-Seite des Gateways weiterleiten
        paymentWindow.location.href = data['url'];
    });
})();

Auf der Callback-Seite des Gateways, nachdem der Kunde die Zahlung abgeschlossen hat, sollte das Popup eine der folgenden Funktionen aufrufen:

// In der Callback-View, die im Popup geladen wird:
window.opener.bookneticPaymentStatus(true);   // Zahlung erfolgreich
// oder
window.opener.bookneticPaymentStatus(false);  // Zahlung fehlgeschlagen

Das Haupt-Buchungspanel überwacht das Popup. Wenn es geschlossen wird, prüft es das Zahlungsergebnis und zeigt dann entweder den Erfolgsbildschirm oder einen Fehlerzustand an.

Zusammenfassung des Zahlungsflusses

1. Der Benutzer klickt auf "Bestätigen"
2. confirmAppointment() → AJAX: bkntc_confirm
3. Der Server erstellt den Termin und gibt die Zahlungs-URL zurück
4. doAction('before_processing_payment') → Gateway öffnet das Popup
5. doAction('after_processing_payment') → Popup wird zur Gateway-URL weitergeleitet
6. Der Kunde schließt die Zahlung im Popup ab
7. Der Gateway-Callback ruft window.opener.bookneticPaymentStatus(true) auf
8. Das Popup wird geschlossen und das Hauptfenster erkennt das
9. doAction('payment_completed')
10. doAction('booking_finished_successfully') → Erfolgsbildschirm wird angezeigt

JavaScript des Admin-Panels

Der Admin-Bereich von Booknetic hat seine eigene JavaScript-Architektur und sein eigenes booknetic-Objekt.

Zentrales Admin-Objekt

Das zentrale Admin-Objekt ist in app/Backend/Base/assets/js/booknetic.js definiert:

var booknetic = {
    // Lokalisierung
    __(key) — Einen übersetzten String abrufen

    // UI
    loadModal(modal, parameters, options) — Ein Modal per AJAX laden
    modalHide($modal) — Ein Modal schließen
    toast(title, type, duration) — Eine Toast-Benachrichtigung anzeigen
    confirm(text, bg, icon, okCallback, okText, cancelText) — Einen Bestätigungsdialog anzeigen

    // AJAX
    ajax(action, params, callback) — Eine AJAX-Anfrage ausführen

    // DataTable
    dataTable.init(container) — Eine Tabelle initialisieren
    dataTable.reload() — Tabellendaten neu laden
    dataTable.doAction(action, ids, params, callback) — Eine Tabellenaktion ausführen
};

JavaScript-Muster für Module

Jedes Admin-Modul hat typischerweise seine eigene JavaScript-Datei und folgt einem konsistenten Interaktionsmuster:

$(document).ready(function () {
    // Hinzufügen-Button
    $(document).on('click', '#addBtn', function () {
        booknetic.loadModal('add_new', {});
    });

    // Bearbeiten-Action der DataTable
    booknetic.dataTable.actionCallbacks['edit'] = function (ids) {
        booknetic.loadModal('add_new', { id: ids[0] });
    };

    // Löschen-Action der DataTable
    booknetic.dataTable.actionCallbacks['delete'] = function (ids) {
        booknetic.confirm(
            booknetic.__('are_you_sure_want_to_delete'),
            'danger', 'trash',
            function () {
                booknetic.dataTable.doAction('delete', ids, {}, function () {
                    booknetic.toast(booknetic.__('deleted'), 'success', 2000);
                });
            }
        );
    };
});

Muster für das Absenden von Modal-Formularen

$(document).on('click', '#saveBtnModal', function () {
    let name = $('#input_name').val();

    if (!name) {
        booknetic.toast(booknetic.__('name_required'), 'error');
        return;
    }

    booknetic.ajax('save', {
        id: $('#input_id').val(),
        name: name
    }, function (result) {
        booknetic.toast(booknetic.__('saved'), 'success', 2000);
        booknetic.modalHide($('.modal'));
        booknetic.dataTable.reload();
    });
});

Addon-Entwicklung

Ein Frontend-Addon muss in der Regel Assets laden, lokalisierte Strings registrieren, sich in Lifecycle-Events einklinken, UI einfügen, benutzerdefinierte Daten senden und in manchen Fällen Schritte validieren.

Frontend-Assets laden

Registriere die Skripte und Styles deines Addons über PHP-Hooks:

// In der Methode initFrontend() deines AddonLoaders:
public function initFrontend()
{
    // Assets im Buchungspanel laden
    add_filter('bkntc_booking_panel_assets', function ($assets) {
        $assets[] = ['type' => 'js',  'url' => self::loadAsset('assets/js/init.js')];
        $assets[] = ['type' => 'css', 'url' => self::loadAsset('assets/css/style.css')];
        return $assets;
    });
}

// In der Methode initBackend() deines AddonLoaders:
public function initBackend()
{
    // Assets auf Admin-Seiten laden
    add_action('bkntc_enqueue_assets', function ($module, $action, $viewPath) {
        if ($module === 'appointments') {
            wp_enqueue_script('my-addon-admin', self::loadAsset('assets/js/admin.js'), ['jquery']);
        }
    }, 10, 3);
}

Lokalisierte Strings hinzufügen

Du kannst übersetzbare Strings aus PHP in JavaScript übergeben:

// PHP: Strings registrieren
add_filter('bkntc_frontend_localization', function ($strings) {
    $strings['my_addon_label'] = bkntc__('My Label');
    $strings['my_addon_error'] = bkntc__('Something went wrong');
    return $strings;
});
// JavaScript: Strings verwenden
booknetic.__('my_addon_label');  // → "My Label"
booknetic.__('my_addon_error');  // → "Something went wrong"

Vollständiges Frontend-Muster für ein Addon

Eine typische init.js eines Addons kann dieser Struktur folgen:

// Panel geladen — Events binden
bookneticHooks.addAction('booking_panel_loaded', function (booknetic) {
    let panel = booknetic.panel_js;

    // DOM-Events per Delegation binden
    panel.on('click', '.my_addon_apply_btn', function () {
        let code = panel.find('#my_addon_input').val();

        booknetic.ajax('my_addon_validate', { code: code }, function (result) {
            // UI mit dem Ergebnis aktualisieren
            panel.find('.my_addon_status').text(result.data.message);
        }, true, function (error) {
            booknetic.toast(error.error_msg, 'error');
        });
    });
});

// UI in einen bestimmten Schritt einfügen
bookneticHooks.addAction('loaded_step_confirm_details', function (booknetic, result) {
    let html = '<div class="my_addon_section">'
        + '<input type="text" id="my_addon_input" placeholder="' + booknetic.__('my_placeholder') + '">'
        + '<button class="my_addon_apply_btn">' + booknetic.__('apply') + '</button>'
        + '</div>';

    booknetic.panel_js.find('.booknetic_confirm_details').append(html);
});

// Benutzerdefinierte Daten an die Terminübermittlung anhängen
bookneticHooks.addFilter('appointment_ajax_data', function (formData, booknetic) {
    let myValue = booknetic.panel_js.find('#my_addon_input').val();
    if (myValue) {
        formData.append('my_addon_code', myValue);
    }
    return formData;
});

// Benutzerdefinierte Schrittvalidierung
bookneticHooks.addFilter('step_validation_information', function (result, booknetic) {
    let field = booknetic.panel_js.find('#my_required_field').val();
    if (!field) {
        return { status: false, errorMsg: booknetic.__('field_required') };
    }
    return result;
});

CSS-Richtlinien

Die Frontend-Styles von Booknetic sind so gekapselt, dass Konflikte mit WordPress-Themes und Drittanbieter-Styles reduziert werden.

Isolierung

Die Styles des Buchungspanels sind innerhalb von .booknetic_appointment isoliert:

.booknetic_appointment * {
    all: unset;
    /* Eigenschaften werden danach explizit neu gesetzt */
}

Wenn du benutzerdefinierte Styles schreibst, halte sie immer im Scope des Buchungspanels:

.booknetic_appointment .my_addon_section {
    padding: 15px;
    border: 1px solid #e0e0e0;
    border-radius: 8px;
    margin-top: 10px;
}

Benennung von CSS-Klassen

PräfixGeltungsbereichBeispiel
booknetic_Zentrale Elemente des Buchungspanels.booknetic_service_card
bkntc_Interne Kurzform-Klassen.bkntc_service_list
bkntcsaas_Elemente des SaaS-Moduls.bkntcsaas_signup_btn

Statusmodifikatoren verwenden das Suffix _{state}, z. B. .booknetic_card_selected und .booknetic_category_accordion_hidden.

Zentrale CSS-Klassen

KlasseBeschreibung
.booknetic_appointmentRoot-Container des gesamten Buchungspanels
.booknetic_appointment_stepsLinke Seitenleiste mit Schrittindikatoren
.booknetic_appointment_containerHauptcontainer für den Inhalt
.booknetic_appointment_container_bodyDynamischer Bereich für Schrittinhalte
.booknetic_cardSelektierbares Kartenelement für Standort, Mitarbeiter und Ähnliches
.booknetic_card_selectedAktuell ausgewählte Karte
.booknetic_service_cardKarte zur Serviceauswahl
.booknetic_service_card_selectedAktuell ausgewählter Service
.booknetic_calendar_rowsRaster der Kalendertage
.booknetic_calendar_daysEinzelner Kalendertag
.booknetic_time_elementZeitfensterelement
.booknetic_selected_timeAktuell ausgewähltes Zeitfenster
.booknetic_appointment_finishedErfolgs- oder Abschlussbildschirm

Responsives Verhalten

Das Buchungspanel wechselt bei einer Viewport-Breite von 1000px in das mobile Layout.

booknetic.isMobileView(); // gibt true zurück, wenn der Viewport ≤ 1000px ist

In der mobilen Ansicht:

  • Die Schritt-Seitenleiste wird ausgeblendet
  • Der Inhaltsbereich nimmt die volle Breite ein
  • Das Scroll-Verhalten ändert sich

RTL-Unterstützung

RTL wird automatisch erkannt:

if ($('html').attr('dir') === 'rtl') {
    $('body').addClass('rtl');
}

Referenz der Dateistruktur

app/Frontend/assets/
├── js/
│   ├── booknetic.js              # Zentrale Logik des Buchungspanels (~2400 Zeilen)
│   ├── booknetic-popup.js        # Modal-/Popup-Handler
│   ├── booknetic-signin.js       # Kunden-Login
│   ├── booknetic-signup.js       # Kunden-Registrierung
│   ├── booknetic-forgot-password.js
│   └── steps/                    # Schrittbezogene JS-Dateien
│       ├── step_locations.js
│       ├── step_services.js
│       ├── step_staff.js
│       ├── step_service_extras.js
│       ├── step_date_time.js
│       ├── step_information.js
│       ├── step_cart.js
│       ├── step_confirm_details.js
│       └── step_recurring_info.js
├── css/
│   ├── booknetic.css             # Hauptstyles des Buchungspanels
│   └── bootstrap-booknetic.css   # Bootstrap-Anpassungen
└── icons/                        # SVG-Icons (40+)

app/Frontend/view/booking_panel/  # PHP-Templates der Schritte
├── locations.php
├── services.php
├── staff.php
├── service_extras.php
├── date_time.php
├── information.php
├── confirm.php
├── cart.php
└── booknetic.php                 # Haupttemplate des Panels

app/Backend/Base/assets/js/
├── booknetic.js                  # Zentrales Admin-Panel-JS
└── generic-table.js              # GenericTable-Klasse

app/Backend/[Module]/assets/      # Modulspezifische Assets
├── js/
└── css/

Zusammenfassung

Die Frontend-Entwicklerschicht von Booknetic bietet dir ein praktisches Erweiterungssystem für das Buchungspanel, das Kundenpanel, das Admin-JavaScript, Zahlungs-Gateways und das Rendering-Verhalten der Oberfläche.

Hook-TypAnzahlZweck
JS-Actions18+Auf Lifecycle-Ereignisse wie Schrittladen, Zahlungsabschluss und erfolgreiche Buchung reagieren
JS-Filter7+Anfragedaten, Validierungsergebnisse, Warenkorbeinträge und gerenderte Frontend-Ausgabe verändern

Schnellreferenz: am häufigsten verwendete Hooks

HookTypHäufiger Anwendungsfall
booking_panel_loadedActionAddon initialisieren und Event-Listener binden
loaded_step / loaded_step_{id}ActionBenutzerdefinierte UI in einen bestimmten Schritt einfügen
step_end_{step_id}ActionReagieren, wenn ein Schritt abgeschlossen ist
before_processing_paymentActionEin Popup für das Zahlungs-Gateway öffnen
after_processing_paymentActionDas Popup zur Checkout-Seite des Gateways weiterleiten
booking_finished_successfullyActionConversion-Tracking und Analytics
step_validation_{step_id}FilterBenutzerdefinierte Schrittvalidierung hinzufügen
appointment_ajax_dataFilterBenutzerdefinierte Daten an die Buchungsübermittlung anhängen
ajax / ajax_{action}FilterAJAX-Anfrageparameter verändern
bkntc_date_time_loadFilterDas Rendering von Zeitfenstern anpassen
load_step_{step_id}FilterDas Laden von Schritten bedingt blockieren
bkntc_cartFilterDaten von Warenkorbeinträgen verändern