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:
| Opcja | Co 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 trwaniaPublicDateCalendar— wybór dniaPublicTimeDialog— wybór kortu dla godzinyReservationDialog— OTP + potwierdzenie + płatność
- Tło
html/bodyi 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-blurani przyciemnienia tła (rozmazuje/przyciemnia stronę hosta pod ramką) — overlay jest przezroczysty (html[data-in-iframe]). - Osadzanie cross-origin wymaga CSP
frame-ancestorsprzez envPUBLIC_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'), migracjamigrations/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') orazisChannelAllowedByContactMethod. getPublicBookingSettingszwracapublic_contact_method, więc/api/public/booking/options(i endpoint dnia) podaje je doPublicBookingApp, a ten doReservationDialog.ReservationDialogprzy jednym dozwolonym kanale nie zgaduje z formatu wpisanej wartości — kanał jest ustalony przez konfigurację, a pole dostaje odpowiednitype/inputMode/autoCompletei etykietę (contactLabelPhone/contactLabelEmail).- Walidacja serwerowa, niezależna od UI:
isPublicContactChannelAllowed(channel, city, street)wlib/actions/public-booking.tsblokuje/api/public/booking/otp/sendi/api/public/booking/otp/verify(oba przyjmują opcjonalnecity/street), acreatePublicReservationzwracacontact_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/streetw żądaniu decyduje rekord globalny.