Skip to main content

Publiczny kalendarz rezerwacji

Instrukcja (WordPress / strona firmy)

Publiczny kalendarz można osadzić na stronie AcePark przez iframe, np.:

<iframe
src="https://klient.acepark.pl/book/ace-park?city=Opole&street=Spokojna"
style="width:100%;height:760px;border:0;background:transparent"
title="Rezerwacja kortu"
allowtransparency="true"
></iframe>

Parametry city i street przypinają lokalizację — na stronie lokalizacji nie trzeba już wybierać obiektu.

Tło strony bookingu jest przezroczyste, żeby w iframe przeświecało tło WordPressa. Same karty (filtry, siatka, dialogi) zostają nieprzezroczyste.

W iframe ukrywany jest nagłówek strony („Zarezerwuj kort” / „Wybierz termin…”) — WordPress ma już swój tytuł sekcji. Przy bezpośrednim wejściu na /book/... nagłówek zostaje.

Filtry, wybór daty, wybór kortu i formularz rezerwacji otwierają się jako wyśrodkowany dialog (nie jako dolny drawer). Dzięki temu overlay wygląda poprawnie także w iframe na WordPressie.

Sposób kontaktu (SMS / e-mail / oba)

Klient potwierdza rezerwację kodem jednorazowym. To, gdzie ten kod trafia, ustawia się w Ustawieniach rezerwacji (/dashboard/reservations-settings) w sekcji „Rezerwacje online”, polem Sposób kontaktu w formularzu publicznym:

OpcjaCo widzi klient
Telefon lub e-mail (klient wybiera)jedno pole „Numer telefonu lub e-mail” — kanał wynika z tego, co klient wpisze
Tylko telefon (kod SMS)pole „Numer telefonu”, klawiatura numeryczna, kod zawsze SMS-em
Tylko e-mail (kod e-mailem)pole „Adres e-mail”, kod zawsze na e-mail

Ustawienie jest per zakres (domyślne / miasto / lokalizacja), tak samo jak pozostałe ustawienia rezerwacji — jedna lokalizacja może wysyłać wyłącznie SMS-y, a druga wyłącznie e-maile. Domyślnie obowiązuje oba i formularz zachowuje się jak dotychczas.

Pole jest widoczne tylko wtedy, gdy dla danego zakresu włączone są rezerwacje online.

Dokumentacja techniczna

  • Trasa: app/(public)/book/[tenant]/
  • Overlaye (Dialog shadcn, bez Vaul Drawer):
    • PublicFiltersBar — lokalizacja / czas trwania
    • PublicDateCalendar — wybór dnia
    • PublicTimeDialog — wybór kortu dla godziny
    • ReservationDialog — OTP + potwierdzenie + płatność
  • Tło html/body i wrappera kalendarza jest przezroczyste (TransparentPageBackground + bg-transparent), żeby embed na WordPressie dziedziczył tło strony hosta.
  • Wykrywanie iframe: useIsInIframe (window.self !== window.top). W embedzie ukrywany jest <header> z tytułem/podtytułem.
  • W iframe dialogi nie używają backdrop-blur ani przyciemnienia tła (rozmazuje/przyciemnia stronę hosta pod ramką) — overlay jest przezroczysty (html[data-in-iframe]).
  • Osadzanie cross-origin wymaga CSP frame-ancestors przez env PUBLIC_BOOKING_FRAME_ANCESTORS (middleware ustawia nagłówek tylko dla /book/*). Przykład:
PUBLIC_BOOKING_FRAME_ANCESTORS='self' https://acepark.pl https://www.acepark.pl

Bez domeny hosta w allowliście przeglądarka zablokuje iframe (frame-ancestors).

Sposób kontaktu — implementacja

  • Kolumna booking_settings.public_contact_method ('sms' | 'email' | 'both', domyślnie 'both'), migracja migrations/0204_add_public_contact_method.sql. Obowiązuje ta sama reguła fallbacku co dla reszty ustawień: ulica → miasto → globalne.
  • Typ i helpery w lib/actions/public-booking-shared.ts: PublicContactMethod, parsePublicContactMethod (każda nieznana wartość → 'both') oraz isChannelAllowedByContactMethod.
  • getPublicBookingSettings zwraca public_contact_method, więc /api/public/booking/options (i endpoint dnia) podaje je do PublicBookingApp, a ten do ReservationDialog.
  • ReservationDialog przy jednym dozwolonym kanale nie zgaduje z formatu wpisanej wartości — kanał jest ustalony przez konfigurację, a pole dostaje odpowiedni type/inputMode/autoComplete i etykietę (contactLabelPhone / contactLabelEmail).
  • Walidacja serwerowa, niezależna od UI: isPublicContactChannelAllowed(channel, city, street) w lib/actions/public-booking.ts blokuje /api/public/booking/otp/send i /api/public/booking/otp/verify (oba przyjmują opcjonalne city/street), a createPublicReservation zwraca contact_method_not_allowed. Wysłanie kodu kosztuje, więc kanał wyłączony w ustawieniach nie może zostać użyty przez klienta pomijającego formularz.
  • Bez city/street w żądaniu decyduje rekord globalny.