Front‑End‑Leitfaden
Erweiterung der Buchungs- und Zahlungsabläufe von Booknetic mit JavaScript-Hooks, der Objekt‑API und der Addon‑Architektur.
Erweiterung der Buchungs- und Zahlungsabläufe von Booknetic mit JavaScript-Hooks, der Objekt‑API und der Addon‑Architektur.
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.
Das Frontend von Booknetic basiert auf einem leichten und vertrauten Stack:
| Technologie | Verwendung |
|---|---|
| jQuery 3.3.1+ | Zentrales JavaScript-Framework |
| Vanilla JavaScript (ES6) | Ergänzende Logik |
| Bootstrap 4.x | CSS-Framework |
| Select2 | Erweiterte Select-Felder |
| Flatpickr | Datumsauswahl |
| intlTelInput | Internationales Telefonnummernfeld |
| NiceScroll | Benutzerdefinierte 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.
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.
bookneticHooksDas 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:
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.
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.
Actions ermöglichen es dir, auf wichtige Lifecycle-Ereignisse im Buchungspanel, im AJAX-System, im Zahlungsfluss und im Kundenpanel zu reagieren.
booking_panel_loadedWird 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:
| Parameter | Typ | Beschreibung |
|---|---|---|
booknetic | object | Die 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_loadingWird direkt vor dem Beginn eines Schrittwechsels ausgelöst, bevor die AJAX-Anfrage gesendet wird.
Parameter:
| Parameter | Typ | Beschreibung |
|---|---|---|
booknetic | object | Die Instanz des Buchungspanels |
next_step | string | Die ID des Schritts, der gleich geladen wird |
current_step | string | Die ID des Schritts, der verlassen wird |
bookneticHooks.addAction('before_step_loading', function (booknetic, nextStep, currentStep) {
console.log(`Navigiere von ${currentStep} zu ${nextStep}`);
});start_step_loadingWird ausgelöst, wenn die AJAX-Anfrage für den nächsten Schritt startet.
Parameter:
| Parameter | Typ | Beschreibung |
|---|---|---|
booknetic | object | Die Instanz des Buchungspanels |
next_step | string | Der Schritt, der geladen wird |
current_step | string | Der vorherige Schritt |
loaded_stepWird 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:
| Parameter | Typ | Beschreibung |
|---|---|---|
booknetic | object | Die Instanz des Buchungspanels |
step_id | string | Die ID des geladenen Schritts |
current_step | string | Die ID des vorherigen Schritts |
result | object | Die 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:
| Parameter | Typ | Beschreibung |
|---|---|---|
booknetic | object | Die Instanz des Buchungspanels |
result | object | Die 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_stepWird ausgelöst, wenn ein zuvor besuchter Schritt aus dem Cache geladen wird.
Parameter:
| Parameter | Typ | Beschreibung |
|---|---|---|
booknetic | object | Die Instanz des Buchungspanels |
step_id | string | Der 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:
| Parameter | Typ | Beschreibung |
|---|---|---|
booknetic | object | Die 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_errorWird ausgelöst, wenn ein Schritt nicht geladen werden kann, weil der Server einen Fehler zurückgegeben hat.
Parameter:
| Parameter | Typ | Beschreibung |
|---|---|---|
booknetic | object | Die Instanz des Buchungspanels |
next_step | string | Der Schritt, der nicht geladen werden konnte |
current_step | string | Der Schritt, zu dem zurückgekehrt wird |
result | object | Die Fehler-Response |
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:
| Parameter | Typ | Beschreibung |
|---|---|---|
params | object | Die Anfrageparameter |
booknetic | object | Die 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}_successWird ausgelöst, nachdem eine AJAX-Response erfolgreich zurückgegeben wurde.
Parameter:
| Parameter | Typ | Beschreibung |
|---|---|---|
booknetic | object | Die Instanz des Buchungspanels |
params | object | Die ursprünglichen Anfrageparameter |
result | object | Die Server-Response |
bookneticHooks.addAction('ajax_after_get_data_success', function (booknetic, params, result) {
// Schrittdaten wurden erfolgreich geladen
});ajax_after_{action}_errorWird ausgelöst, nachdem eine AJAX-Anfrage fehlgeschlagen ist.
Parameter:
| Parameter | Typ | Beschreibung |
|---|---|---|
booknetic | object | Die Instanz des Buchungspanels |
params | object | Die ursprünglichen Anfrageparameter |
result | object | Die Fehler-Response, die bei Netzwerkfehlern undefined sein kann |
before_processing_paymentWird 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:
| Parameter | Typ | Beschreibung |
|---|---|---|
payment_method | string | Die ausgewählte Zahlungsmethode, zum Beispiel 'local', 'stripe' oder 'paypal' |
data | object | Vom 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_paymentWird ausgelöst, nachdem der Zahlungsvorgang initialisiert wurde. Das wird typischerweise verwendet, um das Popup zur Seite des Zahlungs-Gateways weiterzuleiten.
Parameter:
| Parameter | Typ | Beschreibung |
|---|---|---|
payment_method | string | Die Zahlungsmethode |
status | boolean | Ob die Initialisierung der Zahlung erfolgreich war |
data | object | Die 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_completedWird ausgelöst, wenn der Zahlungsvorgang erfolgreich abgeschlossen wurde.
Parameter:
| Parameter | Typ | Beschreibung |
|---|---|---|
status | boolean | Erfolgsstatus der Zahlung |
paymentData | object | Daten des Zahlungsergebnisses |
payment_completed_deprecatedVeralteter Hook, der aus Gründen der Abwärtskompatibilität beibehalten wird. Verwende stattdessen payment_completed.
Parameter:
| Parameter | Typ | Beschreibung |
|---|---|---|
booknetic | object | Die Instanz des Buchungspanels |
payment_errorWird ausgelöst, wenn ein Zahlungsversuch fehlschlägt.
Parameter:
| Parameter | Typ | Beschreibung |
|---|---|---|
booknetic | object | Die Instanz des Buchungspanels |
booking_finished_successfullyWird ausgelöst, wenn der gesamte Buchungsfluss erfolgreich abgeschlossen ist und der Abschlussscreen angezeigt wird.
Parameter:
| Parameter | Typ | Beschreibung |
|---|---|---|
booknetic | object | Die 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
});
}
});customer_panel_loadedWird 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);
});
});
});Filter erlauben es, ausgehende Anfragedaten, Ergebnisse der Schrittvalidierung, Warenkorbeinträge und gerendertes Frontend-HTML zu verändern, bevor Booknetic sie verwendet.
ajaxFiltert alle ausgehenden AJAX-Anfrageparameter. Dieser Hook wird für jede AJAX-Anfrage ausgeführt, die vom Buchungspanel gesendet wird.
Parameter:
| Parameter | Typ | Beschreibung |
|---|---|---|
params | object | Die Anfrageparameter |
booknetic | object | Die 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:
| Parameter | Typ | Beschreibung |
|---|---|---|
params | object | Die Anfrageparameter |
booknetic | object | Die 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_dataFiltert 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:
| Parameter | Typ | Beschreibung |
|---|---|---|
data | FormData | Die Daten der Terminübermittlung |
booknetic | object | Die 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;
});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:
| Parameter | Typ | Beschreibung |
|---|---|---|
result | object | { status: true, errorMsg: '' } |
booknetic | object | Die 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:
| Parameter | Typ | Beschreibung |
|---|---|---|
booknetic | object | Die 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)
});bkntc_cartFiltert die Daten des Warenkorbeintrags, bevor sie im Warenkorb-Array gespeichert werden.
Parameter:
| Parameter | Typ | Beschreibung |
|---|---|---|
cart_item | object | Das Objekt mit den Daten des Warenkorbeintrags |
booknetic | object | Die 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;
});bkntc_date_time_loadFiltert das HTML jedes Zeitfensters, bevor es im Schritt Datum & Uhrzeit gerendert wird.
Parameter:
| Parameter | Typ | Beschreibung |
|---|---|---|
html | string | Das HTML-Element des Zeitfensters |
time_obj | object | Die Daten des Zeitfensters, wie start_time, end_time und andere zugehörige Felder |
booknetic | object | Die 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;
});bookneticJede Instanz des Buchungspanels erstellt ihr eigenes booknetic-Objekt. Dieses fungiert als zentrale API und Zustandscontainer des Panels.
| Eigenschaft | Typ | Beschreibung |
|---|---|---|
panel_js | jQuery | jQuery-Referenz auf das Root-Element dieses Panels |
cartArr | array | Warenkorb-Array |
cartCurrentIndex | number | Index des aktuell bearbeiteten Warenkorbeintrags |
calendarDateTimes | object | Verfügbare Daten und Zeitfenster |
calendarYear | number | Der aktuell angezeigte Kalenderjahrgang |
calendarMonth | number | Der aktuell angezeigte Kalendermonat |
paymentWindow | Window | Referenz auf das Zahlungs-Popup-Fenster |
paymentStatus | boolean | Erfolgs- oder Fehlerstatus der Zahlung |
appointmentId | number | Die ID des erstellten Termins nach der Bestätigung |
paymentId | number | Die ID des Zahlungsdatensatzes |
booknetic.ajax(action, params, successCallback, loading, errorCallback, async)Sendet eine AJAX-Anfrage an das Booknetic-Backend.
| Parameter | Typ | Standard | Beschreibung |
|---|---|---|---|
action | string|object | — | Name 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 |
params | object|FormData | — | Anfrageparameter |
successCallback | function | — | Callback, der im Erfolgsfall mit (result) ausgeführt wird |
loading | boolean | true | Ob der Lade-Spinner angezeigt werden soll |
errorCallback | function | — | Callback, der im Fehlerfall mit (result) ausgeführt wird |
async | boolean | true | Ob 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.
| Parameter | Typ | Beschreibung |
|---|---|---|
message | string | Nachrichtentext |
type | string | 'success', 'error' oder 'warning' |
duration | number | Auto-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.getSelectedEin 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.stepManagerDer 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 speichernbooknetic.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)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.
Das Buchungspanel folgt einem schrittbasierten Wizard-Muster:
Location → Staff → Service → Service Extras → Date/Time → [Recurring Info] → Information → Cart → Confirmation → FinishSchritte können in den Admin-Einstellungen ausgeblendet oder neu angeordnet werden. Navigation, Caching und Validierung werden automatisch vom Step Manager gehandhabt.
Wenn eine Seite geladen wird, die den Shortcode [booknetic] enthält, passiert Folgendes:
bookneticInitBookingPage() für jedes Panel auf der Seite auf.bkntc_get_data geladen.booking_panel_loaded wird ausgelöst.// Vereinfachter Initialisierungsfluss
jQuery(".booknetic_appointment").each((i, element) => {
window.bookneticInitBookingPage(element);
});<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>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.
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', ...)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
}Ein Addon für ein Zahlungs-Gateway muss drei zentrale Hooks implementieren, um sich sauber in den Buchungsfluss zu integrieren.
(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 fehlgeschlagenDas Haupt-Buchungspanel überwacht das Popup. Wenn es geschlossen wird, prüft es das Zahlungsergebnis und zeigt dann entweder den Erfolgsbildschirm oder einen Fehlerzustand an.
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 angezeigtDer Admin-Bereich von Booknetic hat seine eigene JavaScript-Architektur und sein eigenes booknetic-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
};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);
});
}
);
};
});$(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();
});
});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.
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);
}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"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;
});Die Frontend-Styles von Booknetic sind so gekapselt, dass Konflikte mit WordPress-Themes und Drittanbieter-Styles reduziert werden.
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;
}| Präfix | Geltungsbereich | Beispiel |
|---|---|---|
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.
| Klasse | Beschreibung |
|---|---|
.booknetic_appointment | Root-Container des gesamten Buchungspanels |
.booknetic_appointment_steps | Linke Seitenleiste mit Schrittindikatoren |
.booknetic_appointment_container | Hauptcontainer für den Inhalt |
.booknetic_appointment_container_body | Dynamischer Bereich für Schrittinhalte |
.booknetic_card | Selektierbares Kartenelement für Standort, Mitarbeiter und Ähnliches |
.booknetic_card_selected | Aktuell ausgewählte Karte |
.booknetic_service_card | Karte zur Serviceauswahl |
.booknetic_service_card_selected | Aktuell ausgewählter Service |
.booknetic_calendar_rows | Raster der Kalendertage |
.booknetic_calendar_days | Einzelner Kalendertag |
.booknetic_time_element | Zeitfensterelement |
.booknetic_selected_time | Aktuell ausgewähltes Zeitfenster |
.booknetic_appointment_finished | Erfolgs- oder Abschlussbildschirm |
Das Buchungspanel wechselt bei einer Viewport-Breite von 1000px in das mobile Layout.
booknetic.isMobileView(); // gibt true zurück, wenn der Viewport ≤ 1000px istIn der mobilen Ansicht:
RTL wird automatisch erkannt:
if ($('html').attr('dir') === 'rtl') {
$('body').addClass('rtl');
}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/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-Typ | Anzahl | Zweck |
|---|---|---|
| JS-Actions | 18+ | Auf Lifecycle-Ereignisse wie Schrittladen, Zahlungsabschluss und erfolgreiche Buchung reagieren |
| JS-Filter | 7+ | Anfragedaten, Validierungsergebnisse, Warenkorbeinträge und gerenderte Frontend-Ausgabe verändern |
| Hook | Typ | Häufiger Anwendungsfall |
|---|---|---|
booking_panel_loaded | Action | Addon initialisieren und Event-Listener binden |
loaded_step / loaded_step_{id} | Action | Benutzerdefinierte UI in einen bestimmten Schritt einfügen |
step_end_{step_id} | Action | Reagieren, wenn ein Schritt abgeschlossen ist |
before_processing_payment | Action | Ein Popup für das Zahlungs-Gateway öffnen |
after_processing_payment | Action | Das Popup zur Checkout-Seite des Gateways weiterleiten |
booking_finished_successfully | Action | Conversion-Tracking und Analytics |
step_validation_{step_id} | Filter | Benutzerdefinierte Schrittvalidierung hinzufügen |
appointment_ajax_data | Filter | Benutzerdefinierte Daten an die Buchungsübermittlung anhängen |
ajax / ajax_{action} | Filter | AJAX-Anfrageparameter verändern |
bkntc_date_time_load | Filter | Das Rendering von Zeitfenstern anpassen |
load_step_{step_id} | Filter | Das Laden von Schritten bedingt blockieren |
bkntc_cart | Filter | Daten von Warenkorbeinträgen verändern |